Save This Page
Home » apache-ant-1.8.1 » org.apache.tools » ant » [javadoc | source]
    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    *
   17    */
   18   package org.apache.tools.ant;
   19   
   20   import java.io.File;
   21   import java.io.IOException;
   22   import java.io.EOFException;
   23   import java.io.InputStream;
   24   import java.lang.reflect.Method;
   25   import java.lang.reflect.Modifier;
   26   import java.util.Collections;
   27   import java.util.Enumeration;
   28   import java.util.Hashtable;
   29   import java.util.Properties;
   30   import java.util.Stack;
   31   import java.util.Vector;
   32   import java.util.Set;
   33   import java.util.HashSet;
   34   import java.util.HashMap;
   35   import java.util.Map;
   36   import java.util.WeakHashMap;
   37   import org.apache.tools.ant.input.DefaultInputHandler;
   38   import org.apache.tools.ant.input.InputHandler;
   39   import org.apache.tools.ant.helper.DefaultExecutor;
   40   import org.apache.tools.ant.types.FilterSet;
   41   import org.apache.tools.ant.types.FilterSetCollection;
   42   import org.apache.tools.ant.types.Description;
   43   import org.apache.tools.ant.types.Path;
   44   import org.apache.tools.ant.types.Resource;
   45   import org.apache.tools.ant.types.ResourceFactory;
   46   import org.apache.tools.ant.types.resources.FileResource;
   47   import org.apache.tools.ant.util.CollectionUtils;
   48   import org.apache.tools.ant.util.FileUtils;
   49   import org.apache.tools.ant.util.JavaEnvUtils;
   50   import org.apache.tools.ant.util.StringUtils;
   51   import org.apache.tools.ant.util.VectorSet;
   52   
   53   /**
   54    * Central representation of an Ant project. This class defines an
   55    * Ant project with all of its targets, tasks and various other
   56    * properties. It also provides the mechanism to kick off a build using
   57    * a particular target name.
   58    * <p>
   59    * This class also encapsulates methods which allow files to be referred
   60    * to using abstract path names which are translated to native system
   61    * file paths at runtime.
   62    *
   63    */
   64   public class Project implements ResourceFactory {
   65       /** Message priority of &quot;error&quot;. */
   66       public static final int MSG_ERR = 0;
   67       /** Message priority of &quot;warning&quot;. */
   68       public static final int MSG_WARN = 1;
   69       /** Message priority of &quot;information&quot;. */
   70       public static final int MSG_INFO = 2;
   71       /** Message priority of &quot;verbose&quot;. */
   72       public static final int MSG_VERBOSE = 3;
   73       /** Message priority of &quot;debug&quot;. */
   74       public static final int MSG_DEBUG = 4;
   75   
   76       /**
   77        * Constant for the &quot;visiting&quot; state, used when
   78        * traversing a DFS of target dependencies.
   79        */
   80       private static final String VISITING = "VISITING";
   81       /**
   82        * Constant for the &quot;visited&quot; state, used when
   83        * traversing a DFS of target dependencies.
   84        */
   85       private static final String VISITED = "VISITED";
   86   
   87       /**
   88        * Version constant for Java 1.0 .
   89        *
   90        * @deprecated since 1.5.x.
   91        *             Use {@link JavaEnvUtils#JAVA_1_0} instead.
   92        */
   93       public static final String JAVA_1_0 = JavaEnvUtils.JAVA_1_0;
   94       /**
   95        * Version constant for Java 1.1 .
   96        *
   97        * @deprecated since 1.5.x.
   98        *             Use {@link JavaEnvUtils#JAVA_1_1} instead.
   99        */
  100       public static final String JAVA_1_1 = JavaEnvUtils.JAVA_1_1;
  101       /**
  102        * Version constant for Java 1.2 .
  103        *
  104        * @deprecated since 1.5.x.
  105        *             Use {@link JavaEnvUtils#JAVA_1_2} instead.
  106        */
  107       public static final String JAVA_1_2 = JavaEnvUtils.JAVA_1_2;
  108       /**
  109        * Version constant for Java 1.3 .
  110        *
  111        * @deprecated since 1.5.x.
  112        *             Use {@link JavaEnvUtils#JAVA_1_3} instead.
  113        */
  114       public static final String JAVA_1_3 = JavaEnvUtils.JAVA_1_3;
  115       /**
  116        * Version constant for Java 1.4 .
  117        *
  118        * @deprecated since 1.5.x.
  119        *             Use {@link JavaEnvUtils#JAVA_1_4} instead.
  120        */
  121       public static final String JAVA_1_4 = JavaEnvUtils.JAVA_1_4;
  122   
  123       /** Default filter start token. */
  124       public static final String TOKEN_START = FilterSet.DEFAULT_TOKEN_START;
  125       /** Default filter end token. */
  126       public static final String TOKEN_END = FilterSet.DEFAULT_TOKEN_END;
  127   
  128       /** Instance of a utility class to use for file operations. */
  129       private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  130   
  131       /** Name of this project. */
  132       private String name;
  133       /** Description for this project (if any). */
  134       private String description;
  135   
  136   
  137       /** Map of references within the project (paths etc) (String to Object). */
  138       private Hashtable references = new AntRefTable();
  139   
  140       /** Map of id references - used for indicating broken build files */
  141       private HashMap idReferences = new HashMap();
  142   
  143       /** the parent project for old id resolution (if inheritreferences is set) */
  144       private Project parentIdProject = null;
  145   
  146       /** Name of the project's default target. */
  147       private String defaultTarget;
  148   
  149       /** Map from target names to targets (String to Target). */
  150       private Hashtable targets = new Hashtable();
  151       /** Set of global filters. */
  152       private FilterSet globalFilterSet = new FilterSet();
  153       {
  154           // Initialize the globalFileSet's project
  155           globalFilterSet.setProject(this);
  156       }
  157   
  158       /**
  159        * Wrapper around globalFilterSet. This collection only ever
  160        * contains one FilterSet, but the wrapper is needed in order to
  161        * make it easier to use the FileUtils interface.
  162        */
  163       private FilterSetCollection globalFilters
  164           = new FilterSetCollection(globalFilterSet);
  165   
  166       /** Project base directory. */
  167       private File baseDir;
  168   
  169       /** lock object used when adding/removing listeners */
  170       private final Object listenersLock = new Object();
  171   
  172       /** List of listeners to notify of build events. */
  173       private volatile BuildListener[] listeners = new BuildListener[0];
  174   
  175       /** for each thread, record whether it is currently executing
  176           messageLogged */
  177       private final ThreadLocal isLoggingMessage = new ThreadLocal() {
  178               protected Object initialValue() {
  179                   return Boolean.FALSE;
  180               }
  181           };
  182   
  183       /**
  184        * The Ant core classloader--may be <code>null</code> if using
  185        * parent classloader.
  186        */
  187       private ClassLoader coreLoader = null;
  188   
  189       /** Records the latest task to be executed on a thread. */
  190       private final Map/*<Thread,Task>*/ threadTasks =
  191           Collections.synchronizedMap(new WeakHashMap());
  192   
  193       /** Records the latest task to be executed on a thread group. */
  194       private final Map/*<ThreadGroup,Task>*/ threadGroupTasks
  195           = Collections.synchronizedMap(new WeakHashMap());
  196   
  197       /**
  198        * Called to handle any input requests.
  199        */
  200       private InputHandler inputHandler = null;
  201   
  202       /**
  203        * The default input stream used to read any input.
  204        */
  205       private InputStream defaultInputStream = null;
  206   
  207       /**
  208        * Keep going flag.
  209        */
  210       private boolean keepGoingMode = false;
  211   
  212       /**
  213        * Set the input handler.
  214        *
  215        * @param handler the InputHandler instance to use for gathering input.
  216        */
  217       public void setInputHandler(InputHandler handler) {
  218           inputHandler = handler;
  219       }
  220   
  221       /**
  222        * Set the default System input stream. Normally this stream is set to
  223        * System.in. This inputStream is used when no task input redirection is
  224        * being performed.
  225        *
  226        * @param defaultInputStream the default input stream to use when input
  227        *        is requested.
  228        * @since Ant 1.6
  229        */
  230       public void setDefaultInputStream(InputStream defaultInputStream) {
  231           this.defaultInputStream = defaultInputStream;
  232       }
  233   
  234       /**
  235        * Get this project's input stream.
  236        *
  237        * @return the InputStream instance in use by this Project instance to
  238        * read input.
  239        */
  240       public InputStream getDefaultInputStream() {
  241           return defaultInputStream;
  242       }
  243   
  244       /**
  245        * Retrieve the current input handler.
  246        *
  247        * @return the InputHandler instance currently in place for the project
  248        *         instance.
  249        */
  250       public InputHandler getInputHandler() {
  251           return inputHandler;
  252       }
  253   
  254       /**
  255        * Create a new Ant project.
  256        */
  257       public Project() {
  258           inputHandler = new DefaultInputHandler();
  259       }
  260   
  261       /**
  262        * Create and initialize a subproject. By default the subproject will be of
  263        * the same type as its parent. If a no-arg constructor is unavailable, the
  264        * <code>Project</code> class will be used.
  265        * @return a Project instance configured as a subproject of this Project.
  266        * @since Ant 1.7
  267        */
  268       public Project createSubProject() {
  269           Project subProject = null;
  270           try {
  271               subProject = (Project) (getClass().newInstance());
  272           } catch (Exception e) {
  273               subProject = new Project();
  274           }
  275           initSubProject(subProject);
  276           return subProject;
  277       }
  278   
  279       /**
  280        * Initialize a subproject.
  281        * @param subProject the subproject to initialize.
  282        */
  283       public void initSubProject(Project subProject) {
  284           ComponentHelper.getComponentHelper(subProject)
  285               .initSubProject(ComponentHelper.getComponentHelper(this));
  286           subProject.setDefaultInputStream(getDefaultInputStream());
  287           subProject.setKeepGoingMode(this.isKeepGoingMode());
  288           subProject.setExecutor(getExecutor().getSubProjectExecutor());
  289       }
  290   
  291       /**
  292        * Initialise the project.
  293        *
  294        * This involves setting the default task definitions and loading the
  295        * system properties.
  296        *
  297        * @exception BuildException if the default task list cannot be loaded.
  298        */
  299       public void init() throws BuildException {
  300           initProperties();
  301   
  302           ComponentHelper.getComponentHelper(this).initDefaultDefinitions();
  303       }
  304   
  305       /**
  306        * Initializes the properties.
  307        * @exception BuildException if an vital property could not be set.
  308        * @since Ant 1.7
  309        */
  310       public void initProperties() throws BuildException {
  311           setJavaVersionProperty();
  312           setSystemProperties();
  313           setPropertyInternal(MagicNames.ANT_VERSION, Main.getAntVersion());
  314           setAntLib();
  315       }
  316   
  317       /**
  318        * Set a property to the location of ant.jar.
  319        * Use the locator to find the location of the Project.class, and
  320        * if this is not null, set the property {@link MagicNames#ANT_LIB}
  321        * to the result
  322        */
  323       private void setAntLib() {
  324           File antlib = org.apache.tools.ant.launch.Locator.getClassSource(
  325               Project.class);
  326           if (antlib != null) {
  327               setPropertyInternal(MagicNames.ANT_LIB, antlib.getAbsolutePath());
  328           }
  329       }
  330       /**
  331        * Factory method to create a class loader for loading classes from
  332        * a given path.
  333        *
  334        * @param path the path from which classes are to be loaded.
  335        *
  336        * @return an appropriate classloader.
  337        */
  338       public AntClassLoader createClassLoader(Path path) {
  339           return AntClassLoader
  340               .newAntClassLoader(getClass().getClassLoader(), this, path, true);
  341       }
  342   
  343       /**
  344        * Factory method to create a class loader for loading classes from
  345        * a given path.
  346        *
  347        * @param parent the parent classloader for the new loader.
  348        * @param path the path from which classes are to be loaded.
  349        *
  350        * @return an appropriate classloader.
  351        */
  352       public AntClassLoader createClassLoader(
  353           ClassLoader parent, Path path) {
  354           return AntClassLoader.newAntClassLoader(parent, this, path, true);
  355       }
  356   
  357       /**
  358        * Set the core classloader for the project. If a <code>null</code>
  359        * classloader is specified, the parent classloader should be used.
  360        *
  361        * @param coreLoader The classloader to use for the project.
  362        *                   May be <code>null</code>.
  363        */
  364       public void setCoreLoader(ClassLoader coreLoader) {
  365           this.coreLoader = coreLoader;
  366       }
  367   
  368       /**
  369        * Return the core classloader to use for this project.
  370        * This may be <code>null</code>, indicating that
  371        * the parent classloader should be used.
  372        *
  373        * @return the core classloader to use for this project.
  374        *
  375        */
  376       public ClassLoader getCoreLoader() {
  377           return coreLoader;
  378       }
  379   
  380       /**
  381        * Add a build listener to the list. This listener will
  382        * be notified of build events for this project.
  383        *
  384        * @param listener The listener to add to the list.
  385        *                 Must not be <code>null</code>.
  386        */
  387       public void addBuildListener(BuildListener listener) {
  388           synchronized (listenersLock) {
  389               // If the listeners already has this listener, do nothing
  390               for (int i = 0; i < listeners.length; i++) {
  391                   if (listeners[i] == listener) {
  392                       return;
  393                   }
  394               }
  395               // copy on write semantics
  396               BuildListener[] newListeners =
  397                   new BuildListener[listeners.length + 1];
  398               System.arraycopy(listeners, 0, newListeners, 0, listeners.length);
  399               newListeners[listeners.length] = listener;
  400               listeners = newListeners;
  401           }
  402       }
  403   
  404       /**
  405        * Remove a build listener from the list. This listener
  406        * will no longer be notified of build events for this project.
  407        *
  408        * @param listener The listener to remove from the list.
  409        *                 Should not be <code>null</code>.
  410        */
  411       public void removeBuildListener(BuildListener listener) {
  412           synchronized (listenersLock) {
  413               // copy on write semantics
  414               for (int i = 0; i < listeners.length; i++) {
  415                   if (listeners[i] == listener) {
  416                       BuildListener[] newListeners =
  417                           new BuildListener[listeners.length - 1];
  418                       System.arraycopy(listeners, 0, newListeners, 0, i);
  419                       System.arraycopy(listeners, i + 1, newListeners, i,
  420                                        listeners.length - i - 1);
  421                       listeners = newListeners;
  422                       break;
  423                   }
  424               }
  425           }
  426       }
  427   
  428       /**
  429        * Return a copy of the list of build listeners for the project.
  430        * 
  431        * @return a list of build listeners for the project
  432        */
  433       public Vector getBuildListeners() {
  434           synchronized (listenersLock) {
  435               Vector r = new Vector(listeners.length);
  436               for (int i = 0; i < listeners.length; i++) {
  437                   r.add(listeners[i]);
  438               }
  439               return r;
  440           }
  441       }
  442   
  443       /**
  444        * Write a message to the log with the default log level
  445        * of MSG_INFO .
  446        * @param message The text to log. Should not be <code>null</code>.
  447        */
  448   
  449       public void log(String message) {
  450           log(message, MSG_INFO);
  451       }
  452   
  453       /**
  454        * Write a project level message to the log with the given log level.
  455        * @param message The text to log. Should not be <code>null</code>.
  456        * @param msgLevel The log priority level to use.
  457        */
  458       public void log(String message, int msgLevel) {
  459           log(message, null, msgLevel);
  460       }
  461   
  462       /**
  463        * Write a project level message to the log with the given log level.
  464        * @param message The text to log. Should not be <code>null</code>.
  465        * @param throwable The exception causing this log, may be <code>null</code>.
  466        * @param msgLevel The log priority level to use.
  467        * @since 1.7
  468        */
  469       public void log(String message, Throwable throwable, int msgLevel) {
  470           fireMessageLogged(this, message, throwable, msgLevel);
  471       }
  472   
  473       /**
  474        * Write a task level message to the log with the given log level.
  475        * @param task The task to use in the log. Must not be <code>null</code>.
  476        * @param message The text to log. Should not be <code>null</code>.
  477        * @param msgLevel The log priority level to use.
  478        */
  479       public void log(Task task, String message, int msgLevel) {
  480           fireMessageLogged(task, message, null, msgLevel);
  481       }
  482   
  483       /**
  484        * Write a task level message to the log with the given log level.
  485        * @param task The task to use in the log. Must not be <code>null</code>.
  486        * @param message The text to log. Should not be <code>null</code>.
  487        * @param throwable The exception causing this log, may be <code>null</code>.
  488        * @param msgLevel The log priority level to use.
  489        * @since 1.7
  490        */
  491       public void log(Task task, String message, Throwable throwable, int msgLevel) {
  492           fireMessageLogged(task, message, throwable, msgLevel);
  493       }
  494   
  495       /**
  496        * Write a target level message to the log with the given log level.
  497        * @param target The target to use in the log.
  498        *               Must not be <code>null</code>.
  499        * @param message The text to log. Should not be <code>null</code>.
  500        * @param msgLevel The log priority level to use.
  501        */
  502       public void log(Target target, String message, int msgLevel) {
  503           log(target, message, null, msgLevel);
  504       }
  505   
  506       /**
  507        * Write a target level message to the log with the given log level.
  508        * @param target The target to use in the log.
  509        *               Must not be <code>null</code>.
  510        * @param message The text to log. Should not be <code>null</code>.
  511        * @param throwable The exception causing this log, may be <code>null</code>.
  512        * @param msgLevel The log priority level to use.
  513        * @since 1.7
  514        */
  515       public void log(Target target, String message, Throwable throwable,
  516               int msgLevel) {
  517           fireMessageLogged(target, message, throwable, msgLevel);
  518       }
  519   
  520       /**
  521        * Return the set of global filters.
  522        *
  523        * @return the set of global filters.
  524        */
  525       public FilterSet getGlobalFilterSet() {
  526           return globalFilterSet;
  527       }
  528   
  529       /**
  530        * Set a property. Any existing property of the same name
  531        * is overwritten, unless it is a user property.
  532        * @param name The name of property to set.
  533        *             Must not be <code>null</code>.
  534        * @param value The new value of the property.
  535        *              Must not be <code>null</code>.
  536        */
  537       public void setProperty(String name, String value) {
  538           PropertyHelper.getPropertyHelper(this).setProperty(name, value, true);
  539       }
  540   
  541       /**
  542        * Set a property if no value currently exists. If the property
  543        * exists already, a message is logged and the method returns with
  544        * no other effect.
  545        *
  546        * @param name The name of property to set.
  547        *             Must not be <code>null</code>.
  548        * @param value The new value of the property.
  549        *              Must not be <code>null</code>.
  550        * @since 1.5
  551        */
  552       public void setNewProperty(String name, String value) {
  553           PropertyHelper.getPropertyHelper(this).setNewProperty(name, value);
  554       }
  555   
  556       /**
  557        * Set a user property, which cannot be overwritten by
  558        * set/unset property calls. Any previous value is overwritten.
  559        * @param name The name of property to set.
  560        *             Must not be <code>null</code>.
  561        * @param value The new value of the property.
  562        *              Must not be <code>null</code>.
  563        * @see #setProperty(String,String)
  564        */
  565       public void setUserProperty(String name, String value) {
  566           PropertyHelper.getPropertyHelper(this).setUserProperty(name, value);
  567       }
  568   
  569       /**
  570        * Set a user property, which cannot be overwritten by set/unset
  571        * property calls. Any previous value is overwritten. Also marks
  572        * these properties as properties that have not come from the
  573        * command line.
  574        *
  575        * @param name The name of property to set.
  576        *             Must not be <code>null</code>.
  577        * @param value The new value of the property.
  578        *              Must not be <code>null</code>.
  579        * @see #setProperty(String,String)
  580        */
  581       public void setInheritedProperty(String name, String value) {
  582           PropertyHelper.getPropertyHelper(this).setInheritedProperty(name, value);
  583       }
  584   
  585       /**
  586        * Set a property unless it is already defined as a user property
  587        * (in which case the method returns silently).
  588        *
  589        * @param name The name of the property.
  590        *             Must not be <code>null</code>.
  591        * @param value The property value. Must not be <code>null</code>.
  592        */
  593       private void setPropertyInternal(String name, String value) {
  594           PropertyHelper.getPropertyHelper(this).setProperty(name, value, false);
  595       }
  596   
  597       /**
  598        * Return the value of a property, if it is set.
  599        *
  600        * @param propertyName The name of the property.
  601        *             May be <code>null</code>, in which case
  602        *             the return value is also <code>null</code>.
  603        * @return the property value, or <code>null</code> for no match
  604        *         or if a <code>null</code> name is provided.
  605        */
  606       public String getProperty(String propertyName) {
  607           Object value = PropertyHelper.getPropertyHelper(this).getProperty(propertyName);
  608           return value == null ? null : String.valueOf(value);
  609       }
  610   
  611       /**
  612        * Replace ${} style constructions in the given value with the
  613        * string value of the corresponding data types.
  614        *
  615        * @param value The string to be scanned for property references.
  616        *              May be <code>null</code>.
  617        *
  618        * @return the given string with embedded property names replaced
  619        *         by values, or <code>null</code> if the given string is
  620        *         <code>null</code>.
  621        *
  622        * @exception BuildException if the given value has an unclosed
  623        *                           property name, e.g. <code>${xxx</code>.
  624        */
  625       public String replaceProperties(String value) throws BuildException {
  626           return PropertyHelper.getPropertyHelper(this).replaceProperties(null, value, null);
  627       }
  628   
  629       /**
  630        * Return the value of a user property, if it is set.
  631        *
  632        * @param propertyName The name of the property.
  633        *             May be <code>null</code>, in which case
  634        *             the return value is also <code>null</code>.
  635        * @return the property value, or <code>null</code> for no match
  636        *         or if a <code>null</code> name is provided.
  637        */
  638        public String getUserProperty(String propertyName) {
  639           return (String) PropertyHelper.getPropertyHelper(this).getUserProperty(propertyName);
  640       }
  641   
  642       /**
  643        * Return a copy of the properties table.
  644        * @return a hashtable containing all properties
  645        *         (including user properties).
  646        */
  647       public Hashtable getProperties() {
  648           return PropertyHelper.getPropertyHelper(this).getProperties();
  649       }
  650   
  651       /**
  652        * Return a copy of the user property hashtable.
  653        * @return a hashtable containing just the user properties.
  654        */
  655       public Hashtable getUserProperties() {
  656           return PropertyHelper.getPropertyHelper(this).getUserProperties();
  657       }
  658   
  659       /**
  660        * Return a copy of the inherited property hashtable.
  661        * @return a hashtable containing just the inherited properties.
  662        * @since Ant 1.8.0
  663        */
  664       public Hashtable getInheritedProperties() {
  665           return PropertyHelper.getPropertyHelper(this).getInheritedProperties();
  666       }
  667   
  668       /**
  669        * Copy all user properties that have been set on the command
  670        * line or a GUI tool from this instance to the Project instance
  671        * given as the argument.
  672        *
  673        * <p>To copy all &quot;user&quot; properties, you will also have to call
  674        * {@link #copyInheritedProperties copyInheritedProperties}.</p>
  675        *
  676        * @param other the project to copy the properties to.  Must not be null.
  677        *
  678        * @since Ant 1.5
  679        */
  680       public void copyUserProperties(Project other) {
  681           PropertyHelper.getPropertyHelper(this).copyUserProperties(other);
  682       }
  683   
  684       /**
  685        * Copy all user properties that have not been set on the
  686        * command line or a GUI tool from this instance to the Project
  687        * instance given as the argument.
  688        *
  689        * <p>To copy all &quot;user&quot; properties, you will also have to call
  690        * {@link #copyUserProperties copyUserProperties}.</p>
  691        *
  692        * @param other the project to copy the properties to.  Must not be null.
  693        *
  694        * @since Ant 1.5
  695        */
  696       public void copyInheritedProperties(Project other) {
  697           PropertyHelper.getPropertyHelper(this).copyInheritedProperties(other);
  698       }
  699   
  700       /**
  701        * Set the default target of the project.
  702        *
  703        * @param defaultTarget The name of the default target for this project.
  704        *                      May be <code>null</code>, indicating that there is
  705        *                      no default target.
  706        *
  707        * @deprecated since 1.5.x.
  708        *             Use setDefault.
  709        * @see #setDefault(String)
  710        */
  711       public void setDefaultTarget(String defaultTarget) {
  712           setDefault(defaultTarget);
  713       }
  714   
  715       /**
  716        * Return the name of the default target of the project.
  717        * @return name of the default target or
  718        *         <code>null</code> if no default has been set.
  719        */
  720       public String getDefaultTarget() {
  721           return defaultTarget;
  722       }
  723   
  724       /**
  725        * Set the default target of the project.
  726        *
  727        * @param defaultTarget The name of the default target for this project.
  728        *                      May be <code>null</code>, indicating that there is
  729        *                      no default target.
  730        */
  731       public void setDefault(String defaultTarget) {
  732           setUserProperty(MagicNames.PROJECT_DEFAULT_TARGET, defaultTarget);
  733           this.defaultTarget = defaultTarget;
  734       }
  735   
  736       /**
  737        * Set the name of the project, also setting the user
  738        * property <code>ant.project.name</code>.
  739        *
  740        * @param name The name of the project.
  741        *             Must not be <code>null</code>.
  742        */
  743       public void setName(String name) {
  744           setUserProperty(MagicNames.PROJECT_NAME,  name);
  745           this.name = name;
  746       }
  747   
  748       /**
  749        * Return the project name, if one has been set.
  750        *
  751        * @return the project name, or <code>null</code> if it hasn't been set.
  752        */
  753       public String getName() {
  754           return name;
  755       }
  756   
  757       /**
  758        * Set the project description.
  759        *
  760        * @param description The description of the project.
  761        *                    May be <code>null</code>.
  762        */
  763       public void setDescription(String description) {
  764           this.description = description;
  765       }
  766   
  767       /**
  768        * Return the project description, if one has been set.
  769        *
  770        * @return the project description, or <code>null</code> if it hasn't
  771        *         been set.
  772        */
  773       public String getDescription() {
  774           if (description == null) {
  775               description = Description.getDescription(this);
  776           }
  777           return description;
  778       }
  779   
  780       /**
  781        * Add a filter to the set of global filters.
  782        *
  783        * @param token The token to filter.
  784        *              Must not be <code>null</code>.
  785        * @param value The replacement value.
  786        *              Must not be <code>null</code>.
  787        * @deprecated since 1.4.x.
  788        *             Use getGlobalFilterSet().addFilter(token,value)
  789        *
  790        * @see #getGlobalFilterSet()
  791        * @see FilterSet#addFilter(String,String)
  792        */
  793       public void addFilter(String token, String value) {
  794           if (token == null) {
  795               return;
  796           }
  797           globalFilterSet.addFilter(new FilterSet.Filter(token, value));
  798       }
  799   
  800       /**
  801        * Return a hashtable of global filters, mapping tokens to values.
  802        *
  803        * @return a hashtable of global filters, mapping tokens to values
  804        *         (String to String).
  805        *
  806        * @deprecated since 1.4.x
  807        *             Use getGlobalFilterSet().getFilterHash().
  808        *
  809        * @see #getGlobalFilterSet()
  810        * @see FilterSet#getFilterHash()
  811        */
  812       public Hashtable getFilters() {
  813           // we need to build the hashtable dynamically
  814           return globalFilterSet.getFilterHash();
  815       }
  816   
  817       /**
  818        * Set the base directory for the project, checking that
  819        * the given filename exists and is a directory.
  820        *
  821        * @param baseD The project base directory.
  822        *              Must not be <code>null</code>.
  823        *
  824        * @exception BuildException if the directory if invalid.
  825        */
  826       public void setBasedir(String baseD) throws BuildException {
  827           setBaseDir(new File(baseD));
  828       }
  829   
  830       /**
  831        * Set the base directory for the project, checking that
  832        * the given file exists and is a directory.
  833        *
  834        * @param baseDir The project base directory.
  835        *                Must not be <code>null</code>.
  836        * @exception BuildException if the specified file doesn't exist or
  837        *                           isn't a directory.
  838        */
  839       public void setBaseDir(File baseDir) throws BuildException {
  840           baseDir = FILE_UTILS.normalize(baseDir.getAbsolutePath());
  841           if (!baseDir.exists()) {
  842               throw new BuildException("Basedir " + baseDir.getAbsolutePath()
  843                   + " does not exist");
  844           }
  845           if (!baseDir.isDirectory()) {
  846               throw new BuildException("Basedir " + baseDir.getAbsolutePath()
  847                   + " is not a directory");
  848           }
  849           this.baseDir = baseDir;
  850           setPropertyInternal(MagicNames.PROJECT_BASEDIR, this.baseDir.getPath());
  851           String msg = "Project base dir set to: " + this.baseDir;
  852           log(msg, MSG_VERBOSE);
  853       }
  854   
  855       /**
  856        * Return the base directory of the project as a file object.
  857        *
  858        * @return the project base directory, or <code>null</code> if the
  859        *         base directory has not been successfully set to a valid value.
  860        */
  861       public File getBaseDir() {
  862           if (baseDir == null) {
  863               try {
  864                   setBasedir(".");
  865               } catch (BuildException ex) {
  866                   ex.printStackTrace();
  867               }
  868           }
  869           return baseDir;
  870       }
  871   
  872       /**
  873        * Set &quot;keep-going&quot; mode. In this mode Ant will try to execute
  874        * as many targets as possible. All targets that do not depend
  875        * on failed target(s) will be executed.  If the keepGoing settor/getter
  876        * methods are used in conjunction with the <code>ant.executor.class</code>
  877        * property, they will have no effect.
  878        * @param keepGoingMode &quot;keep-going&quot; mode
  879        * @since Ant 1.6
  880        */
  881       public void setKeepGoingMode(boolean keepGoingMode) {
  882           this.keepGoingMode = keepGoingMode;
  883       }
  884   
  885       /**
  886        * Return the keep-going mode.  If the keepGoing settor/getter
  887        * methods are used in conjunction with the <code>ant.executor.class</code>
  888        * property, they will have no effect.
  889        * @return &quot;keep-going&quot; mode
  890        * @since Ant 1.6
  891        */
  892       public boolean isKeepGoingMode() {
  893           return this.keepGoingMode;
  894       }
  895   
  896       /**
  897        * Return the version of Java this class is running under.
  898        * @return the version of Java as a String, e.g. "1.1" .
  899        * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
  900        * @deprecated since 1.5.x.
  901        *             Use org.apache.tools.ant.util.JavaEnvUtils instead.
  902        */
  903       public static String getJavaVersion() {
  904           return JavaEnvUtils.getJavaVersion();
  905       }
  906   
  907       /**
  908        * Set the <code>ant.java.version</code> property and tests for
  909        * unsupported JVM versions. If the version is supported,
  910        * verbose log messages are generated to record the Java version
  911        * and operating system name.
  912        *
  913        * @exception BuildException if this Java version is not supported.
  914        *
  915        * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
  916        */
  917       public void setJavaVersionProperty() throws BuildException {
  918           String javaVersion = JavaEnvUtils.getJavaVersion();
  919           setPropertyInternal(MagicNames.ANT_JAVA_VERSION, javaVersion);
  920   
  921           // sanity check
  922           if (!JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_1_4))  {
  923               throw new BuildException("Ant cannot work on Java prior to 1.4");
  924           }
  925           log("Detected Java version: " + javaVersion + " in: "
  926               + System.getProperty("java.home"), MSG_VERBOSE);
  927   
  928           log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
  929       }
  930   
  931       /**
  932        * Add all system properties which aren't already defined as
  933        * user properties to the project properties.
  934        */
  935       public void setSystemProperties() {
  936           Properties systemP = System.getProperties();
  937           Enumeration e = systemP.propertyNames();
  938           while (e.hasMoreElements()) {
  939               String propertyName = (String) e.nextElement();
  940               String value = systemP.getProperty(propertyName);
  941               if (value != null) {
  942                   this.setPropertyInternal(propertyName, value);
  943               }
  944           }
  945       }
  946   
  947       /**
  948        * Add a new task definition to the project.
  949        * Attempting to override an existing definition with an
  950        * equivalent one (i.e. with the same classname) results in
  951        * a verbose log message. Attempting to override an existing definition
  952        * with a different one results in a warning log message and
  953        * invalidates any tasks which have already been created with the
  954        * old definition.
  955        *
  956        * @param taskName The name of the task to add.
  957        *                 Must not be <code>null</code>.
  958        * @param taskClass The full name of the class implementing the task.
  959        *                  Must not be <code>null</code>.
  960        *
  961        * @exception BuildException if the class is unsuitable for being an Ant
  962        *                           task. An error level message is logged before
  963        *                           this exception is thrown.
  964        *
  965        * @see #checkTaskClass(Class)
  966        */
  967       public void addTaskDefinition(String taskName, Class taskClass)
  968            throws BuildException {
  969           ComponentHelper.getComponentHelper(this).addTaskDefinition(taskName,
  970                   taskClass);
  971       }
  972   
  973       /**
  974        * Check whether or not a class is suitable for serving as Ant task.
  975        * Ant task implementation classes must be public, concrete, and have
  976        * a no-arg constructor.
  977        *
  978        * @param taskClass The class to be checked.
  979        *                  Must not be <code>null</code>.
  980        *
  981        * @exception BuildException if the class is unsuitable for being an Ant
  982        *                           task. An error level message is logged before
  983        *                           this exception is thrown.
  984        */
  985       public void checkTaskClass(final Class taskClass) throws BuildException {
  986           ComponentHelper.getComponentHelper(this).checkTaskClass(taskClass);
  987   
  988           if (!Modifier.isPublic(taskClass.getModifiers())) {
  989               final String message = taskClass + " is not public";
  990               log(message, Project.MSG_ERR);
  991               throw new BuildException(message);
  992           }
  993           if (Modifier.isAbstract(taskClass.getModifiers())) {
  994               final String message = taskClass + " is abstract";
  995               log(message, Project.MSG_ERR);
  996               throw new BuildException(message);
  997           }
  998           try {
  999               taskClass.getConstructor((Class[]) null);
 1000               // don't have to check for public, since
 1001               // getConstructor finds public constructors only.
 1002           } catch (NoSuchMethodException e) {
 1003               final String message = "No public no-arg constructor in "
 1004                   + taskClass;
 1005               log(message, Project.MSG_ERR);
 1006               throw new BuildException(message);
 1007           } catch (LinkageError e) {
 1008               String message = "Could not load " + taskClass + ": " + e;
 1009               log(message, Project.MSG_ERR);
 1010               throw new BuildException(message, e);
 1011           }
 1012           if (!Task.class.isAssignableFrom(taskClass)) {
 1013               TaskAdapter.checkTaskClass(taskClass, this);
 1014           }
 1015       }
 1016   
 1017       /**
 1018        * Return the current task definition hashtable. The returned hashtable is
 1019        * &quot;live&quot; and so should not be modified.
 1020        *
 1021        * @return a map of from task name to implementing class
 1022        *         (String to Class).
 1023        */
 1024       public Hashtable getTaskDefinitions() {
 1025           return ComponentHelper.getComponentHelper(this).getTaskDefinitions();
 1026       }
 1027   
 1028       /**
 1029        * Return the current task definition map. The returned map is a
 1030        * copy of the &quot;live&quot; definitions.
 1031        *
 1032        * @return a map of from task name to implementing class
 1033        *         (String to Class).
 1034        *
 1035        * @since Ant 1.8.1
 1036        */
 1037       public Map getCopyOfTaskDefinitions() {
 1038           return new HashMap(getTaskDefinitions());
 1039       }
 1040   
 1041       /**
 1042        * Add a new datatype definition.
 1043        * Attempting to override an existing definition with an
 1044        * equivalent one (i.e. with the same classname) results in
 1045        * a verbose log message. Attempting to override an existing definition
 1046        * with a different one results in a warning log message, but the
 1047        * definition is changed.
 1048        *
 1049        * @param typeName The name of the datatype.
 1050        *                 Must not be <code>null</code>.
 1051        * @param typeClass The full name of the class implementing the datatype.
 1052        *                  Must not be <code>null</code>.
 1053        */
 1054       public void addDataTypeDefinition(String typeName, Class typeClass) {
 1055           ComponentHelper.getComponentHelper(this).addDataTypeDefinition(typeName,
 1056                   typeClass);
 1057       }
 1058   
 1059       /**
 1060        * Return the current datatype definition hashtable. The returned
 1061        * hashtable is &quot;live&quot; and so should not be modified.
 1062        *
 1063        * @return a map of from datatype name to implementing class
 1064        *         (String to Class).
 1065        */
 1066       public Hashtable getDataTypeDefinitions() {
 1067           return ComponentHelper.getComponentHelper(this).getDataTypeDefinitions();
 1068       }
 1069   
 1070       /**
 1071        * Return the current datatype definition map. The returned
 1072        * map is a copy pf the &quot;live&quot; definitions.
 1073        *
 1074        * @return a map of from datatype name to implementing class
 1075        *         (String to Class).
 1076        *
 1077        * @since Ant 1.8.1
 1078        */
 1079       public Map getCopyOfDataTypeDefinitions() {
 1080           return new HashMap(getDataTypeDefinitions());
 1081       }
 1082   
 1083       /**
 1084        * Add a <em>new</em> target to the project.
 1085        *
 1086        * @param target The target to be added to the project.
 1087        *               Must not be <code>null</code>.
 1088        *
 1089        * @exception BuildException if the target already exists in the project
 1090        *
 1091        * @see Project#addOrReplaceTarget(Target)
 1092        */
 1093       public void addTarget(Target target) throws BuildException {
 1094           addTarget(target.getName(), target);
 1095       }
 1096   
 1097       /**
 1098        * Add a <em>new</em> target to the project.
 1099        *
 1100        * @param targetName The name to use for the target.
 1101        *             Must not be <code>null</code>.
 1102        * @param target The target to be added to the project.
 1103        *               Must not be <code>null</code>.
 1104        *
 1105        * @exception BuildException if the target already exists in the project.
 1106        *
 1107        * @see Project#addOrReplaceTarget(String, Target)
 1108        */
 1109        public void addTarget(String targetName, Target target)
 1110            throws BuildException {
 1111            if (targets.get(targetName) != null) {
 1112                throw new BuildException("Duplicate target: `" + targetName + "'");
 1113            }
 1114            addOrReplaceTarget(targetName, target);
 1115        }
 1116   
 1117       /**
 1118        * Add a target to the project, or replaces one with the same
 1119        * name.
 1120        *
 1121        * @param target The target to be added or replaced in the project.
 1122        *               Must not be <code>null</code>.
 1123        */
 1124       public void addOrReplaceTarget(Target target) {
 1125           addOrReplaceTarget(target.getName(), target);
 1126       }
 1127   
 1128       /**
 1129        * Add a target to the project, or replaces one with the same
 1130        * name.
 1131        *
 1132        * @param targetName The name to use for the target.
 1133        *                   Must not be <code>null</code>.
 1134        * @param target The target to be added or replaced in the project.
 1135        *               Must not be <code>null</code>.
 1136        */
 1137       public void addOrReplaceTarget(String targetName, Target target) {
 1138           String msg = " +Target: " + targetName;
 1139           log(msg, MSG_DEBUG);
 1140           target.setProject(this);
 1141           targets.put(targetName, target);
 1142       }
 1143   
 1144       /**
 1145        * Return the hashtable of targets. The returned hashtable
 1146        * is &quot;live&quot; and so should not be modified.
 1147        * @return a map from name to target (String to Target).
 1148        */
 1149       public Hashtable getTargets() {
 1150           return targets;
 1151       }
 1152   
 1153       /**
 1154        * Return the map of targets. The returned map
 1155        * is a copy of the &quot;live&quot; targets.
 1156        * @return a map from name to target (String to Target).
 1157        * @since Ant 1.8.1
 1158        */
 1159       public Map getCopyOfTargets() {
 1160           return new HashMap(targets);
 1161       }
 1162   
 1163       /**
 1164        * Create a new instance of a task, adding it to a list of
 1165        * created tasks for later invalidation. This causes all tasks
 1166        * to be remembered until the containing project is removed
 1167        * @param taskType The name of the task to create an instance of.
 1168        *                 Must not be <code>null</code>.
 1169        *
 1170        * @return an instance of the specified task, or <code>null</code> if
 1171        *         the task name is not recognised.
 1172        *
 1173        * @exception BuildException if the task name is recognised but task
 1174        *                           creation fails.
 1175        */
 1176       public Task createTask(String taskType) throws BuildException {
 1177           return ComponentHelper.getComponentHelper(this).createTask(taskType);
 1178       }
 1179   
 1180       /**
 1181        * Create a new instance of a data type.
 1182        *
 1183        * @param typeName The name of the data type to create an instance of.
 1184        *                 Must not be <code>null</code>.
 1185        *
 1186        * @return an instance of the specified data type, or <code>null</code> if
 1187        *         the data type name is not recognised.
 1188        *
 1189        * @exception BuildException if the data type name is recognised but
 1190        *                           instance creation fails.
 1191        */
 1192       public Object createDataType(String typeName) throws BuildException {
 1193           return ComponentHelper.getComponentHelper(this).createDataType(typeName);
 1194       }
 1195   
 1196       /**
 1197        * Set the Executor instance for this Project.
 1198        * @param e the Executor to use.
 1199        */
 1200       public void setExecutor(Executor e) {
 1201           addReference(MagicNames.ANT_EXECUTOR_REFERENCE, e);
 1202       }
 1203   
 1204       /**
 1205        * Get this Project's Executor (setting it if necessary).
 1206        * @return an Executor instance.
 1207        */
 1208       public Executor getExecutor() {
 1209           Object o = getReference(MagicNames.ANT_EXECUTOR_REFERENCE);
 1210           if (o == null) {
 1211               String classname = getProperty(MagicNames.ANT_EXECUTOR_CLASSNAME);
 1212               if (classname == null) {
 1213                   classname = DefaultExecutor.class.getName();
 1214               }
 1215               log("Attempting to create object of type " + classname, MSG_DEBUG);
 1216               try {
 1217                   o = Class.forName(classname, true, coreLoader).newInstance();
 1218               } catch (ClassNotFoundException seaEnEfEx) {
 1219                   //try the current classloader
 1220                   try {
 1221                       o = Class.forName(classname).newInstance();
 1222                   } catch (Exception ex) {
 1223                       log(ex.toString(), MSG_ERR);
 1224                   }
 1225               } catch (Exception ex) {
 1226                   log(ex.toString(), MSG_ERR);
 1227               }
 1228               if (o == null) {
 1229                   throw new BuildException(
 1230                       "Unable to obtain a Target Executor instance.");
 1231               }
 1232               setExecutor((Executor) o);
 1233           }
 1234           return (Executor) o;
 1235       }
 1236   
 1237       /**
 1238        * Execute the specified sequence of targets, and the targets
 1239        * they depend on.
 1240        *
 1241        * @param names A vector of target name strings to execute.
 1242        *              Must not be <code>null</code>.
 1243        *
 1244        * @exception BuildException if the build failed.
 1245        */
 1246       public void executeTargets(Vector names) throws BuildException {
 1247           setUserProperty(MagicNames.PROJECT_INVOKED_TARGETS,
 1248                           CollectionUtils.flattenToString(names));
 1249           getExecutor().executeTargets(this,
 1250               (String[]) (names.toArray(new String[names.size()])));
 1251       }
 1252   
 1253       /**
 1254        * Demultiplex output so that each task receives the appropriate
 1255        * messages. If the current thread is not currently executing a task,
 1256        * the message is logged directly.
 1257        *
 1258        * @param output Message to handle. Should not be <code>null</code>.
 1259        * @param isWarning Whether the text represents an warning (<code>true</code>)
 1260        *        or information (<code>false</code>).
 1261        */
 1262       public void demuxOutput(String output, boolean isWarning) {
 1263           Task task = getThreadTask(Thread.currentThread());
 1264           if (task == null) {
 1265               log(output, isWarning ? MSG_WARN : MSG_INFO);
 1266           } else {
 1267               if (isWarning) {
 1268                   task.handleErrorOutput(output);
 1269               } else {
 1270                   task.handleOutput(output);
 1271               }
 1272           }
 1273       }
 1274   
 1275       /**
 1276        * Read data from the default input stream. If no default has been
 1277        * specified, System.in is used.
 1278        *
 1279        * @param buffer the buffer into which data is to be read.
 1280        * @param offset the offset into the buffer at which data is stored.
 1281        * @param length the amount of data to read.
 1282        *
 1283        * @return the number of bytes read.
 1284        *
 1285        * @exception IOException if the data cannot be read.
 1286        * @since Ant 1.6
 1287        */
 1288       public int defaultInput(byte[] buffer, int offset, int length)
 1289           throws IOException {
 1290           if (defaultInputStream != null) {
 1291               System.out.flush();
 1292               return defaultInputStream.read(buffer, offset, length);
 1293           } else {
 1294               throw new EOFException("No input provided for project");
 1295           }
 1296       }
 1297   
 1298       /**
 1299        * Demux an input request to the correct task.
 1300        *
 1301        * @param buffer the buffer into which data is to be read.
 1302        * @param offset the offset into the buffer at which data is stored.
 1303        * @param length the amount of data to read.
 1304        *
 1305        * @return the number of bytes read.
 1306        *
 1307        * @exception IOException if the data cannot be read.
 1308        * @since Ant 1.6
 1309        */
 1310       public int demuxInput(byte[] buffer, int offset, int length)
 1311           throws IOException {
 1312           Task task = getThreadTask(Thread.currentThread());
 1313           if (task == null) {
 1314               return defaultInput(buffer, offset, length);
 1315           } else {
 1316               return task.handleInput(buffer, offset, length);
 1317           }
 1318       }
 1319   
 1320       /**
 1321        * Demultiplex flush operations so that each task receives the appropriate
 1322        * messages. If the current thread is not currently executing a task,
 1323        * the message is logged directly.
 1324        *
 1325        * @since Ant 1.5.2
 1326        *
 1327        * @param output Message to handle. Should not be <code>null</code>.
 1328        * @param isError Whether the text represents an error (<code>true</code>)
 1329        *        or information (<code>false</code>).
 1330        */
 1331       public void demuxFlush(String output, boolean isError) {
 1332           Task task = getThreadTask(Thread.currentThread());
 1333           if (task == null) {
 1334               fireMessageLogged(this, output, isError ? MSG_ERR : MSG_INFO);
 1335           } else {
 1336               if (isError) {
 1337                   task.handleErrorFlush(output);
 1338               } else {
 1339                   task.handleFlush(output);
 1340               }
 1341           }
 1342       }
 1343   
 1344       /**
 1345        * Execute the specified target and any targets it depends on.
 1346        *
 1347        * @param targetName The name of the target to execute.
 1348        *                   Must not be <code>null</code>.
 1349        *
 1350        * @exception BuildException if the build failed.
 1351        */
 1352       public void executeTarget(String targetName) throws BuildException {
 1353   
 1354           // sanity check ourselves, if we've been asked to build nothing
 1355           // then we should complain
 1356   
 1357           if (targetName == null) {
 1358               String msg = "No target specified";
 1359               throw new BuildException(msg);
 1360           }
 1361   
 1362           // Sort and run the dependency tree.
 1363           // Sorting checks if all the targets (and dependencies)
 1364           // exist, and if there is any cycle in the dependency
 1365           // graph.
 1366           executeSortedTargets(topoSort(targetName, targets, false));
 1367       }
 1368   
 1369       /**
 1370        * Execute a <code>Vector</code> of sorted targets.
 1371        * @param sortedTargets   the aforementioned <code>Vector</code>.
 1372        * @throws BuildException on error.
 1373        */
 1374       public void executeSortedTargets(Vector sortedTargets)
 1375           throws BuildException {
 1376           Set succeededTargets = new HashSet();
 1377           BuildException buildException = null; // first build exception
 1378           for (Enumeration iter = sortedTargets.elements();
 1379                iter.hasMoreElements();) {
 1380               Target curtarget = (Target) iter.nextElement();
 1381               boolean canExecute = true;
 1382               for (Enumeration depIter = curtarget.getDependencies();
 1383                    depIter.hasMoreElements();) {
 1384                   String dependencyName = ((String) depIter.nextElement());
 1385                   if (!succeededTargets.contains(dependencyName)) {
 1386                       canExecute = false;
 1387                       log(curtarget,
 1388                           "Cannot execute '" + curtarget.getName() + "' - '"
 1389                           + dependencyName + "' failed or was not executed.",
 1390                           MSG_ERR);
 1391                       break;
 1392                   }
 1393               }
 1394               if (canExecute) {
 1395                   Throwable thrownException = null;
 1396                   try {
 1397                       curtarget.performTasks();
 1398                       succeededTargets.add(curtarget.getName());
 1399                   } catch (RuntimeException ex) {
 1400                       if (!(keepGoingMode)) {
 1401                           throw ex; // throw further
 1402                       }
 1403                       thrownException = ex;
 1404                   } catch (Throwable ex) {
 1405                       if (!(keepGoingMode)) {
 1406                           throw new BuildException(ex);
 1407                       }
 1408                       thrownException = ex;
 1409                   }
 1410                   if (thrownException != null) {
 1411                       if (thrownException instanceof BuildException) {
 1412                           log(curtarget,
 1413                               "Target '" + curtarget.getName()
 1414                               + "' failed with message '"
 1415                               + thrownException.getMessage() + "'.", MSG_ERR);
 1416                           // only the first build exception is reported
 1417                           if (buildException == null) {
 1418                               buildException = (BuildException) thrownException;
 1419                           }
 1420                       } else {
 1421                           log(curtarget,
 1422                               "Target '" + curtarget.getName()
 1423                               + "' failed with message '"
 1424                               + thrownException.getMessage() + "'.", MSG_ERR);
 1425                           thrownException.printStackTrace(System.err);
 1426                           if (buildException == null) {
 1427                               buildException =
 1428                                   new BuildException(thrownException);
 1429                           }
 1430                       }
 1431                   }
 1432               }
 1433           }
 1434           if (buildException != null) {
 1435               throw buildException;
 1436           }
 1437       }
 1438   
 1439       /**
 1440        * Return the canonical form of a filename.
 1441        * <p>
 1442        * If the specified file name is relative it is resolved
 1443        * with respect to the given root directory.
 1444        *
 1445        * @param fileName The name of the file to resolve.
 1446        *                 Must not be <code>null</code>.
 1447        *
 1448        * @param rootDir  The directory respective to which relative file names
 1449        *                 are resolved. May be <code>null</code>, in which case
 1450        *                 the current directory is used.
 1451        *
 1452        * @return the resolved File.
 1453        *
 1454        * @deprecated since 1.4.x
 1455        */
 1456       public File resolveFile(String fileName, File rootDir) {
 1457           return FILE_UTILS.resolveFile(rootDir, fileName);
 1458       }
 1459   
 1460       /**
 1461        * Return the canonical form of a filename.
 1462        * <p>
 1463        * If the specified file name is relative it is resolved
 1464        * with respect to the project's base directory.
 1465        *
 1466        * @param fileName The name of the file to resolve.
 1467        *                 Must not be <code>null</code>.
 1468        *
 1469        * @return the resolved File.
 1470        *
 1471        */
 1472       public File resolveFile(String fileName) {
 1473           return FILE_UTILS.resolveFile(baseDir, fileName);
 1474       }
 1475   
 1476       /**
 1477        * Translate a path into its native (platform specific) format.
 1478        * <p>
 1479        * This method uses PathTokenizer to separate the input path
 1480        * into its components. This handles DOS style paths in a relatively
 1481        * sensible way. The file separators are then converted to their platform
 1482        * specific versions.
 1483        *
 1484        * @param toProcess The path to be translated.
 1485        *                  May be <code>null</code>.
 1486        *
 1487        * @return the native version of the specified path or
 1488        *         an empty string if the path is <code>null</code> or empty.
 1489        *
 1490        * @deprecated since 1.7
 1491        *             Use FileUtils.translatePath instead.
 1492        *
 1493        * @see PathTokenizer
 1494        */
 1495       public static String translatePath(String toProcess) {
 1496           return FileUtils.translatePath(toProcess);
 1497       }
 1498   
 1499       /**
 1500        * Convenience method to copy a file from a source to a destination.
 1501        * No filtering is performed.
 1502        *
 1503        * @param sourceFile Name of file to copy from.
 1504        *                   Must not be <code>null</code>.
 1505        * @param destFile Name of file to copy to.
 1506        *                 Must not be <code>null</code>.
 1507        *
 1508        * @exception IOException if the copying fails.
 1509        *
 1510        * @deprecated since 1.4.x
 1511        */
 1512       public void copyFile(String sourceFile, String destFile)
 1513             throws IOException {
 1514           FILE_UTILS.copyFile(sourceFile, destFile);
 1515       }
 1516   
 1517       /**
 1518        * Convenience method to copy a file from a source to a destination
 1519        * specifying if token filtering should be used.
 1520        *
 1521        * @param sourceFile Name of file to copy from.
 1522        *                   Must not be <code>null</code>.
 1523        * @param destFile Name of file to copy to.
 1524        *                 Must not be <code>null</code>.
 1525        * @param filtering Whether or not token filtering should be used during
 1526        *                  the copy.
 1527        *
 1528        * @exception IOException if the copying fails.
 1529        *
 1530        * @deprecated since 1.4.x
 1531        */
 1532       public void copyFile(String sourceFile, String destFile, boolean filtering)
 1533           throws IOException {
 1534           FILE_UTILS.copyFile(sourceFile, destFile,
 1535               filtering ? globalFilters : null);
 1536       }
 1537   
 1538       /**
 1539        * Convenience method to copy a file from a source to a
 1540        * destination specifying if token filtering should be used and if
 1541        * source files may overwrite newer destination files.
 1542        *
 1543        * @param sourceFile Name of file to copy from.
 1544        *                   Must not be <code>null</code>.
 1545        * @param destFile Name of file to copy to.
 1546        *                 Must not be <code>null</code>.
 1547        * @param filtering Whether or not token filtering should be used during
 1548        *                  the copy.
 1549        * @param overwrite Whether or not the destination file should be
 1550        *                  overwritten if it already exists.
 1551        *
 1552        * @exception IOException if the copying fails.
 1553        *
 1554        * @deprecated since 1.4.x
 1555        */
 1556       public void copyFile(String sourceFile, String destFile, boolean filtering,
 1557                            boolean overwrite) throws IOException {
 1558           FILE_UTILS.copyFile(sourceFile, destFile,
 1559               filtering ? globalFilters : null, overwrite);
 1560       }
 1561   
 1562       /**
 1563        * Convenience method to copy a file from a source to a
 1564        * destination specifying if token filtering should be used, if
 1565        * source files may overwrite newer destination files, and if the
 1566        * last modified time of the resulting file should be set to
 1567        * that of the source file.
 1568        *
 1569        * @param sourceFile Name of file to copy from.
 1570        *                   Must not be <code>null</code>.
 1571        * @param destFile Name of file to copy to.
 1572        *                 Must not be <code>null</code>.
 1573        * @param filtering Whether or not token filtering should be used during
 1574        *                  the copy.
 1575        * @param overwrite Whether or not the destination file should be
 1576        *                  overwritten if it already exists.
 1577        * @param preserveLastModified Whether or not the last modified time of
 1578        *                             the resulting file should be set to that
 1579        *                             of the source file.
 1580        *
 1581        * @exception IOException if the copying fails.
 1582        *
 1583        * @deprecated since 1.4.x
 1584        */
 1585       public void copyFile(String sourceFile, String destFile, boolean filtering,
 1586                            boolean overwrite, boolean preserveLastModified)
 1587           throws IOException {
 1588           FILE_UTILS.copyFile(sourceFile, destFile,
 1589               filtering ? globalFilters : null, overwrite, preserveLastModified);
 1590       }
 1591   
 1592       /**
 1593        * Convenience method to copy a file from a source to a destination.
 1594        * No filtering is performed.
 1595        *
 1596        * @param sourceFile File to copy from.
 1597        *                   Must not be <code>null</code>.
 1598        * @param destFile File to copy to.
 1599        *                 Must not be <code>null</code>.
 1600        *
 1601        * @exception IOException if the copying fails.
 1602        *
 1603        * @deprecated since 1.4.x
 1604        */
 1605       public void copyFile(File sourceFile, File destFile) throws IOException {
 1606           FILE_UTILS.copyFile(sourceFile, destFile);
 1607       }
 1608   
 1609       /**
 1610        * Convenience method to copy a file from a source to a destination
 1611        * specifying if token filtering should be used.
 1612        *
 1613        * @param sourceFile File to copy from.
 1614        *                   Must not be <code>null</code>.
 1615        * @param destFile File to copy to.
 1616        *                 Must not be <code>null</code>.
 1617        * @param filtering Whether or not token filtering should be used during
 1618        *                  the copy.
 1619        *
 1620        * @exception IOException if the copying fails.
 1621        *
 1622        * @deprecated since 1.4.x
 1623        */
 1624       public void copyFile(File sourceFile, File destFile, boolean filtering)
 1625           throws IOException {
 1626           FILE_UTILS.copyFile(sourceFile, destFile,
 1627               filtering ? globalFilters : null);
 1628       }
 1629   
 1630       /**
 1631        * Convenience method to copy a file from a source to a
 1632        * destination specifying if token filtering should be used and if
 1633        * source files may overwrite newer destination files.
 1634        *
 1635        * @param sourceFile File to copy from.
 1636        *                   Must not be <code>null</code>.
 1637        * @param destFile File to copy to.
 1638        *                 Must not be <code>null</code>.
 1639        * @param filtering Whether or not token filtering should be used during
 1640        *                  the copy.
 1641        * @param overwrite Whether or not the destination file should be
 1642        *                  overwritten if it already exists.
 1643        *
 1644        * @exception IOException if the file cannot be copied.
 1645        *
 1646        * @deprecated since 1.4.x
 1647        */
 1648       public void copyFile(File sourceFile, File destFile, boolean filtering,
 1649                            boolean overwrite) throws IOException {
 1650           FILE_UTILS.copyFile(sourceFile, destFile,
 1651               filtering ? globalFilters : null, overwrite);
 1652       }
 1653   
 1654       /**
 1655        * Convenience method to copy a file from a source to a
 1656        * destination specifying if token filtering should be used, if
 1657        * source files may overwrite newer destination files, and if the
 1658        * last modified time of the resulting file should be set to
 1659        * that of the source file.
 1660        *
 1661        * @param sourceFile File to copy from.
 1662        *                   Must not be <code>null</code>.
 1663        * @param destFile File to copy to.
 1664        *                 Must not be <code>null</code>.
 1665        * @param filtering Whether or not token filtering should be used during
 1666        *                  the copy.
 1667        * @param overwrite Whether or not the destination file should be
 1668        *                  overwritten if it already exists.
 1669        * @param preserveLastModified Whether or not the last modified time of
 1670        *                             the resulting file should be set to that
 1671        *                             of the source file.
 1672        *
 1673        * @exception IOException if the file cannot be copied.
 1674        *
 1675        * @deprecated since 1.4.x
 1676        */
 1677       public void copyFile(File sourceFile, File destFile, boolean filtering,
 1678                            boolean overwrite, boolean preserveLastModified)
 1679           throws IOException {
 1680           FILE_UTILS.copyFile(sourceFile, destFile,
 1681               filtering ? globalFilters : null, overwrite, preserveLastModified);
 1682       }
 1683   
 1684       /**
 1685        * Call File.setLastModified(long time) on Java above 1.1, and logs
 1686        * a warning on Java 1.1.
 1687        *
 1688        * @param file The file to set the last modified time on.
 1689        *             Must not be <code>null</code>.
 1690        *
 1691        * @param time the required modification time.
 1692        *
 1693        * @deprecated since 1.4.x
 1694        *
 1695        * @exception BuildException if the last modified time cannot be set
 1696        *                           despite running on a platform with a version
 1697        *                           above 1.1.
 1698        */
 1699       public void setFileLastModified(File file, long time)
 1700            throws BuildException {
 1701           FILE_UTILS.setFileLastModified(file, time);
 1702           log("Setting modification time for " + file, MSG_VERBOSE);
 1703       }
 1704   
 1705       /**
 1706        * Return the boolean equivalent of a string, which is considered
 1707        * <code>true</code> if either <code>"on"</code>, <code>"true"</code>,
 1708        * or <code>"yes"</code> is found, ignoring case.
 1709        *
 1710        * @param s The string to convert to a boolean value.
 1711        *
 1712        * @return <code>true</code> if the given string is <code>"on"</code>,
 1713        *         <code>"true"</code> or <code>"yes"</code>, or
 1714        *         <code>false</code> otherwise.
 1715        */
 1716       public static boolean toBoolean(String s) {
 1717           return ("on".equalsIgnoreCase(s)
 1718                   || "true".equalsIgnoreCase(s)
 1719                   || "yes".equalsIgnoreCase(s));
 1720       }
 1721   
 1722       /**
 1723        * Get the Project instance associated with the specified object.
 1724        * @param o the object to query.
 1725        * @return Project instance, if any.
 1726        * @since Ant 1.7.1
 1727        */
 1728       public static Project getProject(Object o) {
 1729           if (o instanceof ProjectComponent) {
 1730               return ((ProjectComponent) o).getProject();
 1731           }
 1732           try {
 1733               Method m = o.getClass().getMethod("getProject", (Class[]) null);
 1734               if (Project.class == m.getReturnType()) {
 1735                   return (Project) m.invoke(o, (Object[]) null);
 1736               }
 1737           } catch (Exception e) {
 1738               //too bad
 1739           }
 1740           return null;
 1741       }
 1742   
 1743       /**
 1744        * Topologically sort a set of targets.  Equivalent to calling
 1745        * <code>topoSort(new String[] {root}, targets, true)</code>.
 1746        *
 1747        * @param root The name of the root target. The sort is created in such
 1748        *             a way that the sequence of Targets up to the root
 1749        *             target is the minimum possible such sequence.
 1750        *             Must not be <code>null</code>.
 1751        * @param targetTable A Hashtable mapping names to Targets.
 1752        *                Must not be <code>null</code>.
 1753        * @return a Vector of ALL Target objects in sorted order.
 1754        * @exception BuildException if there is a cyclic dependency among the
 1755        *                           targets, or if a named target does not exist.
 1756        */
 1757       public final Vector topoSort(String root, Hashtable targetTable)
 1758           throws BuildException {
 1759           return topoSort(new String[] {root}, targetTable, true);
 1760       }
 1761   
 1762       /**
 1763        * Topologically sort a set of targets.  Equivalent to calling
 1764        * <code>topoSort(new String[] {root}, targets, returnAll)</code>.
 1765        *
 1766        * @param root The name of the root target. The sort is created in such
 1767        *             a way that the sequence of Targets up to the root
 1768        *             target is the minimum possible such sequence.
 1769        *             Must not be <code>null</code>.
 1770        * @param targetTable A Hashtable mapping names to Targets.
 1771        *                Must not be <code>null</code>.
 1772        * @param returnAll <code>boolean</code> indicating whether to return all
 1773        *                  targets, or the execution sequence only.
 1774        * @return a Vector of Target objects in sorted order.
 1775        * @exception BuildException if there is a cyclic dependency among the
 1776        *                           targets, or if a named target does not exist.
 1777        * @since Ant 1.6.3
 1778        */
 1779       public final Vector topoSort(String root, Hashtable targetTable,
 1780                                    boolean returnAll) throws BuildException {
 1781           return topoSort(new String[] {root}, targetTable, returnAll);
 1782       }
 1783   
 1784       /**
 1785        * Topologically sort a set of targets.
 1786        *
 1787        * @param root <code>String[]</code> containing the names of the root targets.
 1788        *             The sort is created in such a way that the ordered sequence of
 1789        *             Targets is the minimum possible such sequence to the specified
 1790        *             root targets.
 1791        *             Must not be <code>null</code>.
 1792        * @param targetTable A map of names to targets (String to Target).
 1793        *                Must not be <code>null</code>.
 1794        * @param returnAll <code>boolean</code> indicating whether to return all
 1795        *                  targets, or the execution sequence only.
 1796        * @return a Vector of Target objects in sorted order.
 1797        * @exception BuildException if there is a cyclic dependency among the
 1798        *                           targets, or if a named target does not exist.
 1799        * @since Ant 1.6.3
 1800        */
 1801       public final Vector topoSort(String[] root, Hashtable targetTable,
 1802                                    boolean returnAll) throws BuildException {
 1803           Vector ret = new VectorSet();
 1804           Hashtable state = new Hashtable();
 1805           Stack visiting = new Stack();
 1806   
 1807           // We first run a DFS based sort using each root as a starting node.
 1808           // This creates the minimum sequence of Targets to the root node(s).
 1809           // We then do a sort on any remaining unVISITED targets.
 1810           // This is unnecessary for doing our build, but it catches
 1811           // circular dependencies or missing Targets on the entire
 1812           // dependency tree, not just on the Targets that depend on the
 1813           // build Target.
 1814   
 1815           for (int i = 0; i < root.length; i++) {
 1816               String st = (String) (state.get(root[i]));
 1817               if (st == null) {
 1818                   tsort(root[i], targetTable, state, visiting, ret);
 1819               } else if (st == VISITING) {
 1820                   throw new RuntimeException("Unexpected node in visiting state: "
 1821                       + root[i]);
 1822               }
 1823           }
 1824           StringBuffer buf = new StringBuffer("Build sequence for target(s)");
 1825   
 1826           for (int j = 0; j < root.length; j++) {
 1827               buf.append((j == 0) ? " `" : ", `").append(root[j]).append('\'');
 1828           }
 1829           buf.append(" is " + ret);
 1830           log(buf.toString(), MSG_VERBOSE);
 1831   
 1832           Vector complete = (returnAll) ? ret : new Vector(ret);
 1833           for (Enumeration en = targetTable.keys(); en.hasMoreElements();) {
 1834               String curTarget = (String) en.nextElement();
 1835               String st = (String) state.get(curTarget);
 1836               if (st == null) {
 1837                   tsort(curTarget, targetTable, state, visiting, complete);
 1838               } else if (st == VISITING) {
 1839                   throw new RuntimeException("Unexpected node in visiting state: "
 1840                       + curTarget);
 1841               }
 1842           }
 1843           log("Complete build sequence is " + complete, MSG_VERBOSE);
 1844           return ret;
 1845       }
 1846   
 1847       /**
 1848        * Perform a single step in a recursive depth-first-search traversal of
 1849        * the target dependency tree.
 1850        * <p>
 1851        * The current target is first set to the &quot;visiting&quot; state, and
 1852        * pushed onto the &quot;visiting&quot; stack.
 1853        * <p>
 1854        * An exception is then thrown if any child of the current node is in the
 1855        * visiting state, as that implies a circular dependency. The exception
 1856        * contains details of the cycle, using elements of the &quot;visiting&quot;
 1857        * stack.
 1858        * <p>
 1859        * If any child has not already been &quot;visited&quot;, this method is
 1860        * called recursively on it.
 1861        * <p>
 1862        * The current target is then added to the ordered list of targets. Note
 1863        * that this is performed after the children have been visited in order
 1864        * to get the correct order. The current target is set to the
 1865        * &quot;visited&quot; state.
 1866        * <p>
 1867        * By the time this method returns, the ordered list contains the sequence
 1868        * of targets up to and including the current target.
 1869        *
 1870        * @param root The current target to inspect.
 1871        *             Must not be <code>null</code>.
 1872        * @param targetTable A mapping from names to targets (String to Target).
 1873        *                Must not be <code>null</code>.
 1874        * @param state   A mapping from target names to states (String to String).
 1875        *                The states in question are &quot;VISITING&quot; and
 1876        *                &quot;VISITED&quot;. Must not be <code>null</code>.
 1877        * @param visiting A stack of targets which are currently being visited.
 1878        *                 Must not be <code>null</code>.
 1879        * @param ret     The list to add target names to. This will end up
 1880        *                containing the complete list of dependencies in
 1881        *                dependency order.
 1882        *                Must not be <code>null</code>.
 1883        *
 1884        * @exception BuildException if a non-existent target is specified or if
 1885        *                           a circular dependency is detected.
 1886        */
 1887       private void tsort(String root, Hashtable targetTable,
 1888                                Hashtable state, Stack visiting,
 1889                                Vector ret)
 1890           throws BuildException {
 1891           state.put(root, VISITING);
 1892           visiting.push(root);
 1893   
 1894           Target target = (Target) targetTable.get(root);
 1895   
 1896           // Make sure we exist
 1897           if (target == null) {
 1898               StringBuffer sb = new StringBuffer("Target \"");
 1899               sb.append(root);
 1900               sb.append("\" does not exist in the project \"");
 1901               sb.append(name);
 1902               sb.append("\". ");
 1903               visiting.pop();
 1904               if (!visiting.empty()) {
 1905                   String parent = (String) visiting.peek();
 1906                   sb.append("It is used from target \"");
 1907                   sb.append(parent);
 1908                   sb.append("\".");
 1909               }
 1910               throw new BuildException(new String(sb));
 1911           }
 1912           for (Enumeration en = target.getDependencies(); en.hasMoreElements();) {
 1913               String cur = (String) en.nextElement();
 1914               String m = (String) state.get(cur);
 1915               if (m == null) {
 1916                   // Not been visited
 1917                   tsort(cur, targetTable, state, visiting, ret);
 1918               } else if (m == VISITING) {
 1919                   // Currently visiting this node, so have a cycle
 1920                   throw makeCircularException(cur, visiting);
 1921               }
 1922           }
 1923           String p = (String) visiting.pop();
 1924           if (root != p) {
 1925               throw new RuntimeException("Unexpected internal error: expected to "
 1926                   + "pop " + root + " but got " + p);
 1927           }
 1928           state.put(root, VISITED);
 1929           ret.addElement(target);
 1930       }
 1931   
 1932       /**
 1933        * Build an appropriate exception detailing a specified circular
 1934        * dependency.
 1935        *
 1936        * @param end The dependency to stop at. Must not be <code>null</code>.
 1937        * @param stk A stack of dependencies. Must not be <code>null</code>.
 1938        *
 1939        * @return a BuildException detailing the specified circular dependency.
 1940        */
 1941       private static BuildException makeCircularException(String end, Stack stk) {
 1942           StringBuffer sb = new StringBuffer("Circular dependency: ");
 1943           sb.append(end);
 1944           String c;
 1945           do {
 1946               c = (String) stk.pop();
 1947               sb.append(" <- ");
 1948               sb.append(c);
 1949           } while (!c.equals(end));
 1950           return new BuildException(new String(sb));
 1951       }
 1952   
 1953       /**
 1954        * Inherit the id references.
 1955        * @param parent the parent project of this project.
 1956        */
 1957       public void inheritIDReferences(Project parent) {
 1958           parentIdProject = parent;
 1959       }
 1960   
 1961       /**
 1962        * Add an id reference.
 1963        * Used for broken build files.
 1964        * @param id the id to set.
 1965        * @param value the value to set it to (Unknown element in this case.
 1966        */
 1967       public void addIdReference(String id, Object value) {
 1968           idReferences.put(id, value);
 1969       }
 1970   
 1971       /**
 1972        * Add a reference to the project.
 1973        *
 1974        * @param referenceName The name of the reference. Must not be <code>null</code>.
 1975        * @param value The value of the reference.
 1976        */
 1977       public void addReference(String referenceName, Object value) {
 1978           Object old = ((AntRefTable) references).getReal(referenceName);
 1979           if (old == value) {
 1980               // no warning, this is not changing anything
 1981               return;
 1982           }
 1983           if (old != null && !(old instanceof UnknownElement)) {
 1984               log("Overriding previous definition of reference to " + referenceName,
 1985                   MSG_VERBOSE);
 1986           }
 1987           log("Adding reference: " + referenceName, MSG_DEBUG);
 1988           references.put(referenceName, value);
 1989       }
 1990   
 1991       /**
 1992        * Return a map of the references in the project (String to Object).
 1993        * The returned hashtable is &quot;live&quot; and so must not be modified.
 1994        *
 1995        * @return a map of the references in the project (String to Object).
 1996        */
 1997       public Hashtable getReferences() {
 1998           return references;
 1999       }
 2000   
 2001       /**
 2002        * Does the project know this reference?
 2003        *
 2004        * @since Ant 1.8.0
 2005        */
 2006       public boolean hasReference(String key) {
 2007           return references.containsKey(key);
 2008       }
 2009   
 2010       /**
 2011        * Return a map of the references in the project (String to
 2012        * Object).  The returned hashtable is a copy of the
 2013        * &quot;live&quot; references.
 2014        *
 2015        * @return a map of the references in the project (String to Object).
 2016        *
 2017        * @since Ant 1.8.1
 2018        */
 2019       public Map getCopyOfReferences() {
 2020           return new HashMap(references);
 2021       }
 2022   
 2023       /**
 2024        * Look up a reference by its key (ID).
 2025        *
 2026        * @param key The key for the desired reference.
 2027        *            Must not be <code>null</code>.
 2028        *
 2029        * @return the reference with the specified ID, or <code>null</code> if
 2030        *         there is no such reference in the project.
 2031        */
 2032       public Object getReference(String key) {
 2033           Object ret = references.get(key);
 2034           if (ret != null) {
 2035               return ret;
 2036           }
 2037           if (!key.equals(MagicNames.REFID_PROPERTY_HELPER)) {
 2038               try {
 2039                   if (PropertyHelper.getPropertyHelper(this).containsProperties(key)) {
 2040                       log("Unresolvable reference " + key
 2041                               + " might be a misuse of property expansion syntax.", MSG_WARN);
 2042                   }
 2043               } catch (Exception e) {
 2044                   //ignore
 2045               }
 2046           }
 2047           return ret;
 2048       }
 2049   
 2050       /**
 2051        * Return a description of the type of the given element, with
 2052        * special handling for instances of tasks and data types.
 2053        * <p>
 2054        * This is useful for logging purposes.
 2055        *
 2056        * @param element The element to describe.
 2057        *                Must not be <code>null</code>.
 2058        *
 2059        * @return a description of the element type.
 2060        *
 2061        * @since 1.95, Ant 1.5
 2062        */
 2063       public String getElementName(Object element) {
 2064           return ComponentHelper.getComponentHelper(this).getElementName(element);
 2065       }
 2066   
 2067       /**
 2068        * Send a &quot;build started&quot; event
 2069        * to the build listeners for this project.
 2070        */
 2071       public void fireBuildStarted() {
 2072           BuildEvent event = new BuildEvent(this);
 2073           BuildListener[] currListeners = listeners;
 2074           for (int i = 0; i < currListeners.length; i++) {
 2075               currListeners[i].buildStarted(event);
 2076           }
 2077       }
 2078   
 2079       /**
 2080        * Send a &quot;build finished&quot; event to the build listeners
 2081        * for this project.
 2082        * @param exception an exception indicating a reason for a build
 2083        *                  failure. May be <code>null</code>, indicating
 2084        *                  a successful build.
 2085        */
 2086       public void fireBuildFinished(Throwable exception) {
 2087           BuildEvent event = new BuildEvent(this);
 2088           event.setException(exception);
 2089           BuildListener[] currListeners = listeners;
 2090           for (int i = 0; i < currListeners.length; i++) {
 2091               currListeners[i].buildFinished(event);
 2092           }
 2093           // Inform IH to clear the cache
 2094           IntrospectionHelper.clearCache();
 2095       }
 2096   
 2097       /**
 2098        * Send a &quot;subbuild started&quot; event to the build listeners for
 2099        * this project.
 2100        *
 2101        * @since Ant 1.6.2
 2102        */
 2103       public void fireSubBuildStarted() {
 2104           BuildEvent event = new BuildEvent(this);
 2105           BuildListener[] currListeners = listeners;
 2106           for (int i = 0; i < currListeners.length; i++) {
 2107               if (currListeners[i] instanceof SubBuildListener) {
 2108                   ((SubBuildListener) currListeners[i]).subBuildStarted(event);
 2109               }
 2110           }
 2111       }
 2112   
 2113       /**
 2114        * Send a &quot;subbuild finished&quot; event to the build listeners for
 2115        * this project.
 2116        * @param exception an exception indicating a reason for a build
 2117        *                  failure. May be <code>null</code>, indicating
 2118        *                  a successful build.
 2119        *
 2120        * @since Ant 1.6.2
 2121        */
 2122       public void fireSubBuildFinished(Throwable exception) {
 2123           BuildEvent event = new BuildEvent(this);
 2124           event.setException(exception);
 2125           BuildListener[] currListeners = listeners;
 2126           for (int i = 0; i < currListeners.length; i++) {
 2127               if (currListeners[i] instanceof SubBuildListener) {
 2128                   ((SubBuildListener) currListeners[i]).subBuildFinished(event);
 2129               }
 2130           }
 2131       }
 2132   
 2133       /**
 2134        * Send a &quot;target started&quot; event to the build listeners
 2135        * for this project.
 2136        *
 2137        * @param target The target which is starting to build.
 2138        *               Must not be <code>null</code>.
 2139        */
 2140       protected void fireTargetStarted(Target target) {
 2141           BuildEvent event = new BuildEvent(target);
 2142           BuildListener[] currListeners = listeners;
 2143           for (int i = 0; i < currListeners.length; i++) {
 2144               currListeners[i].targetStarted(event);
 2145           }
 2146   
 2147       }
 2148   
 2149       /**
 2150        * Send a &quot;target finished&quot; event to the build listeners
 2151        * for this project.
 2152        *
 2153        * @param target    The target which has finished building.
 2154        *                  Must not be <code>null</code>.
 2155        * @param exception an exception indicating a reason for a build
 2156        *                  failure. May be <code>null</code>, indicating
 2157        *                  a successful build.
 2158        */
 2159       protected void fireTargetFinished(Target target, Throwable exception) {
 2160           BuildEvent event = new BuildEvent(target);
 2161           event.setException(exception);
 2162           BuildListener[] currListeners = listeners;
 2163           for (int i = 0; i < currListeners.length; i++) {
 2164               currListeners[i].targetFinished(event);
 2165           }
 2166   
 2167       }
 2168   
 2169       /**
 2170        * Send a &quot;task started&quot; event to the build listeners
 2171        * for this project.
 2172        *
 2173        * @param task The target which is starting to execute.
 2174        *               Must not be <code>null</code>.
 2175        */
 2176       protected void fireTaskStarted(Task task) {
 2177           // register this as the current task on the current thread.
 2178           registerThreadTask(Thread.currentThread(), task);
 2179           BuildEvent event = new BuildEvent(task);
 2180           BuildListener[] currListeners = listeners;
 2181           for (int i = 0; i < currListeners.length; i++) {
 2182               currListeners[i].taskStarted(event);
 2183           }
 2184       }
 2185   
 2186       /**
 2187        * Send a &quot;task finished&quot; event to the build listeners for this
 2188        * project.
 2189        *
 2190        * @param task      The task which has finished executing.
 2191        *                  Must not be <code>null</code>.
 2192        * @param exception an exception indicating a reason for a build
 2193        *                  failure. May be <code>null</code>, indicating
 2194        *                  a successful build.
 2195        */
 2196       protected void fireTaskFinished(Task task, Throwable exception) {
 2197           registerThreadTask(Thread.currentThread(), null);
 2198           System.out.flush();
 2199           System.err.flush();
 2200           BuildEvent event = new BuildEvent(task);
 2201           event.setException(exception);
 2202           BuildListener[] currListeners = listeners;
 2203           for (int i = 0; i < currListeners.length; i++) {
 2204               currListeners[i].taskFinished(event);
 2205           }
 2206   
 2207       }
 2208   
 2209       /**
 2210        * Send a &quot;message logged&quot; event to the build listeners
 2211        * for this project.
 2212        *
 2213        * @param event    The event to send. This should be built up with the
 2214        *                 appropriate task/target/project by the caller, so that
 2215        *                 this method can set the message and priority, then send
 2216        *                 the event. Must not be <code>null</code>.
 2217        * @param message  The message to send. Should not be <code>null</code>.
 2218        * @param priority The priority of the message.
 2219        */
 2220       private void fireMessageLoggedEvent(BuildEvent event, String message,
 2221                                           int priority) {
 2222   
 2223           if (message == null) {
 2224               message = String.valueOf(message);
 2225           }
 2226           if (message.endsWith(StringUtils.LINE_SEP)) {
 2227               int endIndex = message.length() - StringUtils.LINE_SEP.length();
 2228               event.setMessage(message.substring(0, endIndex), priority);
 2229           } else {
 2230               event.setMessage(message, priority);
 2231           }
 2232           if (isLoggingMessage.get() != Boolean.FALSE) {
 2233               /*
 2234                * One of the Listeners has attempted to access
 2235                * System.err or System.out.
 2236                *
 2237                * We used to throw an exception in this case, but
 2238                * sometimes Listeners can't prevent it(like our own
 2239                * Log4jListener which invokes getLogger() which in
 2240                * turn wants to write to the console).
 2241                *
 2242                * @see http://marc.theaimsgroup.com/?t=110538624200006&r=1&w=2
 2243                *
 2244                * We now (Ant 1.6.3 and later) simply swallow the message.
 2245                */
 2246               return;
 2247           }
 2248           try {
 2249               isLoggingMessage.set(Boolean.TRUE);
 2250               BuildListener[] currListeners = listeners;
 2251               for (int i = 0; i < currListeners.length; i++) {
 2252                   currListeners[i].messageLogged(event);
 2253               }
 2254           } finally {
 2255               isLoggingMessage.set(Boolean.FALSE);
 2256           }
 2257       }
 2258   
 2259       /**
 2260        * Send a &quot;message logged&quot; project level event
 2261        * to the build listeners for this project.
 2262        *
 2263        * @param project  The project generating the event.
 2264        *                 Should not be <code>null</code>.
 2265        * @param message  The message to send. Should not be <code>null</code>.
 2266        * @param priority The priority of the message.
 2267        */
 2268       protected void fireMessageLogged(Project project, String message,
 2269                                        int priority) {
 2270           fireMessageLogged(project, message, null, priority);
 2271       }
 2272   
 2273       /**
 2274        * Send a &quot;message logged&quot; project level event
 2275        * to the build listeners for this project.
 2276        *
 2277        * @param project  The project generating the event.
 2278        *                 Should not be <code>null</code>.
 2279        * @param message  The message to send. Should not be <code>null</code>.
 2280        * @param throwable The exception that caused this message. May be <code>null</code>.
 2281        * @param priority The priority of the message.
 2282        * @since 1.7
 2283        */
 2284       protected void fireMessageLogged(Project project, String message,
 2285               Throwable throwable, int priority) {
 2286           BuildEvent event = new BuildEvent(project);
 2287           event.setException(throwable);
 2288           fireMessageLoggedEvent(event, message, priority);
 2289       }
 2290   
 2291       /**
 2292        * Send a &quot;message logged&quot; target level event
 2293        * to the build listeners for this project.
 2294        *
 2295        * @param target   The target generating the event.
 2296        *                 Must not be <code>null</code>.
 2297        * @param message  The message to send. Should not be <code>null</code>.
 2298        * @param priority The priority of the message.
 2299        */
 2300       protected void fireMessageLogged(Target target, String message,
 2301                                        int priority) {
 2302           fireMessageLogged(target, message, null, priority);
 2303       }
 2304   
 2305       /**
 2306        * Send a &quot;message logged&quot; target level event
 2307        * to the build listeners for this project.
 2308        *
 2309        * @param target   The target generating the event.
 2310        *                 Must not be <code>null</code>.
 2311        * @param message  The message to send. Should not be <code>null</code>.
 2312        * @param throwable The exception that caused this message. May be <code>null</code>.
 2313        * @param priority The priority of the message.
 2314        * @since 1.7
 2315        */
 2316       protected void fireMessageLogged(Target target, String message,
 2317               Throwable throwable, int priority) {
 2318           BuildEvent event = new BuildEvent(target);
 2319           event.setException(throwable);
 2320           fireMessageLoggedEvent(event, message, priority);
 2321       }
 2322   
 2323       /**
 2324        * Send a &quot;message logged&quot; task level event
 2325        * to the build listeners for this project.
 2326        *
 2327        * @param task     The task generating the event.
 2328        *                 Must not be <code>null</code>.
 2329        * @param message  The message to send. Should not be <code>null</code>.
 2330        * @param priority The priority of the message.
 2331        */
 2332       protected void fireMessageLogged(Task task, String message, int priority) {
 2333           fireMessageLogged(task, message, null, priority);
 2334       }
 2335   
 2336       /**
 2337        * Send a &quot;message logged&quot; task level event
 2338        * to the build listeners for this project.
 2339        *
 2340        * @param task     The task generating the event.
 2341        *                 Must not be <code>null</code>.
 2342        * @param message  The message to send. Should not be <code>null</code>.
 2343        * @param throwable The exception that caused this message. May be <code>null</code>.
 2344        * @param priority The priority of the message.
 2345        * @since 1.7
 2346        */
 2347       protected void fireMessageLogged(Task task, String message,
 2348               Throwable throwable, int priority) {
 2349           BuildEvent event = new BuildEvent(task);
 2350           event.setException(throwable);
 2351           fireMessageLoggedEvent(event, message, priority);
 2352       }
 2353   
 2354       /**
 2355        * Register a task as the current task for a thread.
 2356        * If the task is null, the thread's entry is removed.
 2357        *
 2358        * @param thread the thread on which the task is registered.
 2359        * @param task the task to be registered.
 2360        * @since Ant 1.5
 2361        */
 2362       public void registerThreadTask(Thread thread, Task task) {
 2363           synchronized(threadTasks) {
 2364               if (task != null) {
 2365                   threadTasks.put(thread, task);
 2366                   threadGroupTasks.put(thread.getThreadGroup(), task);
 2367               } else {
 2368                   threadTasks.remove(thread);
 2369                   threadGroupTasks.remove(thread.getThreadGroup());
 2370               }
 2371           }
 2372       }
 2373   
 2374       /**
 2375        * Get the current task associated with a thread, if any.
 2376        *
 2377        * @param thread the thread for which the task is required.
 2378        * @return the task which is currently registered for the given thread or
 2379        *         null if no task is registered.
 2380        */
 2381       public Task getThreadTask(Thread thread) {
 2382           synchronized(threadTasks) {
 2383               Task task = (Task) threadTasks.get(thread);
 2384               if (task == null) {
 2385                   ThreadGroup group = thread.getThreadGroup();
 2386                   while (task == null && group != null) {
 2387                       task = (Task) threadGroupTasks.get(group);
 2388                       group = group.getParent();
 2389                   }
 2390               }
 2391               return task;
 2392           }
 2393       }
 2394   
 2395   
 2396       // Should move to a separate public class - and have API to add
 2397       // listeners, etc.
 2398       private static class AntRefTable extends Hashtable {
 2399   
 2400           AntRefTable() {
 2401               super();
 2402           }
 2403   
 2404           /** Returns the unmodified original object.
 2405            * This method should be called internally to
 2406            * get the &quot;real&quot; object.
 2407            * The normal get method will do the replacement
 2408            * of UnknownElement (this is similar with the JDNI
 2409            * refs behavior).
 2410            */
 2411           private Object getReal(Object key) {
 2412               return super.get(key);
 2413           }
 2414   
 2415           /** Get method for the reference table.
 2416            *  It can be used to hook dynamic references and to modify
 2417            * some references on the fly--for example for delayed
 2418            * evaluation.
 2419            *
 2420            * It is important to make sure that the processing that is
 2421            * done inside is not calling get indirectly.
 2422            *
 2423            * @param key lookup key.
 2424            * @return mapped value.
 2425            */
 2426           public Object get(Object key) {
 2427               //System.out.println("AntRefTable.get " + key);
 2428               Object o = getReal(key);
 2429               if (o instanceof UnknownElement) {
 2430                   // Make sure that
 2431                   UnknownElement ue = (UnknownElement) o;
 2432                   ue.maybeConfigure();
 2433                   o = ue.getRealThing();
 2434               }
 2435               return o;
 2436           }
 2437       }
 2438   
 2439       /**
 2440        * Set a reference to this Project on the parameterized object.
 2441        * Need to set the project before other set/add elements
 2442        * are called.
 2443        * @param obj the object to invoke setProject(this) on.
 2444        */
 2445       public final void setProjectReference(final Object obj) {
 2446           if (obj instanceof ProjectComponent) {
 2447               ((ProjectComponent) obj).setProject(this);
 2448               return;
 2449           }
 2450           try {
 2451               Method method =
 2452                   obj.getClass().getMethod(
 2453                       "setProject", new Class[] {Project.class});
 2454               if (method != null) {
 2455                   method.invoke(obj, new Object[] {this});
 2456               }
 2457           } catch (Throwable e) {
 2458               // ignore this if the object does not have
 2459               // a set project method or the method
 2460               // is private/protected.
 2461           }
 2462       }
 2463   
 2464       /**
 2465        * Resolve the file relative to the project's basedir and return it as a
 2466        * FileResource.
 2467        * @param name the name of the file to resolve.
 2468        * @return the file resource.
 2469        * @since Ant 1.7
 2470        */
 2471       public Resource getResource(String name) {
 2472           return new FileResource(getBaseDir(), name);
 2473       }
 2474   }

Save This Page
Home » apache-ant-1.8.1 » org.apache.tools » ant » [javadoc | source]