aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java2494
1 files changed, 2494 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java
new file mode 100644
index 00000000..3a0c4b82
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Project.java
@@ -0,0 +1,2494 @@
+/*
+ * 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;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.WeakHashMap;
+
+import org.apache.tools.ant.helper.DefaultExecutor;
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.types.Description;
+import org.apache.tools.ant.types.FilterSet;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.VectorSet;
+
+/**
+ * Central representation of an Ant project. This class defines an
+ * Ant project with all of its targets, tasks and various other
+ * properties. It also provides the mechanism to kick off a build using
+ * a particular target name.
+ * <p>
+ * This class also encapsulates methods which allow files to be referred
+ * to using abstract path names which are translated to native system
+ * file paths at runtime.
+ *
+ */
+public class Project implements ResourceFactory {
+ /** Message priority of &quot;error&quot;. */
+ public static final int MSG_ERR = 0;
+ /** Message priority of &quot;warning&quot;. */
+ public static final int MSG_WARN = 1;
+ /** Message priority of &quot;information&quot;. */
+ public static final int MSG_INFO = 2;
+ /** Message priority of &quot;verbose&quot;. */
+ public static final int MSG_VERBOSE = 3;
+ /** Message priority of &quot;debug&quot;. */
+ public static final int MSG_DEBUG = 4;
+
+ /**
+ * Constant for the &quot;visiting&quot; state, used when
+ * traversing a DFS of target dependencies.
+ */
+ private static final String VISITING = "VISITING";
+ /**
+ * Constant for the &quot;visited&quot; state, used when
+ * traversing a DFS of target dependencies.
+ */
+ private static final String VISITED = "VISITED";
+
+ /**
+ * Version constant for Java 1.0 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_0} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_0 = JavaEnvUtils.JAVA_1_0;
+ /**
+ * Version constant for Java 1.1 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_1} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_1 = JavaEnvUtils.JAVA_1_1;
+ /**
+ * Version constant for Java 1.2 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_2} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_2 = JavaEnvUtils.JAVA_1_2;
+ /**
+ * Version constant for Java 1.3 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_3} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_3 = JavaEnvUtils.JAVA_1_3;
+ /**
+ * Version constant for Java 1.4 .
+ *
+ * @deprecated since 1.5.x.
+ * Use {@link JavaEnvUtils#JAVA_1_4} instead.
+ */
+ @Deprecated
+ public static final String JAVA_1_4 = JavaEnvUtils.JAVA_1_4;
+
+ /** Default filter start token. */
+ public static final String TOKEN_START = FilterSet.DEFAULT_TOKEN_START;
+ /** Default filter end token. */
+ public static final String TOKEN_END = FilterSet.DEFAULT_TOKEN_END;
+
+ /** Instance of a utility class to use for file operations. */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Name of this project. */
+ private String name;
+ /** Description for this project (if any). */
+ private String description;
+
+
+ /** Map of references within the project (paths etc) (String to Object). */
+ private final Hashtable<String, Object> references = new AntRefTable();
+
+ /** Map of id references - used for indicating broken build files */
+ private final HashMap<String, Object> idReferences = new HashMap<String, Object>();
+
+ /** Name of the project's default target. */
+ private String defaultTarget;
+
+ /** Map from target names to targets (String to Target). */
+ private final Hashtable<String, Target> targets = new Hashtable<String, Target>();
+
+ /** Set of global filters. */
+ private final FilterSet globalFilterSet = new FilterSet();
+ {
+ // Initialize the globalFileSet's project
+ globalFilterSet.setProject(this);
+ }
+
+ /**
+ * Wrapper around globalFilterSet. This collection only ever
+ * contains one FilterSet, but the wrapper is needed in order to
+ * make it easier to use the FileUtils interface.
+ */
+ private final FilterSetCollection globalFilters
+ = new FilterSetCollection(globalFilterSet);
+
+ /** Project base directory. */
+ private File baseDir;
+
+ /** lock object used when adding/removing listeners */
+ private final Object listenersLock = new Object();
+
+ /** List of listeners to notify of build events. */
+ private volatile BuildListener[] listeners = new BuildListener[0];
+
+ /** for each thread, record whether it is currently executing
+ messageLogged */
+ private final ThreadLocal<Boolean> isLoggingMessage = new ThreadLocal<Boolean>() {
+ @Override
+ protected Boolean initialValue() {
+ return Boolean.FALSE;
+ }
+ };
+
+ /**
+ * The Ant core classloader--may be <code>null</code> if using
+ * parent classloader.
+ */
+ private ClassLoader coreLoader = null;
+
+ /** Records the latest task to be executed on a thread. */
+ private final Map<Thread,Task> threadTasks =
+ Collections.synchronizedMap(new WeakHashMap<Thread, Task>());
+
+ /** Records the latest task to be executed on a thread group. */
+ private final Map<ThreadGroup,Task> threadGroupTasks
+ = Collections.synchronizedMap(new WeakHashMap<ThreadGroup,Task>());
+
+ /**
+ * Called to handle any input requests.
+ */
+ private InputHandler inputHandler = null;
+
+ /**
+ * The default input stream used to read any input.
+ */
+ private InputStream defaultInputStream = null;
+
+ /**
+ * Keep going flag.
+ */
+ private boolean keepGoingMode = false;
+
+ /**
+ * Set the input handler.
+ *
+ * @param handler the InputHandler instance to use for gathering input.
+ */
+ public void setInputHandler(final InputHandler handler) {
+ inputHandler = handler;
+ }
+
+ /**
+ * Set the default System input stream. Normally this stream is set to
+ * System.in. This inputStream is used when no task input redirection is
+ * being performed.
+ *
+ * @param defaultInputStream the default input stream to use when input
+ * is requested.
+ * @since Ant 1.6
+ */
+ public void setDefaultInputStream(final InputStream defaultInputStream) {
+ this.defaultInputStream = defaultInputStream;
+ }
+
+ /**
+ * Get this project's input stream.
+ *
+ * @return the InputStream instance in use by this Project instance to
+ * read input.
+ */
+ public InputStream getDefaultInputStream() {
+ return defaultInputStream;
+ }
+
+ /**
+ * Retrieve the current input handler.
+ *
+ * @return the InputHandler instance currently in place for the project
+ * instance.
+ */
+ public InputHandler getInputHandler() {
+ return inputHandler;
+ }
+
+ /**
+ * Create a new Ant project.
+ */
+ public Project() {
+ inputHandler = new DefaultInputHandler();
+ }
+
+ /**
+ * Create and initialize a subproject. By default the subproject will be of
+ * the same type as its parent. If a no-arg constructor is unavailable, the
+ * <code>Project</code> class will be used.
+ * @return a Project instance configured as a subproject of this Project.
+ * @since Ant 1.7
+ */
+ public Project createSubProject() {
+ Project subProject = null;
+ try {
+ subProject = (getClass().newInstance());
+ } catch (final Exception e) {
+ subProject = new Project();
+ }
+ initSubProject(subProject);
+ return subProject;
+ }
+
+ /**
+ * Initialize a subproject.
+ * @param subProject the subproject to initialize.
+ */
+ public void initSubProject(final Project subProject) {
+ ComponentHelper.getComponentHelper(subProject)
+ .initSubProject(ComponentHelper.getComponentHelper(this));
+ subProject.setDefaultInputStream(getDefaultInputStream());
+ subProject.setKeepGoingMode(this.isKeepGoingMode());
+ subProject.setExecutor(getExecutor().getSubProjectExecutor());
+ }
+
+ /**
+ * Initialise the project.
+ *
+ * This involves setting the default task definitions and loading the
+ * system properties.
+ *
+ * @exception BuildException if the default task list cannot be loaded.
+ */
+ public void init() throws BuildException {
+ initProperties();
+
+ ComponentHelper.getComponentHelper(this).initDefaultDefinitions();
+ }
+
+ /**
+ * Initializes the properties.
+ * @exception BuildException if an vital property could not be set.
+ * @since Ant 1.7
+ */
+ public void initProperties() throws BuildException {
+ setJavaVersionProperty();
+ setSystemProperties();
+ setPropertyInternal(MagicNames.ANT_VERSION, Main.getAntVersion());
+ setAntLib();
+ }
+
+ /**
+ * Set a property to the location of ant.jar.
+ * Use the locator to find the location of the Project.class, and
+ * if this is not null, set the property {@link MagicNames#ANT_LIB}
+ * to the result
+ */
+ private void setAntLib() {
+ final File antlib = org.apache.tools.ant.launch.Locator.getClassSource(
+ Project.class);
+ if (antlib != null) {
+ setPropertyInternal(MagicNames.ANT_LIB, antlib.getAbsolutePath());
+ }
+ }
+ /**
+ * Factory method to create a class loader for loading classes from
+ * a given path.
+ *
+ * @param path the path from which classes are to be loaded.
+ *
+ * @return an appropriate classloader.
+ */
+ public AntClassLoader createClassLoader(final Path path) {
+ return AntClassLoader
+ .newAntClassLoader(getClass().getClassLoader(), this, path, true);
+ }
+
+ /**
+ * Factory method to create a class loader for loading classes from
+ * a given path.
+ *
+ * @param parent the parent classloader for the new loader.
+ * @param path the path from which classes are to be loaded.
+ *
+ * @return an appropriate classloader.
+ */
+ public AntClassLoader createClassLoader(
+ final ClassLoader parent, final Path path) {
+ return AntClassLoader.newAntClassLoader(parent, this, path, true);
+ }
+
+ /**
+ * Set the core classloader for the project. If a <code>null</code>
+ * classloader is specified, the parent classloader should be used.
+ *
+ * @param coreLoader The classloader to use for the project.
+ * May be <code>null</code>.
+ */
+ public void setCoreLoader(final ClassLoader coreLoader) {
+ this.coreLoader = coreLoader;
+ }
+
+ /**
+ * Return the core classloader to use for this project.
+ * This may be <code>null</code>, indicating that
+ * the parent classloader should be used.
+ *
+ * @return the core classloader to use for this project.
+ *
+ */
+ public ClassLoader getCoreLoader() {
+ return coreLoader;
+ }
+
+ /**
+ * Add a build listener to the list. This listener will
+ * be notified of build events for this project.
+ *
+ * @param listener The listener to add to the list.
+ * Must not be <code>null</code>.
+ */
+ public void addBuildListener(final BuildListener listener) {
+ synchronized (listenersLock) {
+ // If the listeners already has this listener, do nothing
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] == listener) {
+ return;
+ }
+ }
+ // copy on write semantics
+ final BuildListener[] newListeners =
+ new BuildListener[listeners.length + 1];
+ System.arraycopy(listeners, 0, newListeners, 0, listeners.length);
+ newListeners[listeners.length] = listener;
+ listeners = newListeners;
+ }
+ }
+
+ /**
+ * Remove a build listener from the list. This listener
+ * will no longer be notified of build events for this project.
+ *
+ * @param listener The listener to remove from the list.
+ * Should not be <code>null</code>.
+ */
+ public void removeBuildListener(final BuildListener listener) {
+ synchronized (listenersLock) {
+ // copy on write semantics
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] == listener) {
+ final BuildListener[] newListeners =
+ new BuildListener[listeners.length - 1];
+ System.arraycopy(listeners, 0, newListeners, 0, i);
+ System.arraycopy(listeners, i + 1, newListeners, i,
+ listeners.length - i - 1);
+ listeners = newListeners;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Return a copy of the list of build listeners for the project.
+ *
+ * @return a list of build listeners for the project
+ */
+ public Vector<BuildListener> getBuildListeners() {
+ synchronized (listenersLock) {
+ final Vector<BuildListener> r = new Vector<BuildListener>(listeners.length);
+ for (int i = 0; i < listeners.length; i++) {
+ r.add(listeners[i]);
+ }
+ return r;
+ }
+ }
+
+ /**
+ * Write a message to the log with the default log level
+ * of MSG_INFO .
+ * @param message The text to log. Should not be <code>null</code>.
+ */
+
+ public void log(final String message) {
+ log(message, MSG_INFO);
+ }
+
+ /**
+ * Write a project level message to the log with the given log level.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ */
+ public void log(final String message, final int msgLevel) {
+ log(message, null, msgLevel);
+ }
+
+ /**
+ * Write a project level message to the log with the given log level.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param throwable The exception causing this log, may be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ * @since 1.7
+ */
+ public void log(final String message, final Throwable throwable, final int msgLevel) {
+ fireMessageLogged(this, message, throwable, msgLevel);
+ }
+
+ /**
+ * Write a task level message to the log with the given log level.
+ * @param task The task to use in the log. Must not be <code>null</code>.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ */
+ public void log(final Task task, final String message, final int msgLevel) {
+ fireMessageLogged(task, message, null, msgLevel);
+ }
+
+ /**
+ * Write a task level message to the log with the given log level.
+ * @param task The task to use in the log. Must not be <code>null</code>.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param throwable The exception causing this log, may be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ * @since 1.7
+ */
+ public void log(final Task task, final String message, final Throwable throwable, final int msgLevel) {
+ fireMessageLogged(task, message, throwable, msgLevel);
+ }
+
+ /**
+ * Write a target level message to the log with the given log level.
+ * @param target The target to use in the log.
+ * Must not be <code>null</code>.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ */
+ public void log(final Target target, final String message, final int msgLevel) {
+ log(target, message, null, msgLevel);
+ }
+
+ /**
+ * Write a target level message to the log with the given log level.
+ * @param target The target to use in the log.
+ * Must not be <code>null</code>.
+ * @param message The text to log. Should not be <code>null</code>.
+ * @param throwable The exception causing this log, may be <code>null</code>.
+ * @param msgLevel The log priority level to use.
+ * @since 1.7
+ */
+ public void log(final Target target, final String message, final Throwable throwable,
+ final int msgLevel) {
+ fireMessageLogged(target, message, throwable, msgLevel);
+ }
+
+ /**
+ * Return the set of global filters.
+ *
+ * @return the set of global filters.
+ */
+ public FilterSet getGlobalFilterSet() {
+ return globalFilterSet;
+ }
+
+ /**
+ * Set a property. Any existing property of the same name
+ * is overwritten, unless it is a user property.
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ */
+ public void setProperty(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setProperty(name, value, true);
+ }
+
+ /**
+ * Set a property if no value currently exists. If the property
+ * exists already, a message is logged and the method returns with
+ * no other effect.
+ *
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @since 1.5
+ */
+ public void setNewProperty(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setNewProperty(name, value);
+ }
+
+ /**
+ * Set a user property, which cannot be overwritten by
+ * set/unset property calls. Any previous value is overwritten.
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @see #setProperty(String,String)
+ */
+ public void setUserProperty(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setUserProperty(name, value);
+ }
+
+ /**
+ * Set a user property, which cannot be overwritten by set/unset
+ * property calls. Any previous value is overwritten. Also marks
+ * these properties as properties that have not come from the
+ * command line.
+ *
+ * @param name The name of property to set.
+ * Must not be <code>null</code>.
+ * @param value The new value of the property.
+ * Must not be <code>null</code>.
+ * @see #setProperty(String,String)
+ */
+ public void setInheritedProperty(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setInheritedProperty(name, value);
+ }
+
+ /**
+ * Set a property unless it is already defined as a user property
+ * (in which case the method returns silently).
+ *
+ * @param name The name of the property.
+ * Must not be <code>null</code>.
+ * @param value The property value. Must not be <code>null</code>.
+ */
+ private void setPropertyInternal(final String name, final String value) {
+ PropertyHelper.getPropertyHelper(this).setProperty(name, value, false);
+ }
+
+ /**
+ * Return the value of a property, if it is set.
+ *
+ * @param propertyName The name of the property.
+ * May be <code>null</code>, in which case
+ * the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match
+ * or if a <code>null</code> name is provided.
+ */
+ public String getProperty(final String propertyName) {
+ final Object value = PropertyHelper.getPropertyHelper(this).getProperty(propertyName);
+ return value == null ? null : String.valueOf(value);
+ }
+
+ /**
+ * Replace ${} style constructions in the given value with the
+ * string value of the corresponding data types.
+ *
+ * @param value The string to be scanned for property references.
+ * May be <code>null</code>.
+ *
+ * @return the given string with embedded property names replaced
+ * by values, or <code>null</code> if the given string is
+ * <code>null</code>.
+ *
+ * @exception BuildException if the given value has an unclosed
+ * property name, e.g. <code>${xxx</code>.
+ */
+ public String replaceProperties(final String value) throws BuildException {
+ return PropertyHelper.getPropertyHelper(this).replaceProperties(null, value, null);
+ }
+
+ /**
+ * Return the value of a user property, if it is set.
+ *
+ * @param propertyName The name of the property.
+ * May be <code>null</code>, in which case
+ * the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match
+ * or if a <code>null</code> name is provided.
+ */
+ public String getUserProperty(final String propertyName) {
+ return (String) PropertyHelper.getPropertyHelper(this).getUserProperty(propertyName);
+ }
+
+ /**
+ * Return a copy of the properties table.
+ * @return a hashtable containing all properties
+ * (including user properties).
+ */
+ public Hashtable<String, Object> getProperties() {
+ return PropertyHelper.getPropertyHelper(this).getProperties();
+ }
+
+ /**
+ * Return a copy of the user property hashtable.
+ * @return a hashtable containing just the user properties.
+ */
+ public Hashtable<String, Object> getUserProperties() {
+ return PropertyHelper.getPropertyHelper(this).getUserProperties();
+ }
+
+ /**
+ * Return a copy of the inherited property hashtable.
+ * @return a hashtable containing just the inherited properties.
+ * @since Ant 1.8.0
+ */
+ public Hashtable<String, Object> getInheritedProperties() {
+ return PropertyHelper.getPropertyHelper(this).getInheritedProperties();
+ }
+
+ /**
+ * Copy all user properties that have been set on the command
+ * line or a GUI tool from this instance to the Project instance
+ * given as the argument.
+ *
+ * <p>To copy all &quot;user&quot; properties, you will also have to call
+ * {@link #copyInheritedProperties copyInheritedProperties}.</p>
+ *
+ * @param other the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.5
+ */
+ public void copyUserProperties(final Project other) {
+ PropertyHelper.getPropertyHelper(this).copyUserProperties(other);
+ }
+
+ /**
+ * Copy all user properties that have not been set on the
+ * command line or a GUI tool from this instance to the Project
+ * instance given as the argument.
+ *
+ * <p>To copy all &quot;user&quot; properties, you will also have to call
+ * {@link #copyUserProperties copyUserProperties}.</p>
+ *
+ * @param other the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.5
+ */
+ public void copyInheritedProperties(final Project other) {
+ PropertyHelper.getPropertyHelper(this).copyInheritedProperties(other);
+ }
+
+ /**
+ * Set the default target of the project.
+ *
+ * @param defaultTarget The name of the default target for this project.
+ * May be <code>null</code>, indicating that there is
+ * no default target.
+ *
+ * @deprecated since 1.5.x.
+ * Use setDefault.
+ * @see #setDefault(String)
+ */
+ @Deprecated
+ public void setDefaultTarget(final String defaultTarget) {
+ setDefault(defaultTarget);
+ }
+
+ /**
+ * Return the name of the default target of the project.
+ * @return name of the default target or
+ * <code>null</code> if no default has been set.
+ */
+ public String getDefaultTarget() {
+ return defaultTarget;
+ }
+
+ /**
+ * Set the default target of the project.
+ *
+ * @param defaultTarget The name of the default target for this project.
+ * May be <code>null</code>, indicating that there is
+ * no default target.
+ */
+ public void setDefault(final String defaultTarget) {
+ if (defaultTarget != null) {
+ setUserProperty(MagicNames.PROJECT_DEFAULT_TARGET, defaultTarget);
+ }
+ this.defaultTarget = defaultTarget;
+ }
+
+ /**
+ * Set the name of the project, also setting the user
+ * property <code>ant.project.name</code>.
+ *
+ * @param name The name of the project.
+ * Must not be <code>null</code>.
+ */
+ public void setName(final String name) {
+ setUserProperty(MagicNames.PROJECT_NAME, name);
+ this.name = name;
+ }
+
+ /**
+ * Return the project name, if one has been set.
+ *
+ * @return the project name, or <code>null</code> if it hasn't been set.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the project description.
+ *
+ * @param description The description of the project.
+ * May be <code>null</code>.
+ */
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ /**
+ * Return the project description, if one has been set.
+ *
+ * @return the project description, or <code>null</code> if it hasn't
+ * been set.
+ */
+ public String getDescription() {
+ if (description == null) {
+ description = Description.getDescription(this);
+ }
+ return description;
+ }
+
+ /**
+ * Add a filter to the set of global filters.
+ *
+ * @param token The token to filter.
+ * Must not be <code>null</code>.
+ * @param value The replacement value.
+ * Must not be <code>null</code>.
+ * @deprecated since 1.4.x.
+ * Use getGlobalFilterSet().addFilter(token,value)
+ *
+ * @see #getGlobalFilterSet()
+ * @see FilterSet#addFilter(String,String)
+ */
+ @Deprecated
+ public void addFilter(final String token, final String value) {
+ if (token == null) {
+ return;
+ }
+ globalFilterSet.addFilter(new FilterSet.Filter(token, value));
+ }
+
+ /**
+ * Return a hashtable of global filters, mapping tokens to values.
+ *
+ * @return a hashtable of global filters, mapping tokens to values
+ * (String to String).
+ *
+ * @deprecated since 1.4.x
+ * Use getGlobalFilterSet().getFilterHash().
+ *
+ * @see #getGlobalFilterSet()
+ * @see FilterSet#getFilterHash()
+ */
+ @Deprecated
+ public Hashtable<String, String> getFilters() {
+ // we need to build the hashtable dynamically
+ return globalFilterSet.getFilterHash();
+ }
+
+ /**
+ * Set the base directory for the project, checking that
+ * the given filename exists and is a directory.
+ *
+ * @param baseD The project base directory.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the directory if invalid.
+ */
+ public void setBasedir(final String baseD) throws BuildException {
+ setBaseDir(new File(baseD));
+ }
+
+ /**
+ * Set the base directory for the project, checking that
+ * the given file exists and is a directory.
+ *
+ * @param baseDir The project base directory.
+ * Must not be <code>null</code>.
+ * @exception BuildException if the specified file doesn't exist or
+ * isn't a directory.
+ */
+ public void setBaseDir(File baseDir) throws BuildException {
+ baseDir = FILE_UTILS.normalize(baseDir.getAbsolutePath());
+ if (!baseDir.exists()) {
+ throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+ + " does not exist");
+ }
+ if (!baseDir.isDirectory()) {
+ throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+ + " is not a directory");
+ }
+ this.baseDir = baseDir;
+ setPropertyInternal(MagicNames.PROJECT_BASEDIR, this.baseDir.getPath());
+ final String msg = "Project base dir set to: " + this.baseDir;
+ log(msg, MSG_VERBOSE);
+ }
+
+ /**
+ * Return the base directory of the project as a file object.
+ *
+ * @return the project base directory, or <code>null</code> if the
+ * base directory has not been successfully set to a valid value.
+ */
+ public File getBaseDir() {
+ if (baseDir == null) {
+ try {
+ setBasedir(".");
+ } catch (final BuildException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return baseDir;
+ }
+
+ /**
+ * Set &quot;keep-going&quot; mode. In this mode Ant will try to execute
+ * as many targets as possible. All targets that do not depend
+ * on failed target(s) will be executed. If the keepGoing settor/getter
+ * methods are used in conjunction with the <code>ant.executor.class</code>
+ * property, they will have no effect.
+ * @param keepGoingMode &quot;keep-going&quot; mode
+ * @since Ant 1.6
+ */
+ public void setKeepGoingMode(final boolean keepGoingMode) {
+ this.keepGoingMode = keepGoingMode;
+ }
+
+ /**
+ * Return the keep-going mode. If the keepGoing settor/getter
+ * methods are used in conjunction with the <code>ant.executor.class</code>
+ * property, they will have no effect.
+ * @return &quot;keep-going&quot; mode
+ * @since Ant 1.6
+ */
+ public boolean isKeepGoingMode() {
+ return this.keepGoingMode;
+ }
+
+ /**
+ * Return the version of Java this class is running under.
+ * @return the version of Java as a String, e.g. "1.1" .
+ * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
+ * @deprecated since 1.5.x.
+ * Use org.apache.tools.ant.util.JavaEnvUtils instead.
+ */
+ @Deprecated
+ public static String getJavaVersion() {
+ return JavaEnvUtils.getJavaVersion();
+ }
+
+ /**
+ * Set the <code>ant.java.version</code> property and tests for
+ * unsupported JVM versions. If the version is supported,
+ * verbose log messages are generated to record the Java version
+ * and operating system name.
+ *
+ * @exception BuildException if this Java version is not supported.
+ *
+ * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
+ */
+ public void setJavaVersionProperty() throws BuildException {
+ final String javaVersion = JavaEnvUtils.getJavaVersion();
+ setPropertyInternal(MagicNames.ANT_JAVA_VERSION, javaVersion);
+
+ // sanity check
+ if (!JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_1_5)) {
+ throw new BuildException("Ant cannot work on Java prior to 1.5");
+ }
+ log("Detected Java version: " + javaVersion + " in: "
+ + System.getProperty("java.home"), MSG_VERBOSE);
+
+ log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
+ }
+
+ /**
+ * Add all system properties which aren't already defined as
+ * user properties to the project properties.
+ */
+ public void setSystemProperties() {
+ final Properties systemP = System.getProperties();
+ final Enumeration<?> e = systemP.propertyNames();
+ while (e.hasMoreElements()) {
+ final String propertyName = (String) e.nextElement();
+ final String value = systemP.getProperty(propertyName);
+ if (value != null) {
+ this.setPropertyInternal(propertyName, value);
+ }
+ }
+ }
+
+ /**
+ * Add a new task definition to the project.
+ * Attempting to override an existing definition with an
+ * equivalent one (i.e. with the same classname) results in
+ * a verbose log message. Attempting to override an existing definition
+ * with a different one results in a warning log message and
+ * invalidates any tasks which have already been created with the
+ * old definition.
+ *
+ * @param taskName The name of the task to add.
+ * Must not be <code>null</code>.
+ * @param taskClass The full name of the class implementing the task.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the class is unsuitable for being an Ant
+ * task. An error level message is logged before
+ * this exception is thrown.
+ *
+ * @see #checkTaskClass(Class)
+ */
+ public void addTaskDefinition(final String taskName, final Class<?> taskClass)
+ throws BuildException {
+ ComponentHelper.getComponentHelper(this).addTaskDefinition(taskName,
+ taskClass);
+ }
+
+ /**
+ * Check whether or not a class is suitable for serving as Ant task.
+ * Ant task implementation classes must be public, concrete, and have
+ * a no-arg constructor.
+ *
+ * @param taskClass The class to be checked.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the class is unsuitable for being an Ant
+ * task. An error level message is logged before
+ * this exception is thrown.
+ */
+ public void checkTaskClass(final Class<?> taskClass) throws BuildException {
+ ComponentHelper.getComponentHelper(this).checkTaskClass(taskClass);
+
+ if (!Modifier.isPublic(taskClass.getModifiers())) {
+ final String message = taskClass + " is not public";
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ }
+ if (Modifier.isAbstract(taskClass.getModifiers())) {
+ final String message = taskClass + " is abstract";
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ }
+ try {
+ taskClass.getConstructor();
+ // don't have to check for public, since
+ // getConstructor finds public constructors only.
+ } catch (final NoSuchMethodException e) {
+ final String message = "No public no-arg constructor in "
+ + taskClass;
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ } catch (final LinkageError e) {
+ final String message = "Could not load " + taskClass + ": " + e;
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message, e);
+ }
+ if (!Task.class.isAssignableFrom(taskClass)) {
+ TaskAdapter.checkTaskClass(taskClass, this);
+ }
+ }
+
+ /**
+ * Return the current task definition hashtable. The returned hashtable is
+ * &quot;live&quot; and so should not be modified.
+ *
+ * @return a map of from task name to implementing class
+ * (String to Class).
+ */
+ public Hashtable<String, Class<?>> getTaskDefinitions() {
+ return ComponentHelper.getComponentHelper(this).getTaskDefinitions();
+ }
+
+ /**
+ * Return the current task definition map. The returned map is a
+ * copy of the &quot;live&quot; definitions.
+ *
+ * @return a map of from task name to implementing class
+ * (String to Class).
+ *
+ * @since Ant 1.8.1
+ */
+ public Map<String, Class<?>> getCopyOfTaskDefinitions() {
+ return new HashMap<String, Class<?>>(getTaskDefinitions());
+ }
+
+ /**
+ * Add a new datatype definition.
+ * Attempting to override an existing definition with an
+ * equivalent one (i.e. with the same classname) results in
+ * a verbose log message. Attempting to override an existing definition
+ * with a different one results in a warning log message, but the
+ * definition is changed.
+ *
+ * @param typeName The name of the datatype.
+ * Must not be <code>null</code>.
+ * @param typeClass The full name of the class implementing the datatype.
+ * Must not be <code>null</code>.
+ */
+ public void addDataTypeDefinition(final String typeName, final Class<?> typeClass) {
+ ComponentHelper.getComponentHelper(this).addDataTypeDefinition(typeName,
+ typeClass);
+ }
+
+ /**
+ * Return the current datatype definition hashtable. The returned
+ * hashtable is &quot;live&quot; and so should not be modified.
+ *
+ * @return a map of from datatype name to implementing class
+ * (String to Class).
+ */
+ public Hashtable<String, Class<?>> getDataTypeDefinitions() {
+ return ComponentHelper.getComponentHelper(this).getDataTypeDefinitions();
+ }
+
+ /**
+ * Return the current datatype definition map. The returned
+ * map is a copy pf the &quot;live&quot; definitions.
+ *
+ * @return a map of from datatype name to implementing class
+ * (String to Class).
+ *
+ * @since Ant 1.8.1
+ */
+ public Map<String, Class<?>> getCopyOfDataTypeDefinitions() {
+ return new HashMap<String, Class<?>>(getDataTypeDefinitions());
+ }
+
+ /**
+ * Add a <em>new</em> target to the project.
+ *
+ * @param target The target to be added to the project.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the target already exists in the project
+ *
+ * @see Project#addOrReplaceTarget(Target)
+ */
+ public void addTarget(final Target target) throws BuildException {
+ addTarget(target.getName(), target);
+ }
+
+ /**
+ * Add a <em>new</em> target to the project.
+ *
+ * @param targetName The name to use for the target.
+ * Must not be <code>null</code>.
+ * @param target The target to be added to the project.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the target already exists in the project.
+ *
+ * @see Project#addOrReplaceTarget(String, Target)
+ */
+ public void addTarget(final String targetName, final Target target)
+ throws BuildException {
+ if (targets.get(targetName) != null) {
+ throw new BuildException("Duplicate target: `" + targetName + "'");
+ }
+ addOrReplaceTarget(targetName, target);
+ }
+
+ /**
+ * Add a target to the project, or replaces one with the same
+ * name.
+ *
+ * @param target The target to be added or replaced in the project.
+ * Must not be <code>null</code>.
+ */
+ public void addOrReplaceTarget(final Target target) {
+ addOrReplaceTarget(target.getName(), target);
+ }
+
+ /**
+ * Add a target to the project, or replaces one with the same
+ * name.
+ *
+ * @param targetName The name to use for the target.
+ * Must not be <code>null</code>.
+ * @param target The target to be added or replaced in the project.
+ * Must not be <code>null</code>.
+ */
+ public void addOrReplaceTarget(final String targetName, final Target target) {
+ final String msg = " +Target: " + targetName;
+ log(msg, MSG_DEBUG);
+ target.setProject(this);
+ targets.put(targetName, target);
+ }
+
+ /**
+ * Return the hashtable of targets. The returned hashtable
+ * is &quot;live&quot; and so should not be modified.
+ * @return a map from name to target (String to Target).
+ */
+ public Hashtable<String, Target> getTargets() {
+ return targets;
+ }
+
+ /**
+ * Return the map of targets. The returned map
+ * is a copy of the &quot;live&quot; targets.
+ * @return a map from name to target (String to Target).
+ * @since Ant 1.8.1
+ */
+ public Map<String, Target> getCopyOfTargets() {
+ return new HashMap<String, Target>(targets);
+ }
+
+ /**
+ * Create a new instance of a task, adding it to a list of
+ * created tasks for later invalidation. This causes all tasks
+ * to be remembered until the containing project is removed
+ * @param taskType The name of the task to create an instance of.
+ * Must not be <code>null</code>.
+ *
+ * @return an instance of the specified task, or <code>null</code> if
+ * the task name is not recognised.
+ *
+ * @exception BuildException if the task name is recognised but task
+ * creation fails.
+ */
+ public Task createTask(final String taskType) throws BuildException {
+ return ComponentHelper.getComponentHelper(this).createTask(taskType);
+ }
+
+ /**
+ * Create a new instance of a data type.
+ *
+ * @param typeName The name of the data type to create an instance of.
+ * Must not be <code>null</code>.
+ *
+ * @return an instance of the specified data type, or <code>null</code> if
+ * the data type name is not recognised.
+ *
+ * @exception BuildException if the data type name is recognised but
+ * instance creation fails.
+ */
+ public Object createDataType(final String typeName) throws BuildException {
+ return ComponentHelper.getComponentHelper(this).createDataType(typeName);
+ }
+
+ /**
+ * Set the Executor instance for this Project.
+ * @param e the Executor to use.
+ */
+ public void setExecutor(final Executor e) {
+ addReference(MagicNames.ANT_EXECUTOR_REFERENCE, e);
+ }
+
+ /**
+ * Get this Project's Executor (setting it if necessary).
+ * @return an Executor instance.
+ */
+ public Executor getExecutor() {
+ Object o = getReference(MagicNames.ANT_EXECUTOR_REFERENCE);
+ if (o == null) {
+ String classname = getProperty(MagicNames.ANT_EXECUTOR_CLASSNAME);
+ if (classname == null) {
+ classname = DefaultExecutor.class.getName();
+ }
+ log("Attempting to create object of type " + classname, MSG_DEBUG);
+ try {
+ o = Class.forName(classname, true, coreLoader).newInstance();
+ } catch (final ClassNotFoundException seaEnEfEx) {
+ //try the current classloader
+ try {
+ o = Class.forName(classname).newInstance();
+ } catch (final Exception ex) {
+ log(ex.toString(), MSG_ERR);
+ }
+ } catch (final Exception ex) {
+ log(ex.toString(), MSG_ERR);
+ }
+ if (o == null) {
+ throw new BuildException(
+ "Unable to obtain a Target Executor instance.");
+ }
+ setExecutor((Executor) o);
+ }
+ return (Executor) o;
+ }
+
+ /**
+ * Execute the specified sequence of targets, and the targets
+ * they depend on.
+ *
+ * @param names A vector of target name strings to execute.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the build failed.
+ */
+ public void executeTargets(final Vector<String> names) throws BuildException {
+ setUserProperty(MagicNames.PROJECT_INVOKED_TARGETS,
+ CollectionUtils.flattenToString(names));
+ getExecutor().executeTargets(this, names.toArray(new String[names.size()]));
+ }
+
+ /**
+ * Demultiplex output so that each task receives the appropriate
+ * messages. If the current thread is not currently executing a task,
+ * the message is logged directly.
+ *
+ * @param output Message to handle. Should not be <code>null</code>.
+ * @param isWarning Whether the text represents an warning (<code>true</code>)
+ * or information (<code>false</code>).
+ */
+ public void demuxOutput(final String output, final boolean isWarning) {
+ final Task task = getThreadTask(Thread.currentThread());
+ if (task == null) {
+ log(output, isWarning ? MSG_WARN : MSG_INFO);
+ } else {
+ if (isWarning) {
+ task.handleErrorOutput(output);
+ } else {
+ task.handleOutput(output);
+ }
+ }
+ }
+
+ /**
+ * Read data from the default input stream. If no default has been
+ * specified, System.in is used.
+ *
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ * @since Ant 1.6
+ */
+ public int defaultInput(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ if (defaultInputStream != null) {
+ System.out.flush();
+ return defaultInputStream.read(buffer, offset, length);
+ } else {
+ throw new EOFException("No input provided for project");
+ }
+ }
+
+ /**
+ * Demux an input request to the correct task.
+ *
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ * @since Ant 1.6
+ */
+ public int demuxInput(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ final Task task = getThreadTask(Thread.currentThread());
+ if (task == null) {
+ return defaultInput(buffer, offset, length);
+ } else {
+ return task.handleInput(buffer, offset, length);
+ }
+ }
+
+ /**
+ * Demultiplex flush operations so that each task receives the appropriate
+ * messages. If the current thread is not currently executing a task,
+ * the message is logged directly.
+ *
+ * @since Ant 1.5.2
+ *
+ * @param output Message to handle. Should not be <code>null</code>.
+ * @param isError Whether the text represents an error (<code>true</code>)
+ * or information (<code>false</code>).
+ */
+ public void demuxFlush(final String output, final boolean isError) {
+ final Task task = getThreadTask(Thread.currentThread());
+ if (task == null) {
+ fireMessageLogged(this, output, isError ? MSG_ERR : MSG_INFO);
+ } else {
+ if (isError) {
+ task.handleErrorFlush(output);
+ } else {
+ task.handleFlush(output);
+ }
+ }
+ }
+
+ /**
+ * Execute the specified target and any targets it depends on.
+ *
+ * @param targetName The name of the target to execute.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the build failed.
+ */
+ public void executeTarget(final String targetName) throws BuildException {
+
+ // sanity check ourselves, if we've been asked to build nothing
+ // then we should complain
+
+ if (targetName == null) {
+ final String msg = "No target specified";
+ throw new BuildException(msg);
+ }
+
+ // Sort and run the dependency tree.
+ // Sorting checks if all the targets (and dependencies)
+ // exist, and if there is any cycle in the dependency
+ // graph.
+ executeSortedTargets(topoSort(targetName, targets, false));
+ }
+
+ /**
+ * Execute a <code>Vector</code> of sorted targets.
+ * @param sortedTargets the aforementioned <code>Vector</code>.
+ * @throws BuildException on error.
+ */
+ public void executeSortedTargets(final Vector<Target> sortedTargets)
+ throws BuildException {
+ final Set<String> succeededTargets = new HashSet<String>();
+ BuildException buildException = null; // first build exception
+ for (final Target curtarget : sortedTargets) {
+ boolean canExecute = true;
+ for (final Enumeration<String> depIter = curtarget.getDependencies();
+ depIter.hasMoreElements();) {
+ final String dependencyName = depIter.nextElement();
+ if (!succeededTargets.contains(dependencyName)) {
+ canExecute = false;
+ log(curtarget,
+ "Cannot execute '" + curtarget.getName() + "' - '"
+ + dependencyName + "' failed or was not executed.",
+ MSG_ERR);
+ break;
+ }
+ }
+ if (canExecute) {
+ Throwable thrownException = null;
+ try {
+ curtarget.performTasks();
+ succeededTargets.add(curtarget.getName());
+ } catch (final RuntimeException ex) {
+ if (!(keepGoingMode)) {
+ throw ex; // throw further
+ }
+ thrownException = ex;
+ } catch (final Throwable ex) {
+ if (!(keepGoingMode)) {
+ throw new BuildException(ex);
+ }
+ thrownException = ex;
+ }
+ if (thrownException != null) {
+ if (thrownException instanceof BuildException) {
+ log(curtarget,
+ "Target '" + curtarget.getName()
+ + "' failed with message '"
+ + thrownException.getMessage() + "'.", MSG_ERR);
+ // only the first build exception is reported
+ if (buildException == null) {
+ buildException = (BuildException) thrownException;
+ }
+ } else {
+ log(curtarget,
+ "Target '" + curtarget.getName()
+ + "' failed with message '"
+ + thrownException.getMessage() + "'.", MSG_ERR);
+ thrownException.printStackTrace(System.err);
+ if (buildException == null) {
+ buildException =
+ new BuildException(thrownException);
+ }
+ }
+ }
+ }
+ }
+ if (buildException != null) {
+ throw buildException;
+ }
+ }
+
+ /**
+ * Return the canonical form of a filename.
+ * <p>
+ * If the specified file name is relative it is resolved
+ * with respect to the given root directory.
+ *
+ * @param fileName The name of the file to resolve.
+ * Must not be <code>null</code>.
+ *
+ * @param rootDir The directory respective to which relative file names
+ * are resolved. May be <code>null</code>, in which case
+ * the current directory is used.
+ *
+ * @return the resolved File.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public File resolveFile(final String fileName, final File rootDir) {
+ return FILE_UTILS.resolveFile(rootDir, fileName);
+ }
+
+ /**
+ * Return the canonical form of a filename.
+ * <p>
+ * If the specified file name is relative it is resolved
+ * with respect to the project's base directory.
+ *
+ * @param fileName The name of the file to resolve.
+ * Must not be <code>null</code>.
+ *
+ * @return the resolved File.
+ *
+ */
+ public File resolveFile(final String fileName) {
+ return FILE_UTILS.resolveFile(baseDir, fileName);
+ }
+
+ /**
+ * Translate a path into its native (platform specific) format.
+ * <p>
+ * This method uses PathTokenizer to separate the input path
+ * into its components. This handles DOS style paths in a relatively
+ * sensible way. The file separators are then converted to their platform
+ * specific versions.
+ *
+ * @param toProcess The path to be translated.
+ * May be <code>null</code>.
+ *
+ * @return the native version of the specified path or
+ * an empty string if the path is <code>null</code> or empty.
+ *
+ * @deprecated since 1.7
+ * Use FileUtils.translatePath instead.
+ *
+ * @see PathTokenizer
+ */
+ @Deprecated
+ public static String translatePath(final String toProcess) {
+ return FileUtils.translatePath(toProcess);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination.
+ * No filtering is performed.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final String sourceFile, final String destFile)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final String sourceFile, final String destFile, final boolean filtering)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering should be used and if
+ * source files may overwrite newer destination files.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final String sourceFile, final String destFile, final boolean filtering,
+ final boolean overwrite) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null, overwrite);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering should be used, if
+ * source files may overwrite newer destination files, and if the
+ * last modified time of the resulting file should be set to
+ * that of the source file.
+ *
+ * @param sourceFile Name of file to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile Name of file to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final String sourceFile, final String destFile, final boolean filtering,
+ final boolean overwrite, final boolean preserveLastModified)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null, overwrite, preserveLastModified);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination.
+ * No filtering is performed.
+ *
+ * @param sourceFile File to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile File to copy to.
+ * Must not be <code>null</code>.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final File sourceFile, final File destFile) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used.
+ *
+ * @param sourceFile File to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile File to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ *
+ * @exception IOException if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final File sourceFile, final File destFile, final boolean filtering)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering should be used and if
+ * source files may overwrite newer destination files.
+ *
+ * @param sourceFile File to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile File to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ *
+ * @exception IOException if the file cannot be copied.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final File sourceFile, final File destFile, final boolean filtering,
+ final boolean overwrite) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null, overwrite);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a
+ * destination specifying if token filtering should be used, if
+ * source files may overwrite newer destination files, and if the
+ * last modified time of the resulting file should be set to
+ * that of the source file.
+ *
+ * @param sourceFile File to copy from.
+ * Must not be <code>null</code>.
+ * @param destFile File to copy to.
+ * Must not be <code>null</code>.
+ * @param filtering Whether or not token filtering should be used during
+ * the copy.
+ * @param overwrite Whether or not the destination file should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the resulting file should be set to that
+ * of the source file.
+ *
+ * @exception IOException if the file cannot be copied.
+ *
+ * @deprecated since 1.4.x
+ */
+ @Deprecated
+ public void copyFile(final File sourceFile, final File destFile, final boolean filtering,
+ final boolean overwrite, final boolean preserveLastModified)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile,
+ filtering ? globalFilters : null, overwrite, preserveLastModified);
+ }
+
+ /**
+ * Call File.setLastModified(long time) on Java above 1.1, and logs
+ * a warning on Java 1.1.
+ *
+ * @param file The file to set the last modified time on.
+ * Must not be <code>null</code>.
+ *
+ * @param time the required modification time.
+ *
+ * @deprecated since 1.4.x
+ *
+ * @exception BuildException if the last modified time cannot be set
+ * despite running on a platform with a version
+ * above 1.1.
+ */
+ @Deprecated
+ public void setFileLastModified(final File file, final long time)
+ throws BuildException {
+ FILE_UTILS.setFileLastModified(file, time);
+ log("Setting modification time for " + file, MSG_VERBOSE);
+ }
+
+ /**
+ * Return the boolean equivalent of a string, which is considered
+ * <code>true</code> if either <code>"on"</code>, <code>"true"</code>,
+ * or <code>"yes"</code> is found, ignoring case.
+ *
+ * @param s The string to convert to a boolean value.
+ *
+ * @return <code>true</code> if the given string is <code>"on"</code>,
+ * <code>"true"</code> or <code>"yes"</code>, or
+ * <code>false</code> otherwise.
+ */
+ public static boolean toBoolean(final String s) {
+ return ("on".equalsIgnoreCase(s)
+ || "true".equalsIgnoreCase(s)
+ || "yes".equalsIgnoreCase(s));
+ }
+
+ /**
+ * Get the Project instance associated with the specified object.
+ * @param o the object to query.
+ * @return Project instance, if any.
+ * @since Ant 1.7.1
+ */
+ public static Project getProject(final Object o) {
+ if (o instanceof ProjectComponent) {
+ return ((ProjectComponent) o).getProject();
+ }
+ try {
+ final Method m = o.getClass().getMethod("getProject", (Class[]) null);
+ if (Project.class == m.getReturnType()) {
+ return (Project) m.invoke(o, (Object[]) null);
+ }
+ } catch (final Exception e) {
+ //too bad
+ }
+ return null;
+ }
+
+ /**
+ * Topologically sort a set of targets. Equivalent to calling
+ * <code>topoSort(new String[] {root}, targets, true)</code>.
+ *
+ * @param root The name of the root target. The sort is created in such
+ * a way that the sequence of Targets up to the root
+ * target is the minimum possible such sequence.
+ * Must not be <code>null</code>.
+ * @param targetTable A Hashtable mapping names to Targets.
+ * Must not be <code>null</code>.
+ * @return a Vector of ALL Target objects in sorted order.
+ * @exception BuildException if there is a cyclic dependency among the
+ * targets, or if a named target does not exist.
+ */
+ public final Vector<Target> topoSort(final String root, final Hashtable<String, Target> targetTable)
+ throws BuildException {
+ return topoSort(new String[] {root}, targetTable, true);
+ }
+
+ /**
+ * Topologically sort a set of targets. Equivalent to calling
+ * <code>topoSort(new String[] {root}, targets, returnAll)</code>.
+ *
+ * @param root The name of the root target. The sort is created in such
+ * a way that the sequence of Targets up to the root
+ * target is the minimum possible such sequence.
+ * Must not be <code>null</code>.
+ * @param targetTable A Hashtable mapping names to Targets.
+ * Must not be <code>null</code>.
+ * @param returnAll <code>boolean</code> indicating whether to return all
+ * targets, or the execution sequence only.
+ * @return a Vector of Target objects in sorted order.
+ * @exception BuildException if there is a cyclic dependency among the
+ * targets, or if a named target does not exist.
+ * @since Ant 1.6.3
+ */
+ public final Vector<Target> topoSort(final String root, final Hashtable<String, Target> targetTable,
+ final boolean returnAll) throws BuildException {
+ return topoSort(new String[] {root}, targetTable, returnAll);
+ }
+
+ /**
+ * Topologically sort a set of targets.
+ *
+ * @param root <code>String[]</code> containing the names of the root targets.
+ * The sort is created in such a way that the ordered sequence of
+ * Targets is the minimum possible such sequence to the specified
+ * root targets.
+ * Must not be <code>null</code>.
+ * @param targetTable A map of names to targets (String to Target).
+ * Must not be <code>null</code>.
+ * @param returnAll <code>boolean</code> indicating whether to return all
+ * targets, or the execution sequence only.
+ * @return a Vector of Target objects in sorted order.
+ * @exception BuildException if there is a cyclic dependency among the
+ * targets, or if a named target does not exist.
+ * @since Ant 1.6.3
+ */
+ public final Vector<Target> topoSort(final String[] root, final Hashtable<String, Target> targetTable,
+ final boolean returnAll) throws BuildException {
+ final Vector<Target> ret = new VectorSet<Target>();
+ final Hashtable<String, String> state = new Hashtable<String, String>();
+ final Stack<String> visiting = new Stack<String>();
+
+ // We first run a DFS based sort using each root as a starting node.
+ // This creates the minimum sequence of Targets to the root node(s).
+ // We then do a sort on any remaining unVISITED targets.
+ // This is unnecessary for doing our build, but it catches
+ // circular dependencies or missing Targets on the entire
+ // dependency tree, not just on the Targets that depend on the
+ // build Target.
+
+ for (int i = 0; i < root.length; i++) {
+ final String st = (state.get(root[i]));
+ if (st == null) {
+ tsort(root[i], targetTable, state, visiting, ret);
+ } else if (st == VISITING) {
+ throw new RuntimeException("Unexpected node in visiting state: "
+ + root[i]);
+ }
+ }
+ final StringBuffer buf = new StringBuffer("Build sequence for target(s)");
+
+ for (int j = 0; j < root.length; j++) {
+ buf.append((j == 0) ? " `" : ", `").append(root[j]).append('\'');
+ }
+ buf.append(" is " + ret);
+ log(buf.toString(), MSG_VERBOSE);
+
+ final Vector<Target> complete = (returnAll) ? ret : new Vector<Target>(ret);
+ for (final Enumeration<String> en = targetTable.keys(); en.hasMoreElements();) {
+ final String curTarget = en.nextElement();
+ final String st = state.get(curTarget);
+ if (st == null) {
+ tsort(curTarget, targetTable, state, visiting, complete);
+ } else if (st == VISITING) {
+ throw new RuntimeException("Unexpected node in visiting state: "
+ + curTarget);
+ }
+ }
+ log("Complete build sequence is " + complete, MSG_VERBOSE);
+ return ret;
+ }
+
+ /**
+ * Perform a single step in a recursive depth-first-search traversal of
+ * the target dependency tree.
+ * <p>
+ * The current target is first set to the &quot;visiting&quot; state, and
+ * pushed onto the &quot;visiting&quot; stack.
+ * <p>
+ * An exception is then thrown if any child of the current node is in the
+ * visiting state, as that implies a circular dependency. The exception
+ * contains details of the cycle, using elements of the &quot;visiting&quot;
+ * stack.
+ * <p>
+ * If any child has not already been &quot;visited&quot;, this method is
+ * called recursively on it.
+ * <p>
+ * The current target is then added to the ordered list of targets. Note
+ * that this is performed after the children have been visited in order
+ * to get the correct order. The current target is set to the
+ * &quot;visited&quot; state.
+ * <p>
+ * By the time this method returns, the ordered list contains the sequence
+ * of targets up to and including the current target.
+ *
+ * @param root The current target to inspect.
+ * Must not be <code>null</code>.
+ * @param targetTable A mapping from names to targets (String to Target).
+ * Must not be <code>null</code>.
+ * @param state A mapping from target names to states (String to String).
+ * The states in question are &quot;VISITING&quot; and
+ * &quot;VISITED&quot;. Must not be <code>null</code>.
+ * @param visiting A stack of targets which are currently being visited.
+ * Must not be <code>null</code>.
+ * @param ret The list to add target names to. This will end up
+ * containing the complete list of dependencies in
+ * dependency order.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if a non-existent target is specified or if
+ * a circular dependency is detected.
+ */
+ private void tsort(final String root, final Hashtable<String, Target> targetTable,
+ final Hashtable<String, String> state, final Stack<String> visiting,
+ final Vector<Target> ret)
+ throws BuildException {
+ state.put(root, VISITING);
+ visiting.push(root);
+
+ final Target target = targetTable.get(root);
+
+ // Make sure we exist
+ if (target == null) {
+ final StringBuilder sb = new StringBuilder("Target \"");
+ sb.append(root);
+ sb.append("\" does not exist in the project \"");
+ sb.append(name);
+ sb.append("\". ");
+ visiting.pop();
+ if (!visiting.empty()) {
+ final String parent = visiting.peek();
+ sb.append("It is used from target \"");
+ sb.append(parent);
+ sb.append("\".");
+ }
+ throw new BuildException(new String(sb));
+ }
+ for (final Enumeration<String> en = target.getDependencies(); en.hasMoreElements();) {
+ final String cur = en.nextElement();
+ final String m = state.get(cur);
+ if (m == null) {
+ // Not been visited
+ tsort(cur, targetTable, state, visiting, ret);
+ } else if (m == VISITING) {
+ // Currently visiting this node, so have a cycle
+ throw makeCircularException(cur, visiting);
+ }
+ }
+ final String p = visiting.pop();
+ if (root != p) {
+ throw new RuntimeException("Unexpected internal error: expected to "
+ + "pop " + root + " but got " + p);
+ }
+ state.put(root, VISITED);
+ ret.addElement(target);
+ }
+
+ /**
+ * Build an appropriate exception detailing a specified circular
+ * dependency.
+ *
+ * @param end The dependency to stop at. Must not be <code>null</code>.
+ * @param stk A stack of dependencies. Must not be <code>null</code>.
+ *
+ * @return a BuildException detailing the specified circular dependency.
+ */
+ private static BuildException makeCircularException(final String end, final Stack<String> stk) {
+ final StringBuilder sb = new StringBuilder("Circular dependency: ");
+ sb.append(end);
+ String c;
+ do {
+ c = stk.pop();
+ sb.append(" <- ");
+ sb.append(c);
+ } while (!c.equals(end));
+ return new BuildException(sb.toString());
+ }
+
+ /**
+ * Inherit the id references.
+ * @param parent the parent project of this project.
+ */
+ public void inheritIDReferences(final Project parent) {
+ }
+
+ /**
+ * Add an id reference.
+ * Used for broken build files.
+ * @param id the id to set.
+ * @param value the value to set it to (Unknown element in this case.
+ */
+ public void addIdReference(final String id, final Object value) {
+ idReferences.put(id, value);
+ }
+
+ /**
+ * Add a reference to the project.
+ *
+ * @param referenceName The name of the reference. Must not be <code>null</code>.
+ * @param value The value of the reference.
+ */
+ public void addReference(final String referenceName, final Object value) {
+ final Object old = ((AntRefTable) references).getReal(referenceName);
+ if (old == value) {
+ // no warning, this is not changing anything
+ return;
+ }
+ if (old != null && !(old instanceof UnknownElement)) {
+ log("Overriding previous definition of reference to " + referenceName,
+ MSG_VERBOSE);
+ }
+ log("Adding reference: " + referenceName, MSG_DEBUG);
+ references.put(referenceName, value);
+ }
+
+ /**
+ * Return a map of the references in the project (String to Object).
+ * The returned hashtable is &quot;live&quot; and so must not be modified.
+ *
+ * @return a map of the references in the project (String to Object).
+ */
+ public Hashtable<String, Object> getReferences() {
+ return references;
+ }
+
+ /**
+ * Does the project know this reference?
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean hasReference(final String key) {
+ return references.containsKey(key);
+ }
+
+ /**
+ * Return a map of the references in the project (String to
+ * Object). The returned hashtable is a copy of the
+ * &quot;live&quot; references.
+ *
+ * @return a map of the references in the project (String to Object).
+ *
+ * @since Ant 1.8.1
+ */
+ public Map<String, Object> getCopyOfReferences() {
+ return new HashMap<String, Object>(references);
+ }
+
+ /**
+ * Look up a reference by its key (ID).
+ *
+ * @param key The key for the desired reference.
+ * Must not be <code>null</code>.
+ *
+ * @return the reference with the specified ID, or <code>null</code> if
+ * there is no such reference in the project, with type inference.
+ */
+ public <T> T getReference(final String key) {
+ @SuppressWarnings("unchecked")
+ final T ret = (T) references.get(key);
+ if (ret != null) {
+ return ret;
+ }
+ if (!key.equals(MagicNames.REFID_PROPERTY_HELPER)) {
+ try {
+ if (PropertyHelper.getPropertyHelper(this).containsProperties(key)) {
+ log("Unresolvable reference " + key
+ + " might be a misuse of property expansion syntax.", MSG_WARN);
+ }
+ } catch (final Exception e) {
+ //ignore
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return a description of the type of the given element, with
+ * special handling for instances of tasks and data types.
+ * <p>
+ * This is useful for logging purposes.
+ *
+ * @param element The element to describe.
+ * Must not be <code>null</code>.
+ *
+ * @return a description of the element type.
+ *
+ * @since 1.95, Ant 1.5
+ */
+ public String getElementName(final Object element) {
+ return ComponentHelper.getComponentHelper(this).getElementName(element);
+ }
+
+ /**
+ * Send a &quot;build started&quot; event
+ * to the build listeners for this project.
+ */
+ public void fireBuildStarted() {
+ final BuildEvent event = new BuildEvent(this);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].buildStarted(event);
+ }
+ }
+
+ /**
+ * Send a &quot;build finished&quot; event to the build listeners
+ * for this project.
+ * @param exception an exception indicating a reason for a build
+ * failure. May be <code>null</code>, indicating
+ * a successful build.
+ */
+ public void fireBuildFinished(final Throwable exception) {
+ final BuildEvent event = new BuildEvent(this);
+ event.setException(exception);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].buildFinished(event);
+ }
+ // Inform IH to clear the cache
+ IntrospectionHelper.clearCache();
+ }
+
+ /**
+ * Send a &quot;subbuild started&quot; event to the build listeners for
+ * this project.
+ *
+ * @since Ant 1.6.2
+ */
+ public void fireSubBuildStarted() {
+ final BuildEvent event = new BuildEvent(this);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ if (currListeners[i] instanceof SubBuildListener) {
+ ((SubBuildListener) currListeners[i]).subBuildStarted(event);
+ }
+ }
+ }
+
+ /**
+ * Send a &quot;subbuild finished&quot; event to the build listeners for
+ * this project.
+ * @param exception an exception indicating a reason for a build
+ * failure. May be <code>null</code>, indicating
+ * a successful build.
+ *
+ * @since Ant 1.6.2
+ */
+ public void fireSubBuildFinished(final Throwable exception) {
+ final BuildEvent event = new BuildEvent(this);
+ event.setException(exception);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ if (currListeners[i] instanceof SubBuildListener) {
+ ((SubBuildListener) currListeners[i]).subBuildFinished(event);
+ }
+ }
+ }
+
+ /**
+ * Send a &quot;target started&quot; event to the build listeners
+ * for this project.
+ *
+ * @param target The target which is starting to build.
+ * Must not be <code>null</code>.
+ */
+ protected void fireTargetStarted(final Target target) {
+ final BuildEvent event = new BuildEvent(target);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].targetStarted(event);
+ }
+
+ }
+
+ /**
+ * Send a &quot;target finished&quot; event to the build listeners
+ * for this project.
+ *
+ * @param target The target which has finished building.
+ * Must not be <code>null</code>.
+ * @param exception an exception indicating a reason for a build
+ * failure. May be <code>null</code>, indicating
+ * a successful build.
+ */
+ protected void fireTargetFinished(final Target target, final Throwable exception) {
+ final BuildEvent event = new BuildEvent(target);
+ event.setException(exception);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].targetFinished(event);
+ }
+
+ }
+
+ /**
+ * Send a &quot;task started&quot; event to the build listeners
+ * for this project.
+ *
+ * @param task The target which is starting to execute.
+ * Must not be <code>null</code>.
+ */
+ protected void fireTaskStarted(final Task task) {
+ // register this as the current task on the current thread.
+ registerThreadTask(Thread.currentThread(), task);
+ final BuildEvent event = new BuildEvent(task);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].taskStarted(event);
+ }
+ }
+
+ /**
+ * Send a &quot;task finished&quot; event to the build listeners for this
+ * project.
+ *
+ * @param task The task which has finished executing.
+ * Must not be <code>null</code>.
+ * @param exception an exception indicating a reason for a build
+ * failure. May be <code>null</code>, indicating
+ * a successful build.
+ */
+ protected void fireTaskFinished(final Task task, final Throwable exception) {
+ registerThreadTask(Thread.currentThread(), null);
+ System.out.flush();
+ System.err.flush();
+ final BuildEvent event = new BuildEvent(task);
+ event.setException(exception);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].taskFinished(event);
+ }
+
+ }
+
+ /**
+ * Send a &quot;message logged&quot; event to the build listeners
+ * for this project.
+ *
+ * @param event The event to send. This should be built up with the
+ * appropriate task/target/project by the caller, so that
+ * this method can set the message and priority, then send
+ * the event. Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param priority The priority of the message.
+ */
+ private void fireMessageLoggedEvent(final BuildEvent event, String message,
+ final int priority) {
+
+ if (message == null) {
+ message = String.valueOf(message);
+ }
+ if (message.endsWith(StringUtils.LINE_SEP)) {
+ final int endIndex = message.length() - StringUtils.LINE_SEP.length();
+ event.setMessage(message.substring(0, endIndex), priority);
+ } else {
+ event.setMessage(message, priority);
+ }
+ if (isLoggingMessage.get() != Boolean.FALSE) {
+ /*
+ * One of the Listeners has attempted to access
+ * System.err or System.out.
+ *
+ * We used to throw an exception in this case, but
+ * sometimes Listeners can't prevent it(like our own
+ * Log4jListener which invokes getLogger() which in
+ * turn wants to write to the console).
+ *
+ * @see http://marc.theaimsgroup.com/?t=110538624200006&r=1&w=2
+ *
+ * We now (Ant 1.6.3 and later) simply swallow the message.
+ */
+ return;
+ }
+ try {
+ isLoggingMessage.set(Boolean.TRUE);
+ final BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].messageLogged(event);
+ }
+ } finally {
+ isLoggingMessage.set(Boolean.FALSE);
+ }
+ }
+
+ /**
+ * Send a &quot;message logged&quot; project level event
+ * to the build listeners for this project.
+ *
+ * @param project The project generating the event.
+ * Should not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param priority The priority of the message.
+ */
+ protected void fireMessageLogged(final Project project, final String message,
+ final int priority) {
+ fireMessageLogged(project, message, null, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; project level event
+ * to the build listeners for this project.
+ *
+ * @param project The project generating the event.
+ * Should not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param throwable The exception that caused this message. May be <code>null</code>.
+ * @param priority The priority of the message.
+ * @since 1.7
+ */
+ protected void fireMessageLogged(final Project project, final String message,
+ final Throwable throwable, final int priority) {
+ final BuildEvent event = new BuildEvent(project);
+ event.setException(throwable);
+ fireMessageLoggedEvent(event, message, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; target level event
+ * to the build listeners for this project.
+ *
+ * @param target The target generating the event.
+ * Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param priority The priority of the message.
+ */
+ protected void fireMessageLogged(final Target target, final String message,
+ final int priority) {
+ fireMessageLogged(target, message, null, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; target level event
+ * to the build listeners for this project.
+ *
+ * @param target The target generating the event.
+ * Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param throwable The exception that caused this message. May be <code>null</code>.
+ * @param priority The priority of the message.
+ * @since 1.7
+ */
+ protected void fireMessageLogged(final Target target, final String message,
+ final Throwable throwable, final int priority) {
+ final BuildEvent event = new BuildEvent(target);
+ event.setException(throwable);
+ fireMessageLoggedEvent(event, message, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; task level event
+ * to the build listeners for this project.
+ *
+ * @param task The task generating the event.
+ * Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param priority The priority of the message.
+ */
+ protected void fireMessageLogged(final Task task, final String message, final int priority) {
+ fireMessageLogged(task, message, null, priority);
+ }
+
+ /**
+ * Send a &quot;message logged&quot; task level event
+ * to the build listeners for this project.
+ *
+ * @param task The task generating the event.
+ * Must not be <code>null</code>.
+ * @param message The message to send. Should not be <code>null</code>.
+ * @param throwable The exception that caused this message. May be <code>null</code>.
+ * @param priority The priority of the message.
+ * @since 1.7
+ */
+ protected void fireMessageLogged(final Task task, final String message,
+ final Throwable throwable, final int priority) {
+ final BuildEvent event = new BuildEvent(task);
+ event.setException(throwable);
+ fireMessageLoggedEvent(event, message, priority);
+ }
+
+ /**
+ * Register a task as the current task for a thread.
+ * If the task is null, the thread's entry is removed.
+ *
+ * @param thread the thread on which the task is registered.
+ * @param task the task to be registered.
+ * @since Ant 1.5
+ */
+ public void registerThreadTask(final Thread thread, final Task task) {
+ synchronized (threadTasks) {
+ if (task != null) {
+ threadTasks.put(thread, task);
+ threadGroupTasks.put(thread.getThreadGroup(), task);
+ } else {
+ threadTasks.remove(thread);
+ threadGroupTasks.remove(thread.getThreadGroup());
+ }
+ }
+ }
+
+ /**
+ * Get the current task associated with a thread, if any.
+ *
+ * @param thread the thread for which the task is required.
+ * @return the task which is currently registered for the given thread or
+ * null if no task is registered.
+ */
+ public Task getThreadTask(final Thread thread) {
+ synchronized (threadTasks) {
+ Task task = threadTasks.get(thread);
+ if (task == null) {
+ ThreadGroup group = thread.getThreadGroup();
+ while (task == null && group != null) {
+ task = threadGroupTasks.get(group);
+ group = group.getParent();
+ }
+ }
+ return task;
+ }
+ }
+
+
+ // Should move to a separate public class - and have API to add
+ // listeners, etc.
+ private static class AntRefTable extends Hashtable<String, Object> {
+ private static final long serialVersionUID = 1L;
+
+ AntRefTable() {
+ super();
+ }
+
+ /** Returns the unmodified original object.
+ * This method should be called internally to
+ * get the &quot;real&quot; object.
+ * The normal get method will do the replacement
+ * of UnknownElement (this is similar with the JDNI
+ * refs behavior).
+ */
+ private Object getReal(final Object key) {
+ return super.get(key);
+ }
+
+ /** Get method for the reference table.
+ * It can be used to hook dynamic references and to modify
+ * some references on the fly--for example for delayed
+ * evaluation.
+ *
+ * It is important to make sure that the processing that is
+ * done inside is not calling get indirectly.
+ *
+ * @param key lookup key.
+ * @return mapped value.
+ */
+ @Override
+ public Object get(final Object key) {
+ Object o = getReal(key);
+ if (o instanceof UnknownElement) {
+ // Make sure that
+ final UnknownElement ue = (UnknownElement) o;
+ ue.maybeConfigure();
+ o = ue.getRealThing();
+ }
+ return o;
+ }
+ }
+
+ /**
+ * Set a reference to this Project on the parameterized object.
+ * Need to set the project before other set/add elements
+ * are called.
+ * @param obj the object to invoke setProject(this) on.
+ */
+ public final void setProjectReference(final Object obj) {
+ if (obj instanceof ProjectComponent) {
+ ((ProjectComponent) obj).setProject(this);
+ return;
+ }
+ try {
+ final Method method =
+ obj.getClass().getMethod(
+ "setProject", new Class[] {Project.class});
+ if (method != null) {
+ method.invoke(obj, new Object[] {this});
+ }
+ } catch (final Throwable e) {
+ // ignore this if the object does not have
+ // a set project method or the method
+ // is private/protected.
+ }
+ }
+
+ /**
+ * Resolve the file relative to the project's basedir and return it as a
+ * FileResource.
+ * @param name the name of the file to resolve.
+ * @return the file resource.
+ * @since Ant 1.7
+ */
+ public Resource getResource(final String name) {
+ return new FileResource(getBaseDir(), name);
+ }
+}