Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 2,294   Methods: 107
NCLOC: 950   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
Project.java 67.4% 77.8% 80.4% 75.8%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant;
 56   
 
 57   
 import java.io.File;
 58   
 import java.io.IOException;
 59   
 import java.io.EOFException;
 60   
 import java.io.InputStream;
 61   
 import java.lang.reflect.Modifier;
 62   
 import java.util.Enumeration;
 63   
 import java.util.Hashtable;
 64   
 import java.util.Properties;
 65   
 import java.util.Stack;
 66   
 import java.util.Vector;
 67   
 import org.apache.tools.ant.input.DefaultInputHandler;
 68   
 import org.apache.tools.ant.input.InputHandler;
 69   
 import org.apache.tools.ant.types.FilterSet;
 70   
 import org.apache.tools.ant.types.FilterSetCollection;
 71   
 import org.apache.tools.ant.types.Description;
 72   
 import org.apache.tools.ant.types.Path;
 73   
 import org.apache.tools.ant.util.FileUtils;
 74   
 import org.apache.tools.ant.util.JavaEnvUtils;
 75   
 import org.apache.tools.ant.util.WeakishReference;
 76   
 import org.apache.tools.ant.util.LazyHashtable;
 77   
 
 78   
 /**
 79   
  * Central representation of an Ant project. This class defines an
 80   
  * Ant project with all of its targets, tasks and various other
 81   
  * properties. It also provides the mechanism to kick off a build using
 82   
  * a particular target name.
 83   
  * <p>
 84   
  * This class also encapsulates methods which allow files to be referred
 85   
  * to using abstract path names which are translated to native system
 86   
  * file paths at runtime.
 87   
  *
 88   
  * @author duncan@x180.com
 89   
  *
 90   
  * @version $Revision: 1.133 $
 91   
  */
 92   
 
 93   
 public class Project {
 94   
     /** Message priority of "error". */
 95   
     public static final int MSG_ERR = 0;
 96   
     /** Message priority of "warning". */
 97   
     public static final int MSG_WARN = 1;
 98   
     /** Message priority of "information". */
 99   
     public static final int MSG_INFO = 2;
 100   
     /** Message priority of "verbose". */
 101   
     public static final int MSG_VERBOSE = 3;
 102   
     /** Message priority of "debug". */
 103   
     public static final int MSG_DEBUG = 4;
 104   
 
 105   
     /**
 106   
      * Constant for the "visiting" state, used when
 107   
      * traversing a DFS of target dependencies.
 108   
      */
 109   
     private static final String VISITING = "VISITING";
 110   
     /**
 111   
      * Constant for the "visited" state, used when
 112   
      * traversing a DFS of target dependencies.
 113   
      */
 114   
     private static final String VISITED = "VISITED";
 115   
 
 116   
     /**
 117   
      * The class name of the Ant class loader to use for 
 118   
      * JDK 1.2 and above
 119   
      */
 120   
     private static final String ANTCLASSLOADER_JDK12
 121   
         = "org.apache.tools.ant.loader.AntClassLoader2";
 122   
     
 123   
     /**
 124   
      * Version constant for Java 1.0
 125   
      *
 126   
      * @deprecated use org.apache.tools.ant.util.JavaEnvUtils instead
 127   
      */
 128   
     public static final String JAVA_1_0 = JavaEnvUtils.JAVA_1_0;
 129   
     /**
 130   
      * Version constant for Java 1.1
 131   
      *
 132   
      * @deprecated use org.apache.tools.ant.util.JavaEnvUtils instead
 133   
      */
 134   
     public static final String JAVA_1_1 = JavaEnvUtils.JAVA_1_1;
 135   
     /**
 136   
      * Version constant for Java 1.2
 137   
      *
 138   
      * @deprecated use org.apache.tools.ant.util.JavaEnvUtils instead
 139   
      */
 140   
     public static final String JAVA_1_2 = JavaEnvUtils.JAVA_1_2;
 141   
     /**
 142   
      * Version constant for Java 1.3
 143   
      *
 144   
      * @deprecated use org.apache.tools.ant.util.JavaEnvUtils instead
 145   
      */
 146   
     public static final String JAVA_1_3 = JavaEnvUtils.JAVA_1_3;
 147   
     /**
 148   
      * Version constant for Java 1.4
 149   
      *
 150   
      * @deprecated use org.apache.tools.ant.util.JavaEnvUtils instead
 151   
      */
 152   
     public static final String JAVA_1_4 = JavaEnvUtils.JAVA_1_4;
 153   
 
 154   
     /** Default filter start token. */
 155   
     public static final String TOKEN_START = FilterSet.DEFAULT_TOKEN_START;
 156   
     /** Default filter end token. */
 157   
     public static final String TOKEN_END = FilterSet.DEFAULT_TOKEN_END;
 158   
 
 159   
     /** Name of this project. */
 160   
     private String name;
 161   
     /** Description for this project (if any). */
 162   
     private String description;
 163   
 
 164   
 
 165   
     /** Map of references within the project (paths etc) (String to Object). */
 166   
     private Hashtable references = new AntRefTable(this);
 167   
 
 168   
     /** Name of the project's default target. */
 169   
     private String defaultTarget;
 170   
 
 171   
     /** Map from data type names to implementing classes (String to Class). */
 172   
     private Hashtable dataClassDefinitions = new AntTaskTable(this, false);
 173   
     /** Map from task names to implementing classes (String to Class). */
 174   
     private Hashtable taskClassDefinitions = new AntTaskTable(this, true);
 175   
     /**
 176   
      * Map from task names to vectors of created tasks
 177   
      * (String to Vector of Task). This is used to invalidate tasks if
 178   
      * the task definition changes.
 179   
      */
 180   
     private Hashtable createdTasks = new Hashtable();
 181   
 
 182   
     /** Map from target names to targets (String to Target). */
 183   
     private Hashtable targets = new Hashtable();
 184   
     /** Set of global filters. */
 185   
     private FilterSet globalFilterSet = new FilterSet();
 186   
     /**
 187   
      * Wrapper around globalFilterSet. This collection only ever
 188   
      * contains one FilterSet, but the wrapper is needed in order to
 189   
      * make it easier to use the FileUtils interface.
 190   
      */
 191   
     private FilterSetCollection globalFilters
 192   
         = new FilterSetCollection(globalFilterSet);
 193   
 
 194   
     /** Project base directory. */
 195   
     private File baseDir;
 196   
 
 197   
     /** List of listeners to notify of build events. */
 198   
     private Vector listeners = new Vector();
 199   
 
 200   
     /**
 201   
      * The Ant core classloader - may be <code>null</code> if using
 202   
      * parent classloader.
 203   
      */
 204   
     private ClassLoader coreLoader = null;
 205   
 
 206   
     /** Records the latest task to be executed on a thread (Thread to Task). */
 207   
     private Hashtable threadTasks = new Hashtable();
 208   
 
 209   
     /** Records the latest task to be executed on a thread Group. */
 210   
     private Hashtable threadGroupTasks = new Hashtable();
 211   
 
 212   
     /**
 213   
      * Called to handle any input requests.
 214   
      */
 215   
     private InputHandler inputHandler = null;
 216   
 
 217   
     /**
 218   
      * The default input stream used to read any input
 219   
      */
 220   
     private InputStream defaultInputStream = null;
 221   
     
 222   
     /**
 223   
      * Sets the input handler
 224   
      * 
 225   
      * @param handler the InputHandler instance to use for gathering input.
 226   
      */
 227  58
     public void setInputHandler(InputHandler handler) {
 228  58
         inputHandler = handler;
 229   
     }
 230   
 
 231   
     /**
 232   
      * Set the default System input stream. Normally this stream is set to 
 233   
      * System.in. This inputStream is used when no task inptu redirection is
 234   
      * being performed.
 235   
      *
 236   
      * @param defaultInputStream the default input stream to use when input 
 237   
      *        is reuested.
 238   
      * @since Ant 1.6
 239   
      */
 240  53
     public void setDefaultInputStream(InputStream defaultInputStream) {
 241  53
         this.defaultInputStream = defaultInputStream;
 242   
     }
 243   
 
 244   
     /**
 245   
      * Get this project's input stream
 246   
      *
 247   
      * @return the InputStream instance in use by this Porject instance to 
 248   
      * read input
 249   
      */
 250  52
     public InputStream getDefaultInputStream() {
 251  52
         return defaultInputStream;
 252   
     }
 253   
     
 254   
     /**
 255   
      * Retrieves the current input handler.
 256   
      *
 257   
      * @return the InputHandler instance currently in place for the project 
 258   
      *         instance.
 259   
      */
 260  1815
     public InputHandler getInputHandler() {
 261  1815
         return inputHandler;
 262   
     }
 263   
 
 264   
     /** Instance of a utility class to use for file operations. */
 265   
     private FileUtils fileUtils;
 266   
 
 267   
     /** 
 268   
      * Flag which catches Listeners which try to use System.out or System.err 
 269   
      */
 270   
     private boolean loggingMessage = false;
 271   
     
 272   
     /**
 273   
      * Creates a new Ant project.
 274   
      */
 275  684
     public Project() {
 276  684
         fileUtils = FileUtils.newFileUtils();
 277  684
         inputHandler = new DefaultInputHandler();
 278   
     }
 279   
 
 280   
     /**
 281   
      * Initialises the project.
 282   
      *
 283   
      * This involves setting the default task definitions and loading the
 284   
      * system properties.
 285   
      *
 286   
      * @exception BuildException if the default task list cannot be loaded
 287   
      */
 288  551
     public void init() throws BuildException {
 289  551
         setJavaVersionProperty();
 290   
 
 291  551
         String defs = "/org/apache/tools/ant/taskdefs/defaults.properties";
 292   
 
 293  551
         try {
 294  551
             Properties props = new Properties();
 295  551
             InputStream in = this.getClass().getResourceAsStream(defs);
 296  551
             if (in == null) {
 297  0
                 throw new BuildException("Can't load default task list");
 298   
             }
 299  551
             props.load(in);
 300  551
             in.close();
 301  551
             ((AntTaskTable) taskClassDefinitions).addDefinitions(props);
 302   
 
 303   
 
 304   
         } catch (IOException ioe) {
 305  0
             throw new BuildException("Can't load default task list");
 306   
         }
 307   
 
 308  551
         String dataDefs = "/org/apache/tools/ant/types/defaults.properties";
 309   
 
 310  551
         try {
 311  551
             Properties props = new Properties();
 312  551
             InputStream in = this.getClass().getResourceAsStream(dataDefs);
 313  551
             if (in == null) {
 314  0
                 throw new BuildException("Can't load default datatype list");
 315   
             }
 316  551
             props.load(in);
 317  551
             in.close();
 318   
 
 319  551
             ((AntTaskTable) dataClassDefinitions).addDefinitions(props);
 320   
 
 321   
 
 322   
         } catch (IOException ioe) {
 323  0
             throw new BuildException("Can't load default datatype list");
 324   
         }
 325   
 
 326  551
         setSystemProperties();
 327   
     }
 328   
 
 329   
     /**
 330   
      * Factory method to create a class loader for loading classes
 331   
      *
 332   
      * @return an appropriate classloader
 333   
      */
 334  254
     private AntClassLoader createClassLoader() {
 335  254
         AntClassLoader loader = null;
 336  254
         if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
 337  254
             try {
 338   
                 // 1.2+ - create advanced helper dynamically
 339  254
                 Class loaderClass
 340   
                     = Class.forName(ANTCLASSLOADER_JDK12);
 341  254
                 loader = (AntClassLoader) loaderClass.newInstance();
 342   
             } catch (Exception e) {
 343  0
                     log("Unable to create Class Loader: "
 344   
                         + e.getMessage(), Project.MSG_DEBUG);
 345   
             }
 346   
         }
 347   
 
 348  254
         if (loader == null) {
 349  0
             loader = new AntClassLoader();
 350   
         }
 351   
 
 352  254
         loader.setProject(this);
 353  254
         return loader;
 354   
     }
 355   
 
 356   
     /**
 357   
      * Factory method to create a class loader for loading classes from
 358   
      * a given path
 359   
      *
 360   
      * @param path the path from whcih clases are to be loaded.
 361   
      * 
 362   
      * @return an appropriate classloader
 363   
      */
 364  254
     public AntClassLoader createClassLoader(Path path) {
 365  254
         AntClassLoader loader = createClassLoader();
 366  254
         loader.setClassPath(path);
 367  254
         return loader;
 368   
     }
 369   
 
 370   
     /**
 371   
      * Sets the core classloader for the project. If a <code>null</code>
 372   
      * classloader is specified, the parent classloader should be used.
 373   
      *
 374   
      * @param coreLoader The classloader to use for the project.
 375   
      *                   May be <code>null</code>.
 376   
      */
 377  1
     public void setCoreLoader(ClassLoader coreLoader) {
 378  1
         this.coreLoader = coreLoader;
 379   
     }
 380   
 
 381   
     /**
 382   
      * Returns the core classloader to use for this project.
 383   
      * This may be <code>null</code>, indicating that
 384   
      * the parent classloader should be used.
 385   
      *
 386   
      * @return the core classloader to use for this project.
 387   
      *
 388   
      */
 389  42392
     public ClassLoader getCoreLoader() {
 390  42392
         return coreLoader;
 391   
     }
 392   
 
 393   
     /**
 394   
      * Adds a build listener to the list. This listener will
 395   
      * be notified of build events for this project.
 396   
      *
 397   
      * @param listener The listener to add to the list.
 398   
      *                 Must not be <code>null</code>.
 399   
      */
 400  862
     public void addBuildListener(BuildListener listener) {
 401  862
         listeners.addElement(listener);
 402   
     }
 403   
 
 404   
     /**
 405   
      * Removes 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  189
     public void removeBuildListener(BuildListener listener) {
 412  189
         listeners.removeElement(listener);
 413   
     }
 414   
 
 415   
     /**
 416   
      * Returns a list of build listeners for the project.
 417   
      *
 418   
      * @return a list of build listeners for the project
 419   
      */
 420  113656
     public Vector getBuildListeners() {
 421  113656
         return (Vector) listeners.clone();
 422   
     }
 423   
 
 424   
     /**
 425   
      * Writes a message to the log with the default log level
 426   
      * of MSG_INFO
 427   
      * @param message The text to log. Should not be <code>null</code>.
 428   
      */
 429   
 
 430  0
     public void log(String message) {
 431  0
         log(message, MSG_INFO);
 432   
     }
 433   
 
 434   
     /**
 435   
      * Writes a project level message to the log with the given log level.
 436   
      * @param message The text to log. Should not be <code>null</code>.
 437   
      * @param msgLevel The priority level to log at.
 438   
      */
 439  95979
     public void log(String message, int msgLevel) {
 440  95979
         fireMessageLogged(this, message, msgLevel);
 441   
     }
 442   
 
 443   
     /**
 444   
      * Writes a task level message to the log with the given log level.
 445   
      * @param task The task to use in the log. Must not be <code>null</code>.
 446   
      * @param message The text to log. Should not be <code>null</code>.
 447   
      * @param msgLevel The priority level to log at.
 448   
      */
 449  8992
     public void log(Task task, String message, int msgLevel) {
 450  8992
         fireMessageLogged(task, message, msgLevel);
 451   
     }
 452   
 
 453   
     /**
 454   
      * Writes a target level message to the log with the given log level.
 455   
      * @param target The target to use in the log.
 456   
      *               Must not be <code>null</code>.
 457   
      * @param message The text to log. Should not be <code>null</code>.
 458   
      * @param msgLevel The priority level to log at.
 459   
      */
 460  2
     public void log(Target target, String message, int msgLevel) {
 461  2
         fireMessageLogged(target, message, msgLevel);
 462   
     }
 463   
 
 464   
     /**
 465   
      * Returns the set of global filters.
 466   
      *
 467   
      * @return the set of global filters
 468   
      */
 469  11
     public FilterSet getGlobalFilterSet() {
 470  11
         return globalFilterSet;
 471   
     }
 472   
 
 473   
     /**
 474   
      * Sets a property. Any existing property of the same name
 475   
      * is overwritten, unless it is a user property.
 476   
      * @param name The name of property to set.
 477   
      *             Must not be <code>null</code>.
 478   
      * @param value The new value of the property.
 479   
      *              Must not be <code>null</code>.
 480   
      */
 481  127
     public synchronized void setProperty(String name, String value) {
 482  127
         PropertyHelper.getPropertyHelper(this).
 483   
                 setProperty(null, name, value, true);
 484   
     }
 485   
 
 486   
     /**
 487   
      * Sets a property if no value currently exists. If the property
 488   
      * exists already, a message is logged and the method returns with
 489   
      * no other effect.
 490   
      *
 491   
      * @param name The name of property to set.
 492   
      *             Must not be <code>null</code>.
 493   
      * @param value The new value of the property.
 494   
      *              Must not be <code>null</code>.
 495   
      * @since 1.5
 496   
      */
 497  2782
     public synchronized void setNewProperty(String name, String value) {
 498  2782
         PropertyHelper.getPropertyHelper(this).setNewProperty(null, name, 
 499   
                                                               value);
 500   
     }
 501   
 
 502   
     /**
 503   
      * Sets a user property, which cannot be overwritten by
 504   
      * set/unset property calls. Any previous value is overwritten.
 505   
      * @param name The name of property to set.
 506   
      *             Must not be <code>null</code>.
 507   
      * @param value The new value of the property.
 508   
      *              Must not be <code>null</code>.
 509   
      * @see #setProperty(String,String)
 510   
      */
 511  1810
     public synchronized void setUserProperty(String name, String value) {
 512  1810
         PropertyHelper.getPropertyHelper(this).setUserProperty(null, name, 
 513   
                                                                value);
 514   
     }
 515   
 
 516   
     /**
 517   
      * Sets a user property, which cannot be overwritten by set/unset
 518   
      * property calls. Any previous value is overwritten. Also marks
 519   
      * these properties as properties that have not come from the
 520   
      * command line.
 521   
      *
 522   
      * @param name The name of property to set.
 523   
      *             Must not be <code>null</code>.
 524   
      * @param value The new value of the property.
 525   
      *              Must not be <code>null</code>.
 526   
      * @see #setProperty(String,String)
 527   
      */
 528  13
     public synchronized void setInheritedProperty(String name, String value) {
 529  13
         PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
 530  13
         ph.setInheritedProperty(null, name, value);
 531   
     }
 532   
 
 533   
     /**
 534   
      * Sets a property unless it is already defined as a user property
 535   
      * (in which case the method returns silently).
 536   
      *
 537   
      * @param name The name of the property.
 538   
      *             Must not be <code>null</code>.
 539   
      * @param value The property value. Must not be <code>null</code>.
 540   
      */
 541  30016
     private void setPropertyInternal(String name, String value) {
 542  30016
         PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
 543  30016
         ph.setProperty(null, name, value, false);
 544   
     }
 545   
 
 546   
     /**
 547   
      * Returns the value of a property, if it is set.
 548   
      *
 549   
      * @param name The name of the property.
 550   
      *             May be <code>null</code>, in which case
 551   
      *             the return value is also <code>null</code>.
 552   
      * @return the property value, or <code>null</code> for no match
 553   
      *         or if a <code>null</code> name is provided.
 554   
      */
 555  46048
     public String getProperty(String name) {
 556  46048
         PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
 557  46048
         return (String) ph.getProperty(null, name);
 558   
     }
 559   
 
 560   
     /**
 561   
      * Replaces ${} style constructions in the given value with the
 562   
      * string value of the corresponding data types.
 563   
      *
 564   
      * @param value The string to be scanned for property references.
 565   
      *              May be <code>null</code>.
 566   
      *
 567   
      * @return the given string with embedded property names replaced
 568   
      *         by values, or <code>null</code> if the given string is
 569   
      *         <code>null</code>.
 570   
      *
 571   
      * @exception BuildException if the given value has an unclosed
 572   
      *                           property name, e.g. <code>${xxx</code>
 573   
      */
 574  6144
     public String replaceProperties(String value)
 575   
         throws BuildException {
 576  6144
         PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
 577  6144
         return ph.replaceProperties(null, value, null);
 578   
     }
 579   
 
 580   
     /**
 581   
      * Returns the value of a user property, if it is set.
 582   
      *
 583   
      * @param name The name of the property.
 584   
      *             May be <code>null</code>, in which case
 585   
      *             the return value is also <code>null</code>.
 586   
      * @return the property value, or <code>null</code> for no match
 587   
      *         or if a <code>null</code> name is provided.
 588   
      */
 589  11
      public String getUserProperty(String name) {
 590  11
         PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
 591  11
         return (String) ph.getUserProperty(null, name);
 592   
     }
 593   
 
 594   
     /**
 595   
      * Returns a copy of the properties table.
 596   
      * @return a hashtable containing all properties
 597   
      *         (including user properties).
 598   
      */
 599  215
     public Hashtable getProperties() {
 600  215
         PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
 601  215
         return ph.getProperties();
 602   
     }
 603   
 
 604   
     /**
 605   
      * Returns a copy of the user property hashtable
 606   
      * @return a hashtable containing just the user properties
 607   
      */
 608  2
     public Hashtable getUserProperties() {
 609  2
         PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
 610  2
         return ph.getUserProperties();
 611   
     }
 612   
 
 613   
     /**
 614   
      * Copies all user properties that have been set on the command
 615   
      * line or a GUI tool from this instance to the Project instance
 616   
      * given as the argument.
 617   
      *
 618   
      * <p>To copy all "user" properties, you will also have to call
 619   
      * {@link #copyInheritedProperties copyInheritedProperties}.</p>
 620   
      *
 621   
      * @param other the project to copy the properties to.  Must not be null.
 622   
      *
 623   
      * @since Ant 1.5
 624   
      */
 625  50
     public void copyUserProperties(Project other) {
 626  50
         PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
 627  50
         ph.copyUserProperties(other);
 628   
     }
 629   
 
 630   
     /**
 631   
      * Copies all user properties that have not been set on the
 632   
      * command line or a GUI tool from this instance to the Project
 633   
      * instance given as the argument.
 634   
      *
 635   
      * <p>To copy all "user" properties, you will also have to call
 636   
      * {@link #copyUserProperties copyUserProperties}.</p>
 637   
      *
 638   
      * @param other the project to copy the properties to.  Must not be null.
 639   
      *
 640   
      * @since Ant 1.5
 641   
      */
 642  50
     public void copyInheritedProperties(Project other) {
 643  50
         PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
 644  50
         ph.copyInheritedProperties(other);
 645   
     }
 646   
 
 647   
     /**
 648   
      * Sets the default target of the project.
 649   
      *
 650   
      * @param defaultTarget The name of the default target for this project.
 651   
      *                      May be <code>null</code>, indicating that there is
 652   
      *                      no default target.
 653   
      *
 654   
      * @deprecated use setDefault
 655   
      * @see #setDefault(String)
 656   
      */
 657  566
     public void setDefaultTarget(String defaultTarget) {
 658  566
         this.defaultTarget = defaultTarget;
 659   
     }
 660   
 
 661   
     /**
 662   
      * Returns the name of the default target of the project.
 663   
      * @return name of the default target or
 664   
      *         <code>null</code> if no default has been set.
 665   
      */
 666  3
     public String getDefaultTarget() {
 667  3
         return defaultTarget;
 668   
     }
 669   
 
 670   
     /**
 671   
      * Sets the default target of the project.
 672   
      *
 673   
      * @param defaultTarget The name of the default target for this project.
 674   
      *                      May be <code>null</code>, indicating that there is
 675   
      *                      no default target.
 676   
      */
 677  0
     public void setDefault(String defaultTarget) {
 678  0
         this.defaultTarget = defaultTarget;
 679   
     }
 680   
 
 681   
     /**
 682   
      * Sets the name of the project, also setting the user
 683   
      * property <code>ant.project.name</code>.
 684   
      *
 685   
      * @param name The name of the project.
 686   
      *             Must not be <code>null</code>.
 687   
      */
 688  520
     public void setName(String name) {
 689  520
         setUserProperty("ant.project.name",  name);
 690  520
         this.name = name;
 691   
     }
 692   
 
 693   
     /**
 694   
      * Returns the project name, if one has been set.
 695   
      *
 696   
      * @return the project name, or <code>null</code> if it hasn't been set.
 697   
      */
 698  0
     public String getName() {
 699  0
         return name;
 700   
     }
 701   
 
 702   
     /**
 703   
      * Sets the project description.
 704   
      *
 705   
      * @param description The description of the project.
 706   
      *                    May be <code>null</code>.
 707   
      */
 708  0
     public void setDescription(String description) {
 709  0
         this.description = description;
 710   
     }
 711   
 
 712   
     /**
 713   
      * Returns the project description, if one has been set.
 714   
      *
 715   
      * @return the project description, or <code>null</code> if it hasn't
 716   
      *         been set.
 717   
      */
 718  4
     public String getDescription() {
 719  4
         if (description == null) {
 720  4
             description = Description.getDescription(this);
 721   
         }
 722   
 
 723  4
         return description;
 724   
     }
 725   
 
 726   
     /**
 727   
      * Adds a filter to the set of global filters.
 728   
      *
 729   
      * @param token The token to filter.
 730   
      *              Must not be <code>null</code>.
 731   
      * @param value The replacement value.
 732   
      *              Must not be <code>null</code>.
 733   
      * @deprecated Use getGlobalFilterSet().addFilter(token,value)
 734   
      *
 735   
      * @see #getGlobalFilterSet()
 736   
      * @see FilterSet#addFilter(String,String)
 737   
      */
 738  0
     public void addFilter(String token, String value) {
 739  0
         if (token == null) {
 740  0
             return;
 741   
         }
 742   
 
 743  0
         globalFilterSet.addFilter(new FilterSet.Filter(token, value));
 744   
     }
 745   
 
 746   
     /**
 747   
      * Returns a hashtable of global filters, mapping tokens to values.
 748   
      *
 749   
      * @return a hashtable of global filters, mapping tokens to values
 750   
      *         (String to String).
 751   
      *
 752   
      * @deprecated Use getGlobalFilterSet().getFilterHash()
 753   
      *
 754   
      * @see #getGlobalFilterSet()
 755   
      * @see FilterSet#getFilterHash()
 756   
      */
 757  0
     public Hashtable getFilters() {
 758   
         // we need to build the hashtable dynamically
 759  0
         return globalFilterSet.getFilterHash();
 760   
     }
 761   
 
 762   
     /**
 763   
      * Sets the base directory for the project, checking that
 764   
      * the given filename exists and is a directory.
 765   
      *
 766   
      * @param baseD The project base directory.
 767   
      *              Must not be <code>null</code>.
 768   
      *
 769   
      * @exception BuildException if the directory if invalid
 770   
      */
 771  101
     public void setBasedir(String baseD) throws BuildException {
 772  101
         setBaseDir(new File(baseD));
 773   
     }
 774   
 
 775   
     /**
 776   
      * Sets the base directory for the project, checking that
 777   
      * the given file exists and is a directory.
 778   
      *
 779   
      * @param baseDir The project base directory.
 780   
      *                Must not be <code>null</code>.
 781   
      * @exception BuildException if the specified file doesn't exist or
 782   
      *                           isn't a directory
 783   
      */
 784  673
     public void setBaseDir(File baseDir) throws BuildException {
 785  673
         baseDir = fileUtils.normalize(baseDir.getAbsolutePath());
 786  673
         if (!baseDir.exists()) {
 787  0
             throw new BuildException("Basedir " + baseDir.getAbsolutePath()
 788   
                 + " does not exist");
 789   
         }
 790  673
         if (!baseDir.isDirectory()) {
 791  0
             throw new BuildException("Basedir " + baseDir.getAbsolutePath()
 792   
                 + " is not a directory");
 793   
         }
 794  673
         this.baseDir = baseDir;
 795  673
         setPropertyInternal("basedir", this.baseDir.getPath());
 796  673
         String msg = "Project base dir set to: " + this.baseDir;
 797  673
          log(msg, MSG_VERBOSE);
 798   
     }
 799   
 
 800   
     /**
 801   
      * Returns the base directory of the project as a file object.
 802   
      *
 803   
      * @return the project base directory, or <code>null</code> if the
 804   
      *         base directory has not been successfully set to a valid value.
 805   
      */
 806  350
     public File getBaseDir() {
 807  350
         if (baseDir == null) {
 808  0
             try {
 809  0
                 setBasedir(".");
 810   
             } catch (BuildException ex) {
 811  0
                 ex.printStackTrace();
 812   
             }
 813   
         }
 814  350
         return baseDir;
 815   
     }
 816   
 
 817   
     /**
 818   
      * Returns the version of Java this class is running under.
 819   
      * @return the version of Java as a String, e.g. "1.1"
 820   
      * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
 821   
      * @deprecated use org.apache.tools.ant.util.JavaEnvUtils instead
 822   
      */
 823  0
     public static String getJavaVersion() {
 824  0
         return JavaEnvUtils.getJavaVersion();
 825   
     }
 826   
 
 827   
     /**
 828   
      * Sets the <code>ant.java.version</code> property and tests for
 829   
      * unsupported JVM versions. If the version is supported,
 830   
      * verbose log messages are generated to record the Java version
 831   
      * and operating system name.
 832   
      *
 833   
      * @exception BuildException if this Java version is not supported
 834   
      *
 835   
      * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
 836   
      */
 837  603
     public void setJavaVersionProperty() throws BuildException {
 838  603
         String javaVersion = JavaEnvUtils.getJavaVersion();
 839  603
         setPropertyInternal("ant.java.version", javaVersion);
 840   
 
 841   
         // sanity check
 842  603
         if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_0)) {
 843  0
             throw new BuildException("Ant cannot work on Java 1.0");
 844   
         }
 845   
 
 846  603
         log("Detected Java version: " + javaVersion + " in: "
 847   
             + System.getProperty("java.home"), MSG_VERBOSE);
 848   
 
 849  603
         log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
 850   
     }
 851   
 
 852   
     /**
 853   
      * Adds all system properties which aren't already defined as
 854   
      * user properties to the project properties.
 855   
      */
 856  563
     public void setSystemProperties() {
 857  563
         Properties systemP = System.getProperties();
 858  563
         Enumeration e = systemP.keys();
 859  563
         while (e.hasMoreElements()) {
 860  28740
             Object name = e.nextElement();
 861  28740
             String value = systemP.get(name).toString();
 862  28740
             this.setPropertyInternal(name.toString(), value);
 863   
         }
 864   
     }
 865   
 
 866   
     /**
 867   
      * Adds a new task definition to the project.
 868   
      * Attempting to override an existing definition with an
 869   
      * equivalent one (i.e. with the same classname) results in
 870   
      * a verbose log message. Attempting to override an existing definition
 871   
      * with a different one results in a warning log message and
 872   
      * invalidates any tasks which have already been created with the
 873   
      * old definition.
 874   
      *
 875   
      * @param taskName The name of the task to add.
 876   
      *                 Must not be <code>null</code>.
 877   
      * @param taskClass The full name of the class implementing the task.
 878   
      *                  Must not be <code>null</code>.
 879   
      *
 880   
      * @exception BuildException if the class is unsuitable for being an Ant
 881   
      *                           task. An error level message is logged before
 882   
      *                           this exception is thrown.
 883   
      *
 884   
      * @see #checkTaskClass(Class)
 885   
      */
 886  25559
     public void addTaskDefinition(String taskName, Class taskClass)
 887   
          throws BuildException {
 888  25559
         Class old = (Class) taskClassDefinitions.get(taskName);
 889  25559
         if (null != old) {
 890  16479
             if (old.equals(taskClass)) {
 891  16476
                 log("Ignoring override for task " + taskName
 892   
                     + ", it is already defined by the same class.",
 893   
                     MSG_VERBOSE);
 894  16476
                 return;
 895   
             } else {
 896  3
                 int logLevel = MSG_WARN;
 897  3
                 if (old.getName().equals(taskClass.getName())) {
 898  0
                     ClassLoader oldLoader = old.getClassLoader();
 899  0
                     ClassLoader newLoader = taskClass.getClassLoader();
 900   
                     // system classloader on older JDKs can be null
 901  0
                     if (oldLoader != null
 902   
                         && newLoader != null
 903   
                         && oldLoader instanceof AntClassLoader
 904   
                         && newLoader instanceof AntClassLoader
 905   
                         && ((AntClassLoader) oldLoader).getClasspath()
 906   
                         .equals(((AntClassLoader) newLoader).getClasspath())
 907   
                         ) {
 908   
                         // same classname loaded from the same
 909   
                         // classpath components
 910  0
                         logLevel = MSG_VERBOSE;
 911   
                     }
 912   
                 }
 913   
 
 914  3
                 log("Trying to override old definition of task " + taskName,
 915   
                     logLevel);
 916  3
                 invalidateCreatedTasks(taskName);
 917   
             }
 918   
         }
 919   
 
 920  9083
         String msg = " +User task: " + taskName + "     " + taskClass.getName();
 921  9083
         log(msg, MSG_DEBUG);
 922  9083
         checkTaskClass(taskClass);
 923  9073
         taskClassDefinitions.put(taskName, taskClass);
 924   
     }
 925   
 
 926   
     /**
 927   
      * Checks whether or not a class is suitable for serving as Ant task.
 928   
      * Ant task implementation classes must be public, concrete, and have
 929   
      * a no-arg constructor.
 930   
      *
 931   
      * @param taskClass The class to be checked.
 932   
      *                  Must not be <code>null</code>.
 933   
      *
 934   
      * @exception BuildException if the class is unsuitable for being an Ant
 935   
      *                           task. An error level message is logged before
 936   
      *                           this exception is thrown.
 937   
      */
 938  9083
     public void checkTaskClass(final Class taskClass) throws BuildException {
 939  9083
         if (!Modifier.isPublic(taskClass.getModifiers())) {
 940  3
             final String message = taskClass + " is not public";
 941  3
             log(message, Project.MSG_ERR);
 942  3
             throw new BuildException(message);
 943   
         }
 944  9080
         if (Modifier.isAbstract(taskClass.getModifiers())) {
 945  2
             final String message = taskClass + " is abstract";
 946  2
             log(message, Project.MSG_ERR);
 947  2
             throw new BuildException(message);
 948   
         }
 949  9078
         try {
 950  9078
             taskClass.getConstructor(null);
 951   
             // don't have to check for public, since
 952   
             // getConstructor finds public constructors only.
 953   
         } catch (NoSuchMethodException e) {
 954  2
             final String message = "No public no-arg constructor in "
 955   
                 + taskClass;
 956  2
             log(message, Project.MSG_ERR);
 957  2
             throw new BuildException(message);
 958   
         }
 959  9076
         if (!Task.class.isAssignableFrom(taskClass)) {
 960  106
             TaskAdapter.checkTaskClass(taskClass, this);
 961   
         }
 962   
     }
 963   
 
 964   
     /**
 965   
      * Returns the current task definition hashtable. The returned hashtable is
 966   
      * "live" and so should not be modified.
 967   
      *
 968   
      * @return a map of from task name to implementing class
 969   
      *         (String to Class).
 970   
      */
 971  118
     public Hashtable getTaskDefinitions() {
 972  118
         return taskClassDefinitions;
 973   
     }
 974   
 
 975   
     /**
 976   
      * Adds a new datatype definition.
 977   
      * Attempting to override an existing definition with an
 978   
      * equivalent one (i.e. with the same classname) results in
 979   
      * a verbose log message. Attempting to override an existing definition
 980   
      * with a different one results in a warning log message, but the
 981   
      * definition is changed.
 982   
      *
 983   
      * @param typeName The name of the datatype.
 984   
      *                 Must not be <code>null</code>.
 985   
      * @param typeClass The full name of the class implementing the datatype.
 986   
      *                  Must not be <code>null</code>.
 987   
      */
 988  4831
     public void addDataTypeDefinition(String typeName, Class typeClass) {
 989  4831
         synchronized (dataClassDefinitions) {
 990  4831
             Class old = (Class) dataClassDefinitions.get(typeName);
 991  4831
             if (null != old) {
 992  3924
                 if (old.equals(typeClass)) {
 993  3924
                     log("Ignoring override for datatype " + typeName
 994   
                         + ", it is already defined by the same class.",
 995   
                         MSG_VERBOSE);
 996  3924
                     return;
 997   
                 } else {
 998  0
                     log("Trying to override old definition of datatype "
 999   
                         + typeName, MSG_WARN);
 1000   
                 }
 1001   
             }
 1002  907
             dataClassDefinitions.put(typeName, typeClass);
 1003   
         }
 1004  907
         String msg = " +User datatype: " + typeName + "     "
 1005   
             + typeClass.getName();
 1006  907
         log(msg, MSG_DEBUG);
 1007   
     }
 1008   
 
 1009   
     /**
 1010   
      * Returns the current datatype definition hashtable. The returned
 1011   
      * hashtable is "live" and so should not be modified.
 1012   
      *
 1013   
      * @return a map of from datatype name to implementing class
 1014   
      *         (String to Class).
 1015   
      */
 1016  445
     public Hashtable getDataTypeDefinitions() {
 1017  445
         return dataClassDefinitions;
 1018   
     }
 1019   
 
 1020   
     /**
 1021   
      * Adds a <em>new</em> target to the project.
 1022   
      *
 1023   
      * @param target The target to be added to the project.
 1024   
      *               Must not be <code>null</code>.
 1025   
      *
 1026   
      * @exception BuildException if the target already exists in the project
 1027   
      *
 1028   
      * @see Project#addOrReplaceTarget
 1029   
      */
 1030  0
     public void addTarget(Target target) throws BuildException {
 1031  0
         String name = target.getName();
 1032  0
         if (targets.get(name) != null) {
 1033  0
             throw new BuildException("Duplicate target: `" + name + "'");
 1034   
         }
 1035  0
         addOrReplaceTarget(name, target);
 1036   
     }
 1037   
 
 1038   
     /**
 1039   
      * Adds a <em>new</em> target to the project.
 1040   
      *
 1041   
      * @param targetName The name to use for the target.
 1042   
      *             Must not be <code>null</code>.
 1043   
      * @param target The target to be added to the project.
 1044   
      *               Must not be <code>null</code>.
 1045   
      *
 1046   
      * @exception BuildException if the target already exists in the project
 1047   
      *
 1048   
      * @see Project#addOrReplaceTarget
 1049   
      */
 1050  566
      public void addTarget(String targetName, Target target)
 1051   
          throws BuildException {
 1052  566
          if (targets.get(targetName) != null) {
 1053  0
              throw new BuildException("Duplicate target: `" + targetName + "'");
 1054   
          }
 1055  566
          addOrReplaceTarget(targetName, target);
 1056   
      }
 1057   
 
 1058   
     /**
 1059   
      * Adds a target to the project, or replaces one with the same
 1060   
      * name.
 1061   
      *
 1062   
      * @param target The target to be added or replaced in the project.
 1063   
      *               Must not be <code>null</code>.
 1064   
      */
 1065  0
     public void addOrReplaceTarget(Target target) {
 1066  0
         addOrReplaceTarget(target.getName(), target);
 1067   
     }
 1068   
 
 1069   
     /**
 1070   
      * Adds a target to the project, or replaces one with the same
 1071   
      * name.
 1072   
      *
 1073   
      * @param targetName The name to use for the target.
 1074   
      *                   Must not be <code>null</code>.
 1075   
      * @param target The target to be added or replaced in the project.
 1076   
      *               Must not be <code>null</code>.
 1077   
      */
 1078  8526
     public void addOrReplaceTarget(String targetName, Target target) {
 1079  8526
         String msg = " +Target: " + targetName;
 1080  8526
         log(msg, MSG_DEBUG);
 1081  8526
         target.setProject(this);
 1082  8526
         targets.put(targetName, target);
 1083   
     }
 1084   
 
 1085   
     /**
 1086   
      * Returns the hashtable of targets. The returned hashtable
 1087   
      * is "live" and so should not be modified.
 1088   
      * @return a map from name to target (String to Target).
 1089   
      */
 1090  7962
     public Hashtable getTargets() {
 1091  7962
         return targets;
 1092   
     }
 1093   
 
 1094   
     /**
 1095   
      * Creates a new instance of a task, adding it to a list of
 1096   
      * created tasks for later invalidation. This causes all tasks
 1097   
      * to be remembered until the containing project is removed
 1098   
      * @param taskType The name of the task to create an instance of.
 1099   
      *                 Must not be <code>null</code>.
 1100   
      *
 1101   
      * @return an instance of the specified task, or <code>null</code> if
 1102   
      *         the task name is not recognised.
 1103   
      *
 1104   
      * @exception BuildException if the task name is recognised but task
 1105   
      *                           creation fails.
 1106   
      */
 1107  3250
     public Task createTask(String taskType) throws BuildException {
 1108  3250
         Task task = createNewTask(taskType);
 1109  3250
         if (task != null) {
 1110  3077
             addCreatedTask(taskType, task);
 1111   
         }
 1112  3250
         return task;
 1113   
     }
 1114   
 
 1115   
     /**
 1116   
      * Creates a new instance of a task. This task is not
 1117   
      * cached in the createdTasks list.
 1118   
      * @since ant1.6
 1119   
      * @param taskType The name of the task to create an instance of.
 1120   
      *                 Must not be <code>null</code>.
 1121   
      *
 1122   
      * @return an instance of the specified task, or <code>null</code> if
 1123   
      *         the task name is not recognised.
 1124   
      *
 1125   
      * @exception BuildException if the task name is recognised but task
 1126   
      *                           creation fails.
 1127   
      */
 1128  3250
     private Task createNewTask(String taskType) throws BuildException {
 1129  3250
         Class c = (Class) taskClassDefinitions.get(taskType);
 1130   
 
 1131  3250
         if (c == null) {
 1132  173
             return null;
 1133   
         }
 1134   
 
 1135  3077
         try {
 1136  3077
             Object o = c.newInstance();
 1137  3077
             Task task = null;
 1138  3077
             if (o instanceof Task) {
 1139  3011
                task = (Task) o;
 1140   
             } else {
 1141   
                 // "Generic" Bean - use the setter pattern
 1142   
                 // and an Adapter
 1143  66
                 TaskAdapter taskA = new TaskAdapter();
 1144  66
                 taskA.setProxy(o);
 1145  66
                 task = taskA;
 1146   
             }
 1147  3077
             task.setProject(this);
 1148  3077
             task.setTaskType(taskType);
 1149   
 
 1150   
             // set default value, can be changed by the user
 1151  3077
             task.setTaskName(taskType);
 1152   
 
 1153  3077
             String msg = "   +Task: " + taskType;
 1154  3077
             log (msg, MSG_DEBUG);
 1155  3077
             return task;
 1156   
         } catch (Throwable t) {
 1157  0
             String msg = "Could not create task of type: "
 1158   
                  + taskType + " due to " + t;
 1159  0
             throw new BuildException(msg, t);
 1160   
         }
 1161   
     }
 1162   
 
 1163   
     /**
 1164   
      * Keeps a record of all tasks that have been created so that they
 1165   
      * can be invalidated if a new task definition overrides the current one.
 1166   
      *
 1167   
      * @param type The name of the type of task which has been created.
 1168   
      *             Must not be <code>null</code>.
 1169   
      *
 1170   
      * @param task The freshly created task instance.
 1171   
      *             Must not be <code>null</code>.
 1172   
      */
 1173  3077
     private void addCreatedTask(String type, Task task) {
 1174  3077
         synchronized (createdTasks) {
 1175  3077
             Vector v = (Vector) createdTasks.get(type);
 1176  3077
             if (v == null) {
 1177  1302
                 v = new Vector();
 1178  1302
                 createdTasks.put(type, v);
 1179   
             }
 1180  3077
             v.addElement(WeakishReference.createReference(task));
 1181   
         }
 1182   
     }
 1183   
 
 1184   
     /**
 1185   
      * Mark tasks as invalid which no longer are of the correct type
 1186   
      * for a given taskname.
 1187   
      *
 1188   
      * @param type The name of the type of task to invalidate.
 1189   
      *             Must not be <code>null</code>.
 1190   
      */
 1191  3
     private void invalidateCreatedTasks(String type) {
 1192  3
         synchronized (createdTasks) {
 1193  3
             Vector v = (Vector) createdTasks.get(type);
 1194  3
             if (v != null) {
 1195  0
                 Enumeration enum = v.elements();
 1196  0
                 while (enum.hasMoreElements()) {
 1197  0
                     WeakishReference ref =
 1198   
                             (WeakishReference) enum.nextElement();
 1199  0
                     Task t = (Task) ref.get();
 1200   
                     //being a weak ref, it may be null by this point
 1201  0
                     if (t != null) {
 1202  0
                         t.markInvalid();
 1203   
                     }
 1204   
                 }
 1205  0
                 v.removeAllElements();
 1206  0
                 createdTasks.remove(type);
 1207   
             }
 1208   
         }
 1209   
     }
 1210   
 
 1211   
     /**
 1212   
      * Creates a new instance of a data type.
 1213   
      *
 1214   
      * @param typeName The name of the data type to create an instance of.
 1215   
      *                 Must not be <code>null</code>.
 1216   
      *
 1217   
      * @return an instance of the specified data type, or <code>null</code> if
 1218   
      *         the data type name is not recognised.
 1219   
      *
 1220   
      * @exception BuildException if the data type name is recognised but
 1221   
      *                           instance creation fails.
 1222   
      */
 1223  177
     public Object createDataType(String typeName) throws BuildException {
 1224  177
         Class c = (Class) dataClassDefinitions.get(typeName);
 1225   
 
 1226  177
         if (c == null) {
 1227  2
             return null;
 1228   
         }
 1229   
 
 1230  175
         try {
 1231  175
             java.lang.reflect.Constructor ctor = null;
 1232  175
             boolean noArg = false;
 1233   
             // DataType can have a "no arg" constructor or take a single
 1234   
             // Project argument.
 1235  175
             try {
 1236  175
                 ctor = c.getConstructor(new Class[0]);
 1237  86
                 noArg = true;
 1238   
             } catch (NoSuchMethodException nse) {
 1239  89
                 ctor = c.getConstructor(new Class[] {Project.class});
 1240  89
                 noArg = false;
 1241   
             }
 1242   
 
 1243  175
             Object o = null;
 1244  175
             if (noArg) {
 1245  86
                  o = ctor.newInstance(new Object[0]);
 1246   
             } else {
 1247  89
                  o = ctor.newInstance(new Object[] {this});
 1248   
             }
 1249  175
             if (o instanceof ProjectComponent) {
 1250  175
                 ((ProjectComponent) o).setProject(this);
 1251   
             }
 1252  175
             String msg = "   +DataType: " + typeName;
 1253  175
             log (msg, MSG_DEBUG);
 1254  175
             return o;
 1255   
         } catch (java.lang.reflect.InvocationTargetException ite) {
 1256  0
             Throwable t = ite.getTargetException();
 1257  0
             String msg = "Could not create datatype of type: "
 1258   
                  + typeName + " due to " + t;
 1259  0
             throw new BuildException(msg, t);
 1260   
         } catch (Throwable t) {
 1261  0
             String msg = "Could not create datatype of type: "
 1262   
                  + typeName + " due to " + t;
 1263  0
             throw new BuildException(msg, t);
 1264   
         }
 1265   
     }
 1266   
 
 1267   
     /**
 1268   
      * Execute the specified sequence of targets, and the targets
 1269   
      * they depend on.
 1270   
      *
 1271   
      * @param targetNames A vector of target name strings to execute.
 1272   
      *                    Must not be <code>null</code>.
 1273   
      *
 1274   
      * @exception BuildException if the build failed
 1275   
      */
 1276  1
     public void executeTargets(Vector targetNames) throws BuildException {
 1277   
 
 1278  1
         for (int i = 0; i < targetNames.size(); i++) {
 1279  1
             executeTarget((String) targetNames.elementAt(i));
 1280   
         }
 1281   
     }
 1282   
 
 1283   
     /**
 1284   
      * Demultiplexes output so that each task receives the appropriate
 1285   
      * messages. If the current thread is not currently executing a task,
 1286   
      * the message is logged directly.
 1287   
      *
 1288   
      * @param line Message to handle. Should not be <code>null</code>.
 1289   
      * @param isError Whether the text represents an error (<code>true</code>)
 1290   
      *        or information (<code>false</code>).
 1291   
      */
 1292  283
     public void demuxOutput(String line, boolean isError) {
 1293  283
         Task task = getThreadTask(Thread.currentThread());
 1294  283
         if (task == null) {
 1295  0
             fireMessageLogged(this, line, isError ? MSG_ERR : MSG_INFO);
 1296   
         } else {
 1297  283
             if (isError) {
 1298  33
                 task.handleErrorOutput(line);
 1299   
             } else {
 1300  250
                 task.handleOutput(line);
 1301   
             }
 1302   
         }
 1303   
     }
 1304   
 
 1305   
     /**
 1306   
      * Read data from the default input stream. If no default has been 
 1307   
      * specified, System.in is used. 
 1308   
      *
 1309   
      * @param buffer the buffer into which data is to be read.
 1310   
      * @param offset the offset into the buffer at which data is stored.
 1311   
      * @param length the amount of data to read
 1312   
      *
 1313   
      * @return the number of bytes read
 1314   
      * 
 1315   
      * @exception IOException if the data cannot be read
 1316   
      * @since Ant 1.6
 1317   
      */
 1318  0
     public int defaultInput(byte[] buffer, int offset, int length) 
 1319   
         throws IOException {
 1320  0
         if (defaultInputStream != null) {
 1321  0
             return defaultInputStream.read(buffer, offset, length);
 1322   
         } else {
 1323  0
             throw new EOFException("No input provided for project");
 1324   
         }
 1325   
     }
 1326   
     
 1327   
     /**
 1328   
      * Demux an input request to the correct task.
 1329   
      *
 1330   
      * @param buffer the buffer into which data is to be read.
 1331   
      * @param offset the offset into the buffer at which data is stored.
 1332   
      * @param length the amount of data to read
 1333   
      *
 1334   
      * @return the number of bytes read
 1335   
      * 
 1336   
      * @exception IOException if the data cannot be read
 1337   
      * @since Ant 1.6
 1338   
      */     
 1339  0
     public int demuxInput(byte[] buffer, int offset, int length) 
 1340   
         throws IOException {
 1341  0
         Task task = getThreadTask(Thread.currentThread());
 1342  0
         if (task == null) {
 1343  0
             return defaultInput(buffer, offset, length);
 1344   
         } else {
 1345  0
             return task.handleInput(buffer, offset, length);
 1346   
         }
 1347   
     }
 1348   
     
 1349   
     /**
 1350   
      * Demultiplexes flush operation so that each task receives the appropriate
 1351   
      * messages. If the current thread is not currently executing a task,
 1352   
      * the message is logged directly.
 1353   
      *
 1354   
      * @since Ant 1.5.2
 1355   
      *
 1356   
      * @param line Message to handle. Should not be <code>null</code>.
 1357   
      * @param isError Whether the text represents an error (<code>true</code>)
 1358   
      *        or information (<code>false</code>).
 1359   
      */
 1360  0
     public void demuxFlush(String line, boolean isError) {
 1361  0
         Task task = getThreadTask(Thread.currentThread());
 1362  0
         if (task == null) {
 1363  0
             fireMessageLogged(this, line, isError ? MSG_ERR : MSG_INFO);
 1364   
         } else {
 1365  0
             if (isError) {
 1366  0
                 task.handleErrorFlush(line);
 1367   
             } else {
 1368  0
                 task.handleFlush(line);
 1369   
             }
 1370   
         }
 1371   
     }
 1372   
 
 1373   
     
 1374   
     
 1375   
     /**
 1376   
      * Executes the specified target and any targets it depends on.
 1377   
      *
 1378   
      * @param targetName The name of the target to execute.
 1379   
      *                   Must not be <code>null</code>.
 1380   
      *
 1381   
      * @exception BuildException if the build failed
 1382   
      */
 1383  899
     public void executeTarget(String targetName) throws BuildException {
 1384   
 
 1385   
         // sanity check ourselves, if we've been asked to build nothing
 1386   
         // then we should complain
 1387   
 
 1388  899
         if (targetName == null) {
 1389  0
             String msg = "No target specified";
 1390  0
             throw new BuildException(msg);
 1391   
         }
 1392   
 
 1393   
         // Sort the dependency tree, and run everything from the
 1394   
         // beginning until we hit our targetName.
 1395   
         // Sorting checks if all the targets (and dependencies)
 1396   
         // exist, and if there is any cycle in the dependency
 1397   
         // graph.
 1398  899
         Vector sortedTargets = topoSort(targetName, targets);
 1399   
 
 1400  898
         int curidx = 0;
 1401  898
         Target curtarget;
 1402   
 
 1403  898
         do {
 1404  1041
             curtarget = (Target) sortedTargets.elementAt(curidx++);
 1405  1041
             curtarget.performTasks();
 1406  895
         } while (!curtarget.getName().equals(targetName));
 1407   
     }
 1408   
 
 1409   
     /**
 1410   
      * Returns the canonical form of a filename.
 1411   
      * <p>
 1412   
      * If the specified file name is relative it is resolved
 1413   
      * with respect to the given root directory.
 1414   
      *
 1415   
      * @param fileName The name of the file to resolve.
 1416   
      *                 Must not be <code>null</code>.
 1417   
      *
 1418   
      * @param rootDir  The directory to resolve relative file names with
 1419   
      *                 respect to. May be <code>null</code>, in which case
 1420   
      *                 the current directory is used.
 1421   
      *
 1422   
      * @return the resolved File.
 1423   
      *
 1424   
      * @deprecated
 1425   
      */
 1426  527
     public File resolveFile(String fileName, File rootDir) {
 1427  527
         return fileUtils.resolveFile(rootDir, fileName);
 1428   
     }
 1429   
 
 1430   
     /**
 1431   
      * Returns the canonical form of a filename.
 1432   
      * <p>
 1433   
      * If the specified file name is relative it is resolved
 1434   
      * with respect to the project's base directory.
 1435   
      *
 1436   
      * @param fileName The name of the file to resolve.
 1437   
      *                 Must not be <code>null</code>.
 1438   
      *
 1439   
      * @return the resolved File.
 1440   
      *
 1441   
      */
 1442  19849
     public File resolveFile(String fileName) {
 1443  19849
         return fileUtils.resolveFile(baseDir, fileName);
 1444   
     }
 1445   
 
 1446   
     /**
 1447   
      * Translates a path into its native (platform specific) format.
 1448   
      * <p>
 1449   
      * This method uses PathTokenizer to separate the input path
 1450   
      * into its components. This handles DOS style paths in a relatively
 1451   
      * sensible way. The file separators are then converted to their platform
 1452   
      * specific versions.
 1453   
      *
 1454   
      * @param toProcess The path to be translated.
 1455   
      *                  May be <code>null</code>.
 1456   
      *
 1457   
      * @return the native version of the specified path or
 1458   
      *         an empty string if the path is <code>null</code> or empty.
 1459   
      *
 1460   
      * @see PathTokenizer
 1461   
      */
 1462  0
     public static String translatePath(String toProcess) {
 1463  0
         if (toProcess == null || toProcess.length() == 0) {
 1464  0
             return "";
 1465   
         }
 1466   
 
 1467  0
         StringBuffer path = new StringBuffer(toProcess.length() + 50);
 1468  0
         PathTokenizer tokenizer = new PathTokenizer(toProcess);
 1469  0
         while (tokenizer.hasMoreTokens()) {
 1470  0
             String pathComponent = tokenizer.nextToken();
 1471  0
             pathComponent = pathComponent.replace('/', File.separatorChar);
 1472  0
             pathComponent = pathComponent.replace('\\', File.separatorChar);
 1473  0
             if (path.length() != 0) {
 1474  0
                 path.append(File.pathSeparatorChar);
 1475   
             }
 1476  0
             path.append(pathComponent);
 1477   
         }
 1478   
 
 1479  0
         return path.toString();
 1480   
     }
 1481   
 
 1482   
     /**
 1483   
      * Convenience method to copy a file from a source to a destination.
 1484   
      * No filtering is performed.
 1485   
      *
 1486   
      * @param sourceFile Name of file to copy from.
 1487   
      *                   Must not be <code>null</code>.
 1488   
      * @param destFile Name of file to copy to.
 1489   
      *                 Must not be <code>null</code>.
 1490   
      *
 1491   
      * @exception IOException if the copying fails
 1492   
      *
 1493   
      * @deprecated
 1494   
      */
 1495  0
     public void copyFile(String sourceFile, String destFile)
 1496   
           throws IOException {
 1497  0
         fileUtils.copyFile(sourceFile, destFile);
 1498   
     }
 1499   
 
 1500   
     /**
 1501   
      * Convenience method to copy a file from a source to a destination
 1502   
      * specifying if token filtering should be used.
 1503   
      *
 1504   
      * @param sourceFile Name of file to copy from.
 1505   
      *                   Must not be <code>null</code>.
 1506   
      * @param destFile Name of file to copy to.
 1507   
      *                 Must not be <code>null</code>.
 1508   
      * @param filtering Whether or not token filtering should be used during
 1509   
      *                  the copy.
 1510   
      *
 1511   
      * @exception IOException if the copying fails
 1512   
      *
 1513   
      * @deprecated
 1514   
      */
 1515  0
     public void copyFile(String sourceFile, String destFile, boolean filtering)
 1516   
         throws IOException {
 1517  0
         fileUtils.copyFile(sourceFile, destFile,
 1518   
             filtering ? globalFilters : null);
 1519   
     }
 1520   
 
 1521   
     /**
 1522   
      * Convenience method to copy a file from a source to a
 1523   
      * destination specifying if token filtering should be used and if
 1524   
      * source files may overwrite newer destination files.
 1525   
      *
 1526   
      * @param sourceFile Name of file to copy from.
 1527   
      *                   Must not be <code>null</code>.
 1528   
      * @param destFile Name of file to copy to.
 1529   
      *                 Must not be <code>null</code>.
 1530   
      * @param filtering Whether or not token filtering should be used during
 1531   
      *                  the copy.
 1532   
      * @param overwrite Whether or not the destination file should be
 1533   
      *                  overwritten if it already exists.
 1534   
      *
 1535   
      * @exception IOException if the copying fails
 1536   
      *
 1537   
      * @deprecated
 1538   
      */
 1539  688
     public void copyFile(String sourceFile, String destFile, boolean filtering,
 1540   
                          boolean overwrite) throws IOException {
 1541  688
         fileUtils.copyFile(sourceFile, destFile,
 1542   
             filtering ? globalFilters : null, overwrite);
 1543   
     }
 1544   
 
 1545   
     /**
 1546   
      * Convenience method to copy a file from a source to a
 1547   
      * destination specifying if token filtering should be used, if
 1548   
      * source files may overwrite newer destination files, and if the
 1549   
      * last modified time of the resulting file should be set to
 1550   
      * that of the source file.
 1551   
      *
 1552   
      * @param sourceFile Name of file to copy from.
 1553   
      *                   Must not be <code>null</code>.
 1554   
      * @param destFile Name of file to copy to.
 1555   
      *                 Must not be <code>null</code>.
 1556   
      * @param filtering Whether or not token filtering should be used during
 1557   
      *                  the copy.
 1558   
      * @param overwrite Whether or not the destination file should be
 1559   
      *                  overwritten if it already exists.
 1560   
      * @param preserveLastModified Whether or not the last modified time of
 1561   
      *                             the resulting file should be set to that
 1562   
      *                             of the source file.
 1563   
      *
 1564   
      * @exception IOException if the copying fails
 1565   
      *
 1566   
      * @deprecated
 1567   
      */
 1568  0
     public void copyFile(String sourceFile, String destFile, boolean filtering,
 1569   
                          boolean overwrite, boolean preserveLastModified)
 1570   
         throws IOException {
 1571  0
         fileUtils.copyFile(sourceFile, destFile,
 1572   
             filtering ? globalFilters : null, overwrite, preserveLastModified);
 1573   
     }
 1574   
 
 1575   
     /**
 1576   
      * Convenience method to copy a file from a source to a destination.
 1577   
      * No filtering is performed.
 1578   
      *
 1579   
      * @param sourceFile File to copy from.
 1580   
      *                   Must not be <code>null</code>.
 1581   
      * @param destFile File to copy to.
 1582   
      *                 Must not be <code>null</code>.
 1583   
      *
 1584   
      * @exception IOException if the copying fails
 1585   
      *
 1586   
      * @deprecated
 1587   
      */
 1588  0
     public void copyFile(File sourceFile, File destFile) throws IOException {
 1589  0
         fileUtils.copyFile(sourceFile, destFile);
 1590   
     }
 1591   
 
 1592   
     /**
 1593   
      * Convenience method to copy a file from a source to a destination
 1594   
      * specifying if token filtering should be used.
 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   
      * @param filtering Whether or not token filtering should be used during
 1601   
      *                  the copy.
 1602   
      *
 1603   
      * @exception IOException if the copying fails
 1604   
      *
 1605   
      * @deprecated
 1606   
      */
 1607  0
     public void copyFile(File sourceFile, File destFile, boolean filtering)
 1608   
         throws IOException {
 1609  0
         fileUtils.copyFile(sourceFile, destFile,
 1610   
             filtering ? globalFilters : null);
 1611   
     }
 1612   
 
 1613   
     /**
 1614   
      * Convenience method to copy a file from a source to a
 1615   
      * destination specifying if token filtering should be used and if
 1616   
      * source files may overwrite newer destination files.
 1617   
      *
 1618   
      * @param sourceFile File to copy from.
 1619   
      *                   Must not be <code>null</code>.
 1620   
      * @param destFile File to copy to.
 1621   
      *                 Must not be <code>null</code>.
 1622   
      * @param filtering Whether or not token filtering should be used during
 1623   
      *                  the copy.
 1624   
      * @param overwrite Whether or not the destination file should be
 1625   
      *                  overwritten if it already exists.
 1626   
      *
 1627   
      * @exception IOException if the file cannot be copied.
 1628   
      *
 1629   
      * @deprecated
 1630   
      */
 1631  2
     public void copyFile(File sourceFile, File destFile, boolean filtering,
 1632   
                          boolean overwrite) throws IOException {
 1633  2
         fileUtils.copyFile(sourceFile, destFile,
 1634   
             filtering ? globalFilters : null, overwrite);
 1635   
     }
 1636   
 
 1637   
     /**
 1638   
      * Convenience method to copy a file from a source to a
 1639   
      * destination specifying if token filtering should be used, if
 1640   
      * source files may overwrite newer destination files, and if the
 1641   
      * last modified time of the resulting file should be set to
 1642   
      * that of the source file.
 1643   
      *
 1644   
      * @param sourceFile File to copy from.
 1645   
      *                   Must not be <code>null</code>.
 1646   
      * @param destFile File to copy to.
 1647   
      *                 Must not be <code>null</code>.
 1648   
      * @param filtering Whether or not token filtering should be used during
 1649   
      *                  the copy.
 1650   
      * @param overwrite Whether or not the destination file should be
 1651   
      *                  overwritten if it already exists.
 1652   
      * @param preserveLastModified Whether or not the last modified time of
 1653   
      *                             the resulting file should be set to that
 1654   
      *                             of the source file.
 1655   
      *
 1656   
      * @exception IOException if the file cannot be copied.
 1657   
      *
 1658   
      * @deprecated
 1659   
      */
 1660  0
     public void copyFile(File sourceFile, File destFile, boolean filtering,
 1661   
                          boolean overwrite, boolean preserveLastModified)
 1662   
         throws IOException {
 1663  0
         fileUtils.copyFile(sourceFile, destFile,
 1664   
             filtering ? globalFilters : null, overwrite, preserveLastModified);
 1665   
     }
 1666   
 
 1667   
     /**
 1668   
      * Calls File.setLastModified(long time) on Java above 1.1, and logs
 1669   
      * a warning on Java 1.1.
 1670   
      *
 1671   
      * @param file The file to set the last modified time on.
 1672   
      *             Must not be <code>null</code>.
 1673   
      *
 1674   
      * @param time the required modification time.
 1675   
      *
 1676   
      * @deprecated
 1677   
      *
 1678   
      * @exception BuildException if the last modified time cannot be set
 1679   
      *                           despite running on a platform with a version
 1680   
      *                           above 1.1.
 1681   
      */
 1682  0
     public void setFileLastModified(File file, long time)
 1683   
          throws BuildException {
 1684  0
         if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
 1685  0
             log("Cannot change the modification time of " + file
 1686   
                 + " in JDK 1.1", Project.MSG_WARN);
 1687  0
             return;
 1688   
         }
 1689  0
         fileUtils.setFileLastModified(file, time);
 1690  0
         log("Setting modification time for " + file, MSG_VERBOSE);
 1691   
     }
 1692   
 
 1693   
     /**
 1694   
      * Returns the boolean equivalent of a string, which is considered
 1695   
      * <code>true</code> if either <code>"on"</code>, <code>"true"</code>,
 1696   
      * or <code>"yes"</code> is found, ignoring case.
 1697   
      *
 1698   
      * @param s The string to convert to a boolean value.
 1699   
      *          Must not be <code>null</code>.
 1700   
      *
 1701   
      * @return <code>true</code> if the given string is <code>"on"</code>,
 1702   
      *         <code>"true"</code> or <code>"yes"</code>, or
 1703   
      *         <code>false</code> otherwise.
 1704   
      */
 1705  223
     public static boolean toBoolean(String s) {
 1706  223
         return (s.equalsIgnoreCase("on") ||
 1707   
                 s.equalsIgnoreCase("true") ||
 1708   
                 s.equalsIgnoreCase("yes"));
 1709   
     }
 1710   
 
 1711   
     /**
 1712   
      * Topologically sorts a set of targets.
 1713   
      *
 1714   
      * @param root The name of the root target. The sort is created in such
 1715   
      *             a way that the sequence of Targets up to the root
 1716   
      *             target is the minimum possible such sequence.
 1717   
      *             Must not be <code>null</code>.
 1718   
      * @param targets A map of names to targets (String to Target).
 1719   
      *                Must not be <code>null</code>.
 1720   
      * @return a vector of strings with the names of the targets in
 1721   
      *         sorted order.
 1722   
      * @exception BuildException if there is a cyclic dependency among the
 1723   
      *                           targets, or if a named target does not exist.
 1724   
      */
 1725  899
     public final Vector topoSort(String root, Hashtable targets)
 1726   
         throws BuildException {
 1727  899
         Vector ret = new Vector();
 1728  899
         Hashtable state = new Hashtable();
 1729  899
         Stack visiting = new Stack();
 1730   
 
 1731   
         // We first run a DFS based sort using the root as the starting node.
 1732   
         // This creates the minimum sequence of Targets to the root node.
 1733   
         // We then do a sort on any remaining unVISITED targets.
 1734   
         // This is unnecessary for doing our build, but it catches
 1735   
         // circular dependencies or missing Targets on the entire
 1736   
         // dependency tree, not just on the Targets that depend on the
 1737   
         // build Target.
 1738   
 
 1739  899
         tsort(root, targets, state, visiting, ret);
 1740  898
         log("Build sequence for target `" + root + "' is " + ret, MSG_VERBOSE);
 1741  898
         for (Enumeration en = targets.keys(); en.hasMoreElements();) {
 1742  14936
             String curTarget = (String) en.nextElement();
 1743  14936
             String st = (String) state.get(curTarget);
 1744  14936
             if (st == null) {
 1745  13295
                 tsort(curTarget, targets, state, visiting, ret);
 1746  1641
             } else if (st == VISITING) {
 1747  0
                 throw new RuntimeException("Unexpected node in visiting state: "
 1748   
                     + curTarget);
 1749   
             }
 1750   
         }
 1751  898
         log("Complete build sequence is " + ret, MSG_VERBOSE);
 1752  898
         return ret;
 1753   
     }
 1754   
 
 1755   
     /**
 1756   
      * Performs a single step in a recursive depth-first-search traversal of
 1757   
      * the target dependency tree.
 1758   
      * <p>
 1759   
      * The current target is first set to the "visiting" state, and pushed
 1760   
      * onto the "visiting" stack.
 1761   
      * <p>
 1762   
      * An exception is then thrown if any child of the current node is in the
 1763   
      * visiting state, as that implies a circular dependency. The exception
 1764   
      * contains details of the cycle, using elements of the "visiting" stack.
 1765   
      * <p>
 1766   
      * If any child has not already been "visited", this method is called
 1767   
      * recursively on it.
 1768   
      * <p>
 1769   
      * The current target is then added to the ordered list of targets. Note
 1770   
      * that this is performed after the children have been visited in order
 1771   
      * to get the correct order. The current target is set to the "visited"
 1772   
      * state.
 1773   
      * <p>
 1774   
      * By the time this method returns, the ordered list contains the sequence
 1775   
      * of targets up to and including the current target.
 1776   
      *
 1777   
      * @param root The current target to inspect.
 1778   
      *             Must not be <code>null</code>.
 1779   
      * @param targets A mapping from names to targets (String to Target).
 1780   
      *                Must not be <code>null</code>.
 1781   
      * @param state   A mapping from target names to states
 1782   
      *                (String to String).
 1783   
      *                The states in question are "VISITING" and "VISITED".
 1784   
      *                Must not be <code>null</code>.
 1785   
      * @param visiting A stack of targets which are currently being visited.
 1786   
      *                 Must not be <code>null</code>.
 1787   
      * @param ret     The list to add target names to. This will end up
 1788   
      *                containing the complete list of depenencies in
 1789   
      *                dependency order.
 1790   
      *                Must not be <code>null</code>.
 1791   
      *
 1792   
      * @exception BuildException if a non-existent target is specified or if
 1793   
      *                           a circular dependency is detected.
 1794   
      */
 1795  14937
     private final void tsort(String root, Hashtable targets,
 1796   
                              Hashtable state, Stack visiting,
 1797   
                              Vector ret)
 1798   
         throws BuildException {
 1799  14937
         state.put(root, VISITING);
 1800  14937
         visiting.push(root);
 1801   
 
 1802  14937
         Target target = (Target) targets.get(root);
 1803   
 
 1804   
         // Make sure we exist
 1805  14937
         if (target == null) {
 1806  1
             StringBuffer sb = new StringBuffer("Target `");
 1807  1
             sb.append(root);
 1808  1
             sb.append("' does not exist in this project. ");
 1809  1
             visiting.pop();
 1810  1
             if (!visiting.empty()) {
 1811  0
                 String parent = (String) visiting.peek();
 1812  0
                 sb.append("It is used from target `");
 1813  0
                 sb.append(parent);
 1814  0
                 sb.append("'.");
 1815   
             }
 1816   
 
 1817  1
             throw new BuildException(new String(sb));
 1818   
         }
 1819   
 
 1820  14936
         for (Enumeration en = target.getDependencies(); en.hasMoreElements();) {
 1821  3140
             String cur = (String) en.nextElement();
 1822  3140
             String m = (String) state.get(cur);
 1823  3140
             if (m == null) {
 1824   
                 // Not been visited
 1825  743
                 tsort(cur, targets, state, visiting, ret);
 1826  2397
             } else if (m == VISITING) {
 1827   
                 // Currently visiting this node, so have a cycle
 1828  0
                 throw makeCircularException(cur, visiting);
 1829   
             }
 1830   
         }
 1831   
 
 1832  14936
         String p = (String) visiting.pop();
 1833  14936
         if (root != p) {
 1834  0
             throw new RuntimeException("Unexpected internal error: expected to "
 1835   
                 + "pop " + root + " but got " + p);
 1836   
         }
 1837  14936
         state.put(root, VISITED);
 1838  14936
         ret.addElement(target);
 1839   
     }
 1840   
 
 1841   
     /**
 1842   
      * Builds an appropriate exception detailing a specified circular
 1843   
      * dependency.
 1844   
      *
 1845   
      * @param end The dependency to stop at. Must not be <code>null</code>.
 1846   
      * @param stk A stack of dependencies. Must not be <code>null</code>.
 1847   
      *
 1848   
      * @return a BuildException detailing the specified circular dependency.
 1849   
      */
 1850  0
     private static BuildException makeCircularException(String end, Stack stk) {
 1851  0
         StringBuffer sb = new StringBuffer("Circular dependency: ");
 1852  0
         sb.append(end);
 1853  0
         String c;
 1854  0
         do {
 1855  0
             c = (String) stk.pop();
 1856  0
             sb.append(" <- ");
 1857  0
             sb.append(c);
 1858  0
         } while (!c.equals(end));
 1859  0
         return new BuildException(new String(sb));
 1860   
     }
 1861   
 
 1862   
     /**
 1863   
      * Adds a reference to the project.
 1864   
      *
 1865   
      * @param name The name of the reference. Must not be <code>null</code>.
 1866   
      * @param value The value of the reference. Must not be <code>null</code>.
 1867   
      */
 1868  3516
     public void addReference(String name, Object value) {
 1869  3516
         synchronized (references) {
 1870  3516
             Object old = ((AntRefTable) references).getReal(name);
 1871  3516
             if (old == value) {
 1872   
                 // no warning, this is not changing anything
 1873  0
                 return;
 1874   
             }
 1875  3516
             if (old != null && !(old instanceof UnknownElement)) {
 1876  18
                 log("Overriding previous definition of reference to " + name,
 1877   
                     MSG_WARN);
 1878   
             }
 1879   
 
 1880  3516
             String valueAsString = "";
 1881  3516
             try {
 1882  3516
                 valueAsString = value.toString();
 1883   
             } catch (Throwable t) {
 1884  16
                 log("Caught exception (" + t.getClass().getName() + ")"
 1885   
                     + " while expanding " + name + ": " + t.getMessage(),
 1886   
                     MSG_WARN);
 1887   
             }
 1888  3516
             log("Adding reference: " + name + " -> " + valueAsString,
 1889   
                 MSG_DEBUG);
 1890  3516
             references.put(name, value);
 1891   
         }
 1892   
     }
 1893   
 
 1894   
     /**
 1895   
      * Returns a map of the references in the project (String to Object).
 1896   
      * The returned hashtable is "live" and so must not be modified.
 1897   
      *
 1898   
      * @return a map of the references in the project (String to Object).
 1899   
      */
 1900  136
     public Hashtable getReferences() {
 1901  136
         return references;
 1902   
     }
 1903   
 
 1904   
     /**
 1905   
      * Looks up a reference by its key (ID).
 1906   
      *
 1907   
      * @param key The key for the desired reference.
 1908   
      *            Must not be <code>null</code>.
 1909   
      *
 1910   
      * @return the reference with the specified ID, or <code>null</code> if
 1911   
      *         there is no such reference in the project.
 1912   
      */
 1913  433298
     public Object getReference(String key) {
 1914  433298
         return references.get(key);
 1915   
     }
 1916   
 
 1917   
     /**
 1918   
      * Returns a description of the type of the given element, with
 1919   
      * special handling for instances of tasks and data types.
 1920   
      * <p>
 1921   
      * This is useful for logging purposes.
 1922   
      *
 1923   
      * @param element The element to describe.
 1924   
      *                Must not be <code>null</code>.
 1925   
      *
 1926   
      * @return a description of the element type
 1927   
      *
 1928   
      * @since 1.95, Ant 1.5
 1929   
      */
 1930  178
     public String getElementName(Object element) {
 1931  178
         Hashtable elements = taskClassDefinitions;
 1932  178
         Class elementClass = element.getClass();
 1933  178
         String typeName = "task";
 1934  178
         if (!elements.contains(elementClass)) {
 1935  173
             elements = dataClassDefinitions;
 1936  173
             typeName = "data type";
 1937  173
             if (!elements.contains(elementClass)) {
 1938  7
                 elements = null;
 1939   
             }
 1940   
         }
 1941   
 
 1942  178
         if (elements != null) {
 1943  171
             Enumeration e = elements.keys();
 1944  2318
             while (e.hasMoreElements()) {
 1945  2318
                 String name = (String) e.nextElement();
 1946  2318
                 Class clazz = (Class) elements.get(name);
 1947  2318
                 if (elementClass.equals(clazz)) {
 1948  171
                     return "The <" + name + "> " + typeName;
 1949   
                 }
 1950   
             }
 1951   
         }
 1952   
 
 1953  7
         return "Class " + elementClass.getName();
 1954   
     }
 1955   
 
 1956   
     /**
 1957   
      * Sends a "build started" event to the build listeners for this project.
 1958   
      */
 1959  1
     public void fireBuildStarted() {
 1960  1
         BuildEvent event = new BuildEvent(this);
 1961  1
         Vector listeners = getBuildListeners();
 1962  1
         for (int i = 0; i < listeners.size(); i++) {
 1963  1
             BuildListener listener = (BuildListener) listeners.elementAt(i);
 1964  1
             listener.buildStarted(event);
 1965   
         }
 1966   
     }
 1967   
 
 1968   
     /**
 1969   
      * Sends a "build finished" event to the build listeners for this project.
 1970   
      * @param exception an exception indicating a reason for a build
 1971   
      *                  failure. May be <code>null</code>, indicating
 1972   
      *                  a successful build.
 1973   
      */
 1974  2
     public void fireBuildFinished(Throwable exception) {
 1975  2
         BuildEvent event = new BuildEvent(this);
 1976  2
         event.setException(exception);
 1977  2
         Vector listeners = getBuildListeners();
 1978  2
         for (int i = 0; i < listeners.size(); i++) {
 1979  173
             BuildListener listener = (BuildListener) listeners.elementAt(i);
 1980  173
             listener.buildFinished(event);
 1981   
         }
 1982   
     }
 1983   
 
 1984   
 
 1985   
     /**
 1986   
      * Sends a "target started" event to the build listeners for this project.
 1987   
      *
 1988   
      * @param target The target which is starting to build.
 1989   
      *               Must not be <code>null</code>.
 1990   
      */
 1991  1041
     protected void fireTargetStarted(Target target) {
 1992  1041
         BuildEvent event = new BuildEvent(target);
 1993  1041
         Vector listeners = getBuildListeners();
 1994  1041
         for (int i = 0; i < listeners.size(); i++) {
 1995  1431
             BuildListener listener = (BuildListener) listeners.elementAt(i);
 1996  1431
             listener.targetStarted(event);
 1997   
         }
 1998   
     }
 1999   
 
 2000   
     /**
 2001   
      * Sends a "target finished" event to the build listeners for this
 2002   
      * project.
 2003   
      *
 2004   
      * @param target    The target which has finished building.
 2005   
      *                  Must not be <code>null</code>.
 2006   
      * @param exception an exception indicating a reason for a build
 2007   
      *                  failure. May be <code>null</code>, indicating
 2008   
      *                  a successful build.
 2009   
      */
 2010  1041
     protected void fireTargetFinished(Target target, Throwable exception) {
 2011  1041
         BuildEvent event = new BuildEvent(target);
 2012  1041
         event.setException(exception);
 2013  1041
         Vector listeners = getBuildListeners();
 2014  1041
         for (int i = 0; i < listeners.size(); i++) {
 2015  1651
             BuildListener listener = (BuildListener) listeners.elementAt(i);
 2016  1651
             listener.targetFinished(event);
 2017   
         }
 2018   
     }
 2019   
 
 2020   
     /**
 2021   
      * Sends a "task started" event to the build listeners for this project.
 2022   
      *
 2023   
      * @param task The target which is starting to execute.
 2024   
      *               Must not be <code>null</code>.
 2025   
      */
 2026  3274
     protected void fireTaskStarted(Task task) {
 2027   
         // register this as the current task on the current thread.
 2028  3274
         registerThreadTask(Thread.currentThread(), task);
 2029  3274
         BuildEvent event = new BuildEvent(task);
 2030  3274
         Vector listeners = getBuildListeners();
 2031  3274
         for (int i = 0; i < listeners.size(); i++) {
 2032  5132
             BuildListener listener = (BuildListener) listeners.elementAt(i);
 2033  5132
             listener.taskStarted(event);
 2034   
         }
 2035   
     }
 2036   
 
 2037   
     /**
 2038   
      * Sends a "task finished" event to the build listeners for this
 2039   
      * project.
 2040   
      *
 2041   
      * @param task      The task which has finished executing.
 2042   
      *                  Must not be <code>null</code>.
 2043   
      * @param exception an exception indicating a reason for a build
 2044   
      *                  failure. May be <code>null</code>, indicating
 2045   
      *                  a successful build.
 2046   
      */
 2047  3274
     protected void fireTaskFinished(Task task, Throwable exception) {
 2048  3274
         registerThreadTask(Thread.currentThread(), null);
 2049  3274
         System.out.flush();
 2050  3274
         System.err.flush();
 2051  3274
         BuildEvent event = new BuildEvent(task);
 2052  3274
         event.setException(exception);
 2053  3274
         Vector listeners = getBuildListeners();
 2054  3274
         for (int i = 0; i < listeners.size(); i++) {
 2055  5374
             BuildListener listener = (BuildListener) listeners.elementAt(i);
 2056  5374
             listener.taskFinished(event);
 2057   
         }
 2058   
     }
 2059   
 
 2060   
     /**
 2061   
      * Sends a "message logged" event to the build listeners for this project.
 2062   
      *
 2063   
      * @param event    The event to send. This should be built up with the
 2064   
      *                 appropriate task/target/project by the caller, so that
 2065   
      *                 this method can set the message and priority, then send
 2066   
      *                 the event. Must not be <code>null</code>.
 2067   
      * @param message  The message to send. Should not be <code>null</code>.
 2068   
      * @param priority The priority of the message.
 2069   
      */
 2070  104973
     private void fireMessageLoggedEvent(BuildEvent event, String message,
 2071   
                                         int priority) {
 2072  104973
         event.setMessage(message, priority);
 2073  104973
         Vector listeners = getBuildListeners();
 2074  104973
         synchronized(this) {
 2075  104973
             if (loggingMessage) {
 2076  0
                 throw new BuildException("Listener attempted to access " 
 2077   
                     + (priority == MSG_ERR ? "System.err" : "System.out") 
 2078   
                     + " - infinite loop terminated");
 2079   
             }
 2080  104973
             loggingMessage = true;                
 2081  104973
             for (int i = 0; i < listeners.size(); i++) {
 2082  993686
                 BuildListener listener = (BuildListener) listeners.elementAt(i);
 2083  993686
                 listener.messageLogged(event);
 2084   
             }
 2085  104973
             loggingMessage = false;
 2086   
         }
 2087   
     }
 2088   
 
 2089   
     /**
 2090   
      * Sends a "message logged" project level event to the build listeners for
 2091   
      * this project.
 2092   
      *
 2093   
      * @param project  The project generating the event.
 2094   
      *                 Should not be <code>null</code>.
 2095   
      * @param message  The message to send. Should not be <code>null</code>.
 2096   
      * @param priority The priority of the message.
 2097   
      */
 2098  95979
     protected void fireMessageLogged(Project project, String message,
 2099   
                                      int priority) {
 2100  95979
         BuildEvent event = new BuildEvent(project);
 2101  95979
         fireMessageLoggedEvent(event, message, priority);
 2102   
     }
 2103   
 
 2104   
     /**
 2105   
      * Sends a "message logged" target level event to the build listeners for
 2106   
      * this project.
 2107   
      *
 2108   
      * @param target   The target generating the event.
 2109   
      *                 Must not be <code>null</code>.
 2110   
      * @param message  The message to send. Should not be <code>null</code>.
 2111   
      * @param priority The priority of the message.
 2112   
      */
 2113  2
     protected void fireMessageLogged(Target target, String message,
 2114   
                                      int priority) {
 2115  2
         BuildEvent event = new BuildEvent(target);
 2116  2
         fireMessageLoggedEvent(event, message, priority);
 2117   
     }
 2118   
 
 2119   
     /**
 2120   
      * Sends a "message logged" task level event to the build listeners for
 2121   
      * this project.
 2122   
      *
 2123   
      * @param task     The task generating the event.
 2124   
      *                 Must not be <code>null</code>.
 2125   
      * @param message  The message to send. Should not be <code>null</code>.
 2126   
      * @param priority The priority of the message.
 2127   
      */
 2128  8992
     protected void fireMessageLogged(Task task, String message, int priority) {
 2129  8992
         BuildEvent event = new BuildEvent(task);
 2130  8992
         fireMessageLoggedEvent(event, message, priority);
 2131   
     }
 2132   
 
 2133   
     /**
 2134   
      * Register a task as the current task for a thread.
 2135   
      * If the task is null, the thread's entry is removed.
 2136   
      *
 2137   
      * @param thread the thread on which the task is registered.
 2138   
      * @param task the task to be registered.
 2139   
      * @since Ant 1.5
 2140   
      */
 2141  6550
     public synchronized void registerThreadTask(Thread thread, Task task) {
 2142  6550
         if (task != null) {
 2143  3274
             threadTasks.put(thread, task);
 2144  3274
             threadGroupTasks.put(thread.getThreadGroup(), task);
 2145   
         } else {
 2146  3276
             threadTasks.remove(thread);
 2147  3276
             threadGroupTasks.remove(thread.getThreadGroup());
 2148   
         }
 2149   
     }
 2150   
 
 2151   
     /**
 2152   
      * Get the current task assopciated with a thread, if any
 2153   
      *
 2154   
      * @param thread the thread for which the task is required.
 2155   
      * @return the task which is currently registered for the given thread or
 2156   
      *         null if no task is registered.
 2157   
      */
 2158  285
     public Task getThreadTask(Thread thread) {
 2159  285
         Task task = (Task) threadTasks.get(thread);
 2160  285
         if (task == null) {
 2161  2
             ThreadGroup group = thread.getThreadGroup();
 2162  2
             while (task == null && group != null) {
 2163  4
                 task = (Task) threadGroupTasks.get(group);
 2164  4
                 group = group.getParent();
 2165   
             }
 2166   
         }
 2167  285
         return task;
 2168   
     }
 2169   
 
 2170   
     
 2171   
     // Should move to a separate public class - and have API to add
 2172   
     // listeners, etc.
 2173   
     private static class AntRefTable extends Hashtable {
 2174   
         Project project;
 2175  684
         public AntRefTable(Project project) {
 2176  684
             super();
 2177  684
             this.project = project;
 2178   
         }
 2179   
 
 2180   
         /** Returns the unmodified original object.
 2181   
          * This method should be called internally to
 2182   
          * get the 'real' object.
 2183   
          * The normal get method will do the replacement
 2184   
          * of UnknownElement ( this is similar with the JDNI
 2185   
          * refs behavior )
 2186   
          */
 2187  3516
         public Object getReal(Object key) {
 2188  3516
             return super.get(key);
 2189   
         }
 2190   
 
 2191   
         /** Get method for the reference table.
 2192   
          *  It can be used to hook dynamic references and to modify
 2193   
          * some references on the fly - for example for delayed
 2194   
          * evaluation.
 2195   
          *
 2196   
          * It is important to make sure that the processing that is
 2197   
          * done inside is not calling get indirectly.
 2198   
          *
 2199   
          * @param key
 2200   
          * @return
 2201   
          */
 2202  433346
         public Object get(Object key) {
 2203   
             //System.out.println("AntRefTable.get " + key);
 2204  433346
             Object o = super.get(key);
 2205  433346
             if (o instanceof UnknownElement) {
 2206   
                 // Make sure that
 2207  2
                 ((UnknownElement) o).maybeConfigure();
 2208  2
                 o = ((UnknownElement) o).getTask();
 2209   
             }
 2210  433346
             return o;
 2211   
         }
 2212   
     }
 2213   
 
 2214   
     private static class AntTaskTable extends LazyHashtable {
 2215   
         Project project;
 2216   
         Properties props;
 2217   
         boolean tasks = false;
 2218   
 
 2219  1368
         public AntTaskTable(Project p, boolean tasks) {
 2220  1368
             this.project = p;
 2221  1368
             this.tasks = tasks;
 2222   
         }
 2223   
 
 2224  1102
         public void addDefinitions(Properties props) {
 2225  1102
             this.props = props;
 2226   
         }
 2227   
 
 2228  1014
         protected void initAll() {
 2229  578
             if (initAllDone ) return;
 2230  436
             project.log("InitAll", Project.MSG_DEBUG);
 2231  127
             if (props==null ) return;
 2232  309
             Enumeration enum = props.propertyNames();
 2233  309
             while (enum.hasMoreElements()) {
 2234  20941
                 String key = (String) enum.nextElement();
 2235  20941
                 Class taskClass=getTask( key );
 2236  20941
                 if (taskClass!=null ) {
 2237   
                     // This will call a get() and a put()
 2238  20395
                     if (tasks )
 2239  16471
                         project.addTaskDefinition(key, taskClass);
 2240   
                     else
 2241  3924
                         project.addDataTypeDefinition(key, taskClass );
 2242   
                 }
 2243   
             }
 2244  309
             initAllDone=true;
 2245   
         }
 2246   
 
 2247  52545
         protected Class getTask(String key) {
 2248  9980
             if (props==null ) return null; // for tasks loaded before init()
 2249  42565
             String value=props.getProperty(key);
 2250  42565
             if (value==null) {
 2251   
                 //project.log( "No class name for " + key, Project.MSG_VERBOSE );
 2252  194
                 return null;
 2253   
             }
 2254  42371
             try {
 2255  42371
                 Class taskClass=null;
 2256  42371
                 if (project.getCoreLoader() != null &&
 2257   
                     !("only".equals(project.getProperty("build.sysclasspath")))) {
 2258  0
                     try {
 2259  0
                         taskClass=project.getCoreLoader().loadClass(value);
 2260  0
                         if (taskClass != null ) return taskClass;
 2261   
                     } catch( Exception ex ) {
 2262   
                     }
 2263   
                 }
 2264  42371
                 taskClass = Class.forName(value);
 2265  41825
                 return taskClass;
 2266   
             } catch (NoClassDefFoundError ncdfe) {
 2267  0
                 project.log("Could not load a dependent class ("
 2268   
                         + ncdfe.getMessage() + ") for task " + key, Project.MSG_DEBUG);
 2269   
             } catch (ClassNotFoundException cnfe) {
 2270  546
                 project.log("Could not load class (" + value
 2271   
                         + ") for task " + key, Project.MSG_DEBUG);
 2272   
             }
 2273  546
             return null;
 2274   
         }
 2275   
 
 2276   
         // Hashtable implementation
 2277  48754
         public Object get( Object key ) {
 2278  48754
             Object orig=super.get( key );
 2279  17150
             if (orig!= null ) return orig;
 2280  0
             if (! (key instanceof String) ) return null;
 2281  31604
             project.log("Get task " + key, Project.MSG_DEBUG );
 2282  31604
             Object taskClass=getTask( (String) key);
 2283  31604
             if (taskClass != null)
 2284  21430
                 super.put( key, taskClass );
 2285  31604
             return taskClass;
 2286   
         }
 2287   
 
 2288  10
         public boolean containsKey(Object key) {
 2289  10
             return get(key) != null;
 2290   
         }
 2291   
 
 2292   
     }
 2293   
 }
 2294