| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.tools.ant.taskdefs; |
| |
| import java.io.File; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.Vector; |
| |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.ProjectHelper; |
| import org.apache.tools.ant.ProjectHelperRepository; |
| import org.apache.tools.ant.Task; |
| import org.apache.tools.ant.types.Resource; |
| import org.apache.tools.ant.types.ResourceCollection; |
| import org.apache.tools.ant.types.resources.FileProvider; |
| import org.apache.tools.ant.types.resources.FileResource; |
| import org.apache.tools.ant.types.resources.URLProvider; |
| import org.apache.tools.ant.types.resources.URLResource; |
| import org.apache.tools.ant.types.resources.Union; |
| import org.apache.tools.ant.util.FileUtils; |
| |
| /** |
| * Task to import another build file into the current project. |
| * <p> |
| * It must be 'top level'. On execution it will read another Ant file |
| * into the same Project. |
| * </p> |
| * <p> |
| * <b>Important</b>: Trying to understand how relative file references |
| * resolved in deep/complex build hierarchies - such as what happens |
| * when an imported file imports another file can be difficult. Use absolute references for |
| * enhanced build file stability, especially in the imported files. |
| * </p> |
| * <p>Examples:</p> |
| * <pre> |
| * <import file="../common-targets.xml"/> |
| * </pre> |
| * <p>Import targets from a file in a parent directory.</p> |
| * <pre> |
| * <import file="${deploy-platform}.xml"/> |
| * </pre> |
| * <p>Import the project defined by the property <code>deploy-platform</code>.</p> |
| * |
| * @since Ant1.6 |
| * @ant.task category="control" |
| */ |
| public class ImportTask extends Task { |
| private String file; |
| private boolean optional; |
| private String targetPrefix = ProjectHelper.USE_PROJECT_NAME_AS_TARGET_PREFIX; |
| private String prefixSeparator = "."; |
| private final Union resources = new Union(); |
| private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); |
| |
| public ImportTask() { |
| resources.setCache(true); |
| } |
| |
| /** |
| * sets the optional attribute |
| * |
| * @param optional if true ignore files that are not present, |
| * default is false |
| */ |
| public void setOptional(boolean optional) { |
| this.optional = optional; |
| } |
| |
| /** |
| * the name of the file to import. How relative paths are resolved is still |
| * in flux: use absolute paths for safety. |
| * @param file the name of the file |
| */ |
| public void setFile(String file) { |
| // I don't think we can use File - different rules |
| // for relative paths. |
| this.file = file; |
| } |
| |
| /** |
| * The prefix to use when prefixing the imported target names. |
| * |
| * @since Ant 1.8.0 |
| */ |
| public void setAs(String prefix) { |
| targetPrefix = prefix; |
| } |
| |
| /** |
| * The separator to use between prefix and target name, default is |
| * ".". |
| * |
| * @since Ant 1.8.0 |
| */ |
| public void setPrefixSeparator(String s) { |
| prefixSeparator = s; |
| } |
| |
| /** |
| * The resource to import. |
| * |
| * @since Ant 1.8.0 |
| */ |
| public void add(ResourceCollection r) { |
| resources.add(r); |
| } |
| |
| public void execute() { |
| if (file == null && resources.size() == 0) { |
| throw new BuildException("import requires file attribute or" |
| + " at least one nested resource"); |
| } |
| if (getOwningTarget() == null |
| || !"".equals(getOwningTarget().getName())) { |
| throw new BuildException("import only allowed as a top-level task"); |
| } |
| |
| ProjectHelper helper = |
| (ProjectHelper) getProject(). |
| getReference(ProjectHelper.PROJECTHELPER_REFERENCE); |
| |
| if (helper == null) { |
| // this happens if the projecthelper was not registered with the project. |
| throw new BuildException("import requires support in ProjectHelper"); |
| } |
| |
| Vector<Object> importStack = helper.getImportStack(); |
| |
| if (importStack.size() == 0) { |
| // this happens if ant is used with a project |
| // helper that doesn't set the import. |
| throw new BuildException("import requires support in ProjectHelper"); |
| } |
| |
| if (getLocation() == null || getLocation().getFileName() == null) { |
| throw new BuildException("Unable to get location of import task"); |
| } |
| |
| Union resourcesToImport = new Union(getProject(), resources); |
| Resource fromFileAttribute = getFileAttributeResource(); |
| if (fromFileAttribute != null) { |
| resources.add(fromFileAttribute); |
| } |
| for (Resource r : resourcesToImport) { |
| importResource(helper, r); |
| } |
| } |
| |
| private void importResource(ProjectHelper helper, |
| Resource importedResource) { |
| Vector<Object> importStack = helper.getImportStack(); |
| |
| getProject().log("Importing file " + importedResource + " from " |
| + getLocation().getFileName(), Project.MSG_VERBOSE); |
| |
| if (!importedResource.isExists()) { |
| String message = |
| "Cannot find " + importedResource + " imported from " |
| + getLocation().getFileName(); |
| if (optional) { |
| getProject().log(message, Project.MSG_VERBOSE); |
| return; |
| } else { |
| throw new BuildException(message); |
| } |
| } |
| |
| if (!isInIncludeMode() && |
| hasAlreadyBeenImported(importedResource, importStack)) { |
| getProject().log( |
| "Skipped already imported file:\n " |
| + importedResource + "\n", Project.MSG_VERBOSE); |
| return; |
| } |
| |
| // nested invocations are possible like an imported file |
| // importing another one |
| String oldPrefix = ProjectHelper.getCurrentTargetPrefix(); |
| boolean oldIncludeMode = ProjectHelper.isInIncludeMode(); |
| String oldSep = ProjectHelper.getCurrentPrefixSeparator(); |
| try { |
| String prefix; |
| if (isInIncludeMode() && oldPrefix != null |
| && targetPrefix != null) { |
| prefix = oldPrefix + oldSep + targetPrefix; |
| } else if (isInIncludeMode()) { |
| prefix = targetPrefix; |
| } else if (!ProjectHelper.USE_PROJECT_NAME_AS_TARGET_PREFIX.equals(targetPrefix)) { |
| prefix = targetPrefix; |
| } else { |
| prefix = oldPrefix; |
| } |
| setProjectHelperProps(prefix, prefixSeparator, |
| isInIncludeMode()); |
| |
| ProjectHelper subHelper = ProjectHelperRepository.getInstance().getProjectHelperForBuildFile( |
| importedResource); |
| |
| // push current stacks into the sub helper |
| subHelper.getImportStack().addAll(helper.getImportStack()); |
| subHelper.getExtensionStack().addAll(helper.getExtensionStack()); |
| getProject().addReference(ProjectHelper.PROJECTHELPER_REFERENCE, subHelper); |
| |
| subHelper.parse(getProject(), importedResource); |
| |
| // push back the stack from the sub helper to the main one |
| getProject().addReference(ProjectHelper.PROJECTHELPER_REFERENCE, helper); |
| helper.getImportStack().clear(); |
| helper.getImportStack().addAll(subHelper.getImportStack()); |
| helper.getExtensionStack().clear(); |
| helper.getExtensionStack().addAll(subHelper.getExtensionStack()); |
| } catch (BuildException ex) { |
| throw ProjectHelper.addLocationToBuildException( |
| ex, getLocation()); |
| } finally { |
| setProjectHelperProps(oldPrefix, oldSep, oldIncludeMode); |
| } |
| } |
| |
| private Resource getFileAttributeResource() { |
| // Paths are relative to the build file they're imported from, |
| // *not* the current directory (same as entity includes). |
| |
| if (file != null) { |
| if (isExistingAbsoluteFile(file)) { |
| return new FileResource(FILE_UTILS.normalize(file)); |
| } |
| |
| File buildFile = |
| new File(getLocation().getFileName()).getAbsoluteFile(); |
| if (buildFile.exists()) { |
| File buildFileParent = new File(buildFile.getParent()); |
| File importedFile = |
| FILE_UTILS.resolveFile(buildFileParent, file); |
| return new FileResource(importedFile); |
| } |
| // maybe this import tasks is inside an imported URL? |
| try { |
| URL buildFileURL = new URL(getLocation().getFileName()); |
| URL importedFile = new URL(buildFileURL, file); |
| return new URLResource(importedFile); |
| } catch (MalformedURLException ex) { |
| log(ex.toString(), Project.MSG_VERBOSE); |
| } |
| throw new BuildException("failed to resolve " + file |
| + " relative to " |
| + getLocation().getFileName()); |
| } |
| return null; |
| } |
| |
| private boolean isExistingAbsoluteFile(String name) { |
| File f = new File(name); |
| return f.isAbsolute() && f.exists(); |
| } |
| |
| private boolean hasAlreadyBeenImported(Resource importedResource, |
| Vector<Object> importStack) { |
| File importedFile = null; |
| FileProvider fp = importedResource.as(FileProvider.class); |
| if (fp != null) { |
| importedFile = fp.getFile(); |
| } |
| URL importedURL = null; |
| URLProvider up = importedResource.as(URLProvider.class); |
| if (up != null) { |
| importedURL = up.getURL(); |
| } |
| for (Object o : importStack) { |
| if (isOneOf(o, importedResource, importedFile, importedURL)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private boolean isOneOf(Object o, Resource importedResource, |
| File importedFile, URL importedURL) { |
| if (o.equals(importedResource) || o.equals(importedFile) |
| || o.equals(importedURL)) { |
| return true; |
| } |
| if (o instanceof Resource) { |
| if (importedFile != null) { |
| FileProvider fp = ((Resource) o).as(FileProvider.class); |
| if (fp != null && fp.getFile().equals(importedFile)) { |
| return true; |
| } |
| } |
| if (importedURL != null) { |
| URLProvider up = ((Resource) o).as(URLProvider.class); |
| if (up != null && up.getURL().equals(importedURL)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Whether the task is in include (as opposed to import) mode. |
| * |
| * <p>In include mode included targets are only known by their |
| * prefixed names and their depends lists get rewritten so that |
| * all dependencies get the prefix as well.</p> |
| * |
| * <p>In import mode imported targets are known by an adorned as |
| * well as a prefixed name and the unadorned target may be |
| * overwritten in the importing build file. The depends list of |
| * the imported targets is not modified at all.</p> |
| * |
| * @since Ant 1.8.0 |
| */ |
| protected final boolean isInIncludeMode() { |
| return "include".equals(getTaskType()); |
| } |
| |
| /** |
| * Sets a bunch of Thread-local ProjectHelper properties. |
| * |
| * @since Ant 1.8.0 |
| */ |
| private static void setProjectHelperProps(String prefix, |
| String prefixSep, |
| boolean inIncludeMode) { |
| ProjectHelper.setCurrentTargetPrefix(prefix); |
| ProjectHelper.setCurrentPrefixSeparator(prefixSep); |
| ProjectHelper.setInIncludeMode(inIncludeMode); |
| } |
| } |