0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: */
0018:
0019: package org.apache.tools.ant;
0020:
0021: import java.io.File;
0022: import java.io.IOException;
0023: import java.io.EOFException;
0024: import java.io.InputStream;
0025: import java.lang.reflect.Method;
0026: import java.lang.reflect.Modifier;
0027: import java.util.Collections;
0028: import java.util.Enumeration;
0029: import java.util.Hashtable;
0030: import java.util.Iterator;
0031: import java.util.Properties;
0032: import java.util.Stack;
0033: import java.util.Vector;
0034: import java.util.Set;
0035: import java.util.HashSet;
0036: import java.util.HashMap;
0037: import java.util.Map;
0038: import java.util.WeakHashMap;
0039: import org.apache.tools.ant.input.DefaultInputHandler;
0040: import org.apache.tools.ant.input.InputHandler;
0041: import org.apache.tools.ant.helper.DefaultExecutor;
0042: import org.apache.tools.ant.types.FilterSet;
0043: import org.apache.tools.ant.types.FilterSetCollection;
0044: import org.apache.tools.ant.types.Description;
0045: import org.apache.tools.ant.types.Path;
0046: import org.apache.tools.ant.types.Resource;
0047: import org.apache.tools.ant.types.ResourceFactory;
0048: import org.apache.tools.ant.types.resources.FileResource;
0049: import org.apache.tools.ant.util.FileUtils;
0050: import org.apache.tools.ant.util.JavaEnvUtils;
0051: import org.apache.tools.ant.util.StringUtils;
0052:
0053: /**
0054: * Central representation of an Ant project. This class defines an
0055: * Ant project with all of its targets, tasks and various other
0056: * properties. It also provides the mechanism to kick off a build using
0057: * a particular target name.
0058: * <p>
0059: * This class also encapsulates methods which allow files to be referred
0060: * to using abstract path names which are translated to native system
0061: * file paths at runtime.
0062: *
0063: */
0064: public class Project implements ResourceFactory {
0065: private static final String LINE_SEP = System
0066: .getProperty("line.separator");
0067:
0068: /** Message priority of "error". */
0069: public static final int MSG_ERR = 0;
0070: /** Message priority of "warning". */
0071: public static final int MSG_WARN = 1;
0072: /** Message priority of "information". */
0073: public static final int MSG_INFO = 2;
0074: /** Message priority of "verbose". */
0075: public static final int MSG_VERBOSE = 3;
0076: /** Message priority of "debug". */
0077: public static final int MSG_DEBUG = 4;
0078:
0079: /**
0080: * Constant for the "visiting" state, used when
0081: * traversing a DFS of target dependencies.
0082: */
0083: private static final String VISITING = "VISITING";
0084: /**
0085: * Constant for the "visited" state, used when
0086: * traversing a DFS of target dependencies.
0087: */
0088: private static final String VISITED = "VISITED";
0089:
0090: /**
0091: * Version constant for Java 1.0 .
0092: *
0093: * @deprecated since 1.5.x.
0094: * Use {@link JavaEnvUtils#JAVA_1_0} instead.
0095: */
0096: public static final String JAVA_1_0 = JavaEnvUtils.JAVA_1_0;
0097: /**
0098: * Version constant for Java 1.1 .
0099: *
0100: * @deprecated since 1.5.x.
0101: * Use {@link JavaEnvUtils#JAVA_1_1} instead.
0102: */
0103: public static final String JAVA_1_1 = JavaEnvUtils.JAVA_1_1;
0104: /**
0105: * Version constant for Java 1.2 .
0106: *
0107: * @deprecated since 1.5.x.
0108: * Use {@link JavaEnvUtils#JAVA_1_2} instead.
0109: */
0110: public static final String JAVA_1_2 = JavaEnvUtils.JAVA_1_2;
0111: /**
0112: * Version constant for Java 1.3 .
0113: *
0114: * @deprecated since 1.5.x.
0115: * Use {@link JavaEnvUtils#JAVA_1_3} instead.
0116: */
0117: public static final String JAVA_1_3 = JavaEnvUtils.JAVA_1_3;
0118: /**
0119: * Version constant for Java 1.4 .
0120: *
0121: * @deprecated since 1.5.x.
0122: * Use {@link JavaEnvUtils#JAVA_1_4} instead.
0123: */
0124: public static final String JAVA_1_4 = JavaEnvUtils.JAVA_1_4;
0125:
0126: /** Default filter start token. */
0127: public static final String TOKEN_START = FilterSet.DEFAULT_TOKEN_START;
0128: /** Default filter end token. */
0129: public static final String TOKEN_END = FilterSet.DEFAULT_TOKEN_END;
0130:
0131: /** Instance of a utility class to use for file operations. */
0132: private static final FileUtils FILE_UTILS = FileUtils
0133: .getFileUtils();
0134:
0135: /** Name of this project. */
0136: private String name;
0137: /** Description for this project (if any). */
0138: private String description;
0139:
0140: /** Map of references within the project (paths etc) (String to Object). */
0141: private Hashtable references = new AntRefTable();
0142:
0143: /** Map of id references - used for indicating broken build files */
0144: private HashMap idReferences = new HashMap();
0145:
0146: /** the parent project for old id resolution (if inheritreferences is set) */
0147: private Project parentIdProject = null;
0148:
0149: /** Name of the project's default target. */
0150: private String defaultTarget;
0151:
0152: /** Map from target names to targets (String to Target). */
0153: private Hashtable targets = new Hashtable();
0154: /** Set of global filters. */
0155: private FilterSet globalFilterSet = new FilterSet();
0156: {
0157: // Initialize the globalFileSet's project
0158: globalFilterSet.setProject(this );
0159: }
0160:
0161: /**
0162: * Wrapper around globalFilterSet. This collection only ever
0163: * contains one FilterSet, but the wrapper is needed in order to
0164: * make it easier to use the FileUtils interface.
0165: */
0166: private FilterSetCollection globalFilters = new FilterSetCollection(
0167: globalFilterSet);
0168:
0169: /** Project base directory. */
0170: private File baseDir;
0171:
0172: /** List of listeners to notify of build events. */
0173: private Vector listeners = new Vector();
0174:
0175: /**
0176: * The Ant core classloader--may be <code>null</code> if using
0177: * parent classloader.
0178: */
0179: private ClassLoader coreLoader = null;
0180:
0181: /** Records the latest task to be executed on a thread. */
0182: private Map/*<Thread,Task>*/threadTasks = Collections
0183: .synchronizedMap(new WeakHashMap());
0184:
0185: /** Records the latest task to be executed on a thread group. */
0186: private Map/*<ThreadGroup,Task>*/threadGroupTasks = Collections
0187: .synchronizedMap(new WeakHashMap());
0188:
0189: /**
0190: * Called to handle any input requests.
0191: */
0192: private InputHandler inputHandler = null;
0193:
0194: /**
0195: * The default input stream used to read any input.
0196: */
0197: private InputStream defaultInputStream = null;
0198:
0199: /**
0200: * Keep going flag.
0201: */
0202: private boolean keepGoingMode = false;
0203:
0204: /**
0205: * Flag which catches Listeners which try to use System.out or System.err .
0206: */
0207: private boolean loggingMessage = false;
0208:
0209: /**
0210: * Set the input handler.
0211: *
0212: * @param handler the InputHandler instance to use for gathering input.
0213: */
0214: public void setInputHandler(InputHandler handler) {
0215: inputHandler = handler;
0216: }
0217:
0218: /**
0219: * Set the default System input stream. Normally this stream is set to
0220: * System.in. This inputStream is used when no task input redirection is
0221: * being performed.
0222: *
0223: * @param defaultInputStream the default input stream to use when input
0224: * is requested.
0225: * @since Ant 1.6
0226: */
0227: public void setDefaultInputStream(InputStream defaultInputStream) {
0228: this .defaultInputStream = defaultInputStream;
0229: }
0230:
0231: /**
0232: * Get this project's input stream.
0233: *
0234: * @return the InputStream instance in use by this Project instance to
0235: * read input.
0236: */
0237: public InputStream getDefaultInputStream() {
0238: return defaultInputStream;
0239: }
0240:
0241: /**
0242: * Retrieve the current input handler.
0243: *
0244: * @return the InputHandler instance currently in place for the project
0245: * instance.
0246: */
0247: public InputHandler getInputHandler() {
0248: return inputHandler;
0249: }
0250:
0251: /**
0252: * Create a new Ant project.
0253: */
0254: public Project() {
0255: inputHandler = new DefaultInputHandler();
0256: }
0257:
0258: /**
0259: * Create and initialize a subproject. By default the subproject will be of
0260: * the same type as its parent. If a no-arg constructor is unavailable, the
0261: * <code>Project</code> class will be used.
0262: * @return a Project instance configured as a subproject of this Project.
0263: * @since Ant 1.7
0264: */
0265: public Project createSubProject() {
0266: Project subProject = null;
0267: try {
0268: subProject = (Project) (getClass().newInstance());
0269: } catch (Exception e) {
0270: subProject = new Project();
0271: }
0272: initSubProject(subProject);
0273: return subProject;
0274: }
0275:
0276: /**
0277: * Initialize a subproject.
0278: * @param subProject the subproject to initialize.
0279: */
0280: public void initSubProject(Project subProject) {
0281: ComponentHelper.getComponentHelper(subProject).initSubProject(
0282: ComponentHelper.getComponentHelper(this ));
0283: subProject.setDefaultInputStream(getDefaultInputStream());
0284: subProject.setKeepGoingMode(this .isKeepGoingMode());
0285: subProject.setExecutor(getExecutor().getSubProjectExecutor());
0286: }
0287:
0288: /**
0289: * Initialise the project.
0290: *
0291: * This involves setting the default task definitions and loading the
0292: * system properties.
0293: *
0294: * @exception BuildException if the default task list cannot be loaded.
0295: */
0296: public void init() throws BuildException {
0297: initProperties();
0298:
0299: ComponentHelper.getComponentHelper(this )
0300: .initDefaultDefinitions();
0301: }
0302:
0303: /**
0304: * Initializes the properties.
0305: * @exception BuildException if an vital property could not be set.
0306: * @since Ant 1.7
0307: */
0308: public void initProperties() throws BuildException {
0309: setJavaVersionProperty();
0310: setSystemProperties();
0311: setPropertyInternal(MagicNames.ANT_VERSION, Main
0312: .getAntVersion());
0313: setAntLib();
0314: }
0315:
0316: private void setAntLib() {
0317: File antlib = org.apache.tools.ant.launch.Locator
0318: .getClassSource(Project.class);
0319: if (antlib != null) {
0320: setPropertyInternal(MagicNames.ANT_LIB, antlib
0321: .getAbsolutePath());
0322: }
0323: }
0324:
0325: /**
0326: * Factory method to create a class loader for loading classes from
0327: * a given path.
0328: *
0329: * @param path the path from which classes are to be loaded.
0330: *
0331: * @return an appropriate classloader.
0332: */
0333: public AntClassLoader createClassLoader(Path path) {
0334: return new AntClassLoader(getClass().getClassLoader(), this ,
0335: path);
0336: }
0337:
0338: /**
0339: * Factory method to create a class loader for loading classes from
0340: * a given path.
0341: *
0342: * @param parent the parent classloader for the new loader.
0343: * @param path the path from which classes are to be loaded.
0344: *
0345: * @return an appropriate classloader.
0346: */
0347: public AntClassLoader createClassLoader(ClassLoader parent,
0348: Path path) {
0349: return new AntClassLoader(parent, this , path);
0350: }
0351:
0352: /**
0353: * Set the core classloader for the project. If a <code>null</code>
0354: * classloader is specified, the parent classloader should be used.
0355: *
0356: * @param coreLoader The classloader to use for the project.
0357: * May be <code>null</code>.
0358: */
0359: public void setCoreLoader(ClassLoader coreLoader) {
0360: this .coreLoader = coreLoader;
0361: }
0362:
0363: /**
0364: * Return the core classloader to use for this project.
0365: * This may be <code>null</code>, indicating that
0366: * the parent classloader should be used.
0367: *
0368: * @return the core classloader to use for this project.
0369: *
0370: */
0371: public ClassLoader getCoreLoader() {
0372: return coreLoader;
0373: }
0374:
0375: /**
0376: * Add a build listener to the list. This listener will
0377: * be notified of build events for this project.
0378: *
0379: * @param listener The listener to add to the list.
0380: * Must not be <code>null</code>.
0381: */
0382: public synchronized void addBuildListener(BuildListener listener) {
0383: // If the listeners already has this listener, do nothing
0384: if (listeners.contains(listener)) {
0385: return;
0386: }
0387: // create a new Vector to avoid ConcurrentModificationExc when
0388: // the listeners get added/removed while we are in fire
0389: Vector newListeners = getBuildListeners();
0390: newListeners.addElement(listener);
0391: listeners = newListeners;
0392: }
0393:
0394: /**
0395: * Remove a build listener from the list. This listener
0396: * will no longer be notified of build events for this project.
0397: *
0398: * @param listener The listener to remove from the list.
0399: * Should not be <code>null</code>.
0400: */
0401: public synchronized void removeBuildListener(BuildListener listener) {
0402: // create a new Vector to avoid ConcurrentModificationExc when
0403: // the listeners get added/removed while we are in fire
0404: Vector newListeners = getBuildListeners();
0405: newListeners.removeElement(listener);
0406: listeners = newListeners;
0407: }
0408:
0409: /**
0410: * Return a copy of the list of build listeners for the project.
0411: *
0412: * @return a list of build listeners for the project
0413: */
0414: public Vector getBuildListeners() {
0415: return (Vector) listeners.clone();
0416: }
0417:
0418: /**
0419: * Write a message to the log with the default log level
0420: * of MSG_INFO .
0421: * @param message The text to log. Should not be <code>null</code>.
0422: */
0423:
0424: public void log(String message) {
0425: log(message, MSG_INFO);
0426: }
0427:
0428: /**
0429: * Write a project level message to the log with the given log level.
0430: * @param message The text to log. Should not be <code>null</code>.
0431: * @param msgLevel The log priority level to use.
0432: */
0433: public void log(String message, int msgLevel) {
0434: log(message, null, msgLevel);
0435: }
0436:
0437: /**
0438: * Write a project level message to the log with the given log level.
0439: * @param message The text to log. Should not be <code>null</code>.
0440: * @param throwable The exception causing this log, may be <code>null</code>.
0441: * @param msgLevel The log priority level to use.
0442: * @since 1.7
0443: */
0444: public void log(String message, Throwable throwable, int msgLevel) {
0445: fireMessageLogged(this , message, throwable, msgLevel);
0446: }
0447:
0448: /**
0449: * Write a task level message to the log with the given log level.
0450: * @param task The task to use in the log. Must not be <code>null</code>.
0451: * @param message The text to log. Should not be <code>null</code>.
0452: * @param msgLevel The log priority level to use.
0453: */
0454: public void log(Task task, String message, int msgLevel) {
0455: fireMessageLogged(task, message, null, msgLevel);
0456: }
0457:
0458: /**
0459: * Write a task level message to the log with the given log level.
0460: * @param task The task to use in the log. Must not be <code>null</code>.
0461: * @param message The text to log. Should not be <code>null</code>.
0462: * @param throwable The exception causing this log, may be <code>null</code>.
0463: * @param msgLevel The log priority level to use.
0464: * @since 1.7
0465: */
0466: public void log(Task task, String message, Throwable throwable,
0467: int msgLevel) {
0468: fireMessageLogged(task, message, throwable, msgLevel);
0469: }
0470:
0471: /**
0472: * Write a target level message to the log with the given log level.
0473: * @param target The target to use in the log.
0474: * Must not be <code>null</code>.
0475: * @param message The text to log. Should not be <code>null</code>.
0476: * @param msgLevel The log priority level to use.
0477: */
0478: public void log(Target target, String message, int msgLevel) {
0479: log(target, message, null, msgLevel);
0480: }
0481:
0482: /**
0483: * Write a target level message to the log with the given log level.
0484: * @param target The target to use in the log.
0485: * Must not be <code>null</code>.
0486: * @param message The text to log. Should not be <code>null</code>.
0487: * @param throwable The exception causing this log, may be <code>null</code>.
0488: * @param msgLevel The log priority level to use.
0489: * @since 1.7
0490: */
0491: public void log(Target target, String message, Throwable throwable,
0492: int msgLevel) {
0493: fireMessageLogged(target, message, throwable, msgLevel);
0494: }
0495:
0496: /**
0497: * Return the set of global filters.
0498: *
0499: * @return the set of global filters.
0500: */
0501: public FilterSet getGlobalFilterSet() {
0502: return globalFilterSet;
0503: }
0504:
0505: /**
0506: * Set a property. Any existing property of the same name
0507: * is overwritten, unless it is a user property.
0508: * @param name The name of property to set.
0509: * Must not be <code>null</code>.
0510: * @param value The new value of the property.
0511: * Must not be <code>null</code>.
0512: */
0513: public void setProperty(String name, String value) {
0514: PropertyHelper.getPropertyHelper(this ).setProperty(null, name,
0515: value, true);
0516: }
0517:
0518: /**
0519: * Set a property if no value currently exists. If the property
0520: * exists already, a message is logged and the method returns with
0521: * no other effect.
0522: *
0523: * @param name The name of property to set.
0524: * Must not be <code>null</code>.
0525: * @param value The new value of the property.
0526: * Must not be <code>null</code>.
0527: * @since 1.5
0528: */
0529: public void setNewProperty(String name, String value) {
0530: PropertyHelper.getPropertyHelper(this ).setNewProperty(null,
0531: name, value);
0532: }
0533:
0534: /**
0535: * Set a user property, which cannot be overwritten by
0536: * set/unset property calls. Any previous value is overwritten.
0537: * @param name The name of property to set.
0538: * Must not be <code>null</code>.
0539: * @param value The new value of the property.
0540: * Must not be <code>null</code>.
0541: * @see #setProperty(String,String)
0542: */
0543: public void setUserProperty(String name, String value) {
0544: PropertyHelper.getPropertyHelper(this ).setUserProperty(null,
0545: name, value);
0546: }
0547:
0548: /**
0549: * Set a user property, which cannot be overwritten by set/unset
0550: * property calls. Any previous value is overwritten. Also marks
0551: * these properties as properties that have not come from the
0552: * command line.
0553: *
0554: * @param name The name of property to set.
0555: * Must not be <code>null</code>.
0556: * @param value The new value of the property.
0557: * Must not be <code>null</code>.
0558: * @see #setProperty(String,String)
0559: */
0560: public void setInheritedProperty(String name, String value) {
0561: PropertyHelper ph = PropertyHelper.getPropertyHelper(this );
0562: ph.setInheritedProperty(null, name, value);
0563: }
0564:
0565: /**
0566: * Set a property unless it is already defined as a user property
0567: * (in which case the method returns silently).
0568: *
0569: * @param name The name of the property.
0570: * Must not be <code>null</code>.
0571: * @param value The property value. Must not be <code>null</code>.
0572: */
0573: private void setPropertyInternal(String name, String value) {
0574: PropertyHelper ph = PropertyHelper.getPropertyHelper(this );
0575: ph.setProperty(null, name, value, false);
0576: }
0577:
0578: /**
0579: * Return the value of a property, if it is set.
0580: *
0581: * @param propertyName The name of the property.
0582: * May be <code>null</code>, in which case
0583: * the return value is also <code>null</code>.
0584: * @return the property value, or <code>null</code> for no match
0585: * or if a <code>null</code> name is provided.
0586: */
0587: public String getProperty(String propertyName) {
0588: PropertyHelper ph = PropertyHelper.getPropertyHelper(this );
0589: return (String) ph.getProperty(null, propertyName);
0590: }
0591:
0592: /**
0593: * Replace ${} style constructions in the given value with the
0594: * string value of the corresponding data types.
0595: *
0596: * @param value The string to be scanned for property references.
0597: * May be <code>null</code>.
0598: *
0599: * @return the given string with embedded property names replaced
0600: * by values, or <code>null</code> if the given string is
0601: * <code>null</code>.
0602: *
0603: * @exception BuildException if the given value has an unclosed
0604: * property name, e.g. <code>${xxx</code>.
0605: */
0606: public String replaceProperties(String value) throws BuildException {
0607: PropertyHelper ph = PropertyHelper.getPropertyHelper(this );
0608: return ph.replaceProperties(null, value, null);
0609: }
0610:
0611: /**
0612: * Return the value of a user property, if it is set.
0613: *
0614: * @param propertyName The name of the property.
0615: * May be <code>null</code>, in which case
0616: * the return value is also <code>null</code>.
0617: * @return the property value, or <code>null</code> for no match
0618: * or if a <code>null</code> name is provided.
0619: */
0620: public String getUserProperty(String propertyName) {
0621: PropertyHelper ph = PropertyHelper.getPropertyHelper(this );
0622: return (String) ph.getUserProperty(null, propertyName);
0623: }
0624:
0625: /**
0626: * Return a copy of the properties table.
0627: * @return a hashtable containing all properties
0628: * (including user properties).
0629: */
0630: public Hashtable getProperties() {
0631: PropertyHelper ph = PropertyHelper.getPropertyHelper(this );
0632: return ph.getProperties();
0633: }
0634:
0635: /**
0636: * Return a copy of the user property hashtable.
0637: * @return a hashtable containing just the user properties.
0638: */
0639: public Hashtable getUserProperties() {
0640: PropertyHelper ph = PropertyHelper.getPropertyHelper(this );
0641: return ph.getUserProperties();
0642: }
0643:
0644: /**
0645: * Copy all user properties that have been set on the command
0646: * line or a GUI tool from this instance to the Project instance
0647: * given as the argument.
0648: *
0649: * <p>To copy all "user" properties, you will also have to call
0650: * {@link #copyInheritedProperties copyInheritedProperties}.</p>
0651: *
0652: * @param other the project to copy the properties to. Must not be null.
0653: *
0654: * @since Ant 1.5
0655: */
0656: public void copyUserProperties(Project other) {
0657: PropertyHelper ph = PropertyHelper.getPropertyHelper(this );
0658: ph.copyUserProperties(other);
0659: }
0660:
0661: /**
0662: * Copy all user properties that have not been set on the
0663: * command line or a GUI tool from this instance to the Project
0664: * instance given as the argument.
0665: *
0666: * <p>To copy all "user" properties, you will also have to call
0667: * {@link #copyUserProperties copyUserProperties}.</p>
0668: *
0669: * @param other the project to copy the properties to. Must not be null.
0670: *
0671: * @since Ant 1.5
0672: */
0673: public void copyInheritedProperties(Project other) {
0674: PropertyHelper ph = PropertyHelper.getPropertyHelper(this );
0675: ph.copyInheritedProperties(other);
0676: }
0677:
0678: /**
0679: * Set the default target of the project.
0680: *
0681: * @param defaultTarget The name of the default target for this project.
0682: * May be <code>null</code>, indicating that there is
0683: * no default target.
0684: *
0685: * @deprecated since 1.5.x.
0686: * Use setDefault.
0687: * @see #setDefault(String)
0688: */
0689: public void setDefaultTarget(String defaultTarget) {
0690: this .defaultTarget = defaultTarget;
0691: }
0692:
0693: /**
0694: * Return the name of the default target of the project.
0695: * @return name of the default target or
0696: * <code>null</code> if no default has been set.
0697: */
0698: public String getDefaultTarget() {
0699: return defaultTarget;
0700: }
0701:
0702: /**
0703: * Set the default target of the project.
0704: *
0705: * @param defaultTarget The name of the default target for this project.
0706: * May be <code>null</code>, indicating that there is
0707: * no default target.
0708: */
0709: public void setDefault(String defaultTarget) {
0710: this .defaultTarget = defaultTarget;
0711: }
0712:
0713: /**
0714: * Set the name of the project, also setting the user
0715: * property <code>ant.project.name</code>.
0716: *
0717: * @param name The name of the project.
0718: * Must not be <code>null</code>.
0719: */
0720: public void setName(String name) {
0721: setUserProperty("ant.project.name", name);
0722: this .name = name;
0723: }
0724:
0725: /**
0726: * Return the project name, if one has been set.
0727: *
0728: * @return the project name, or <code>null</code> if it hasn't been set.
0729: */
0730: public String getName() {
0731: return name;
0732: }
0733:
0734: /**
0735: * Set the project description.
0736: *
0737: * @param description The description of the project.
0738: * May be <code>null</code>.
0739: */
0740: public void setDescription(String description) {
0741: this .description = description;
0742: }
0743:
0744: /**
0745: * Return the project description, if one has been set.
0746: *
0747: * @return the project description, or <code>null</code> if it hasn't
0748: * been set.
0749: */
0750: public String getDescription() {
0751: if (description == null) {
0752: description = Description.getDescription(this );
0753: }
0754: return description;
0755: }
0756:
0757: /**
0758: * Add a filter to the set of global filters.
0759: *
0760: * @param token The token to filter.
0761: * Must not be <code>null</code>.
0762: * @param value The replacement value.
0763: * Must not be <code>null</code>.
0764: * @deprecated since 1.4.x.
0765: * Use getGlobalFilterSet().addFilter(token,value)
0766: *
0767: * @see #getGlobalFilterSet()
0768: * @see FilterSet#addFilter(String,String)
0769: */
0770: public void addFilter(String token, String value) {
0771: if (token == null) {
0772: return;
0773: }
0774: globalFilterSet.addFilter(new FilterSet.Filter(token, value));
0775: }
0776:
0777: /**
0778: * Return a hashtable of global filters, mapping tokens to values.
0779: *
0780: * @return a hashtable of global filters, mapping tokens to values
0781: * (String to String).
0782: *
0783: * @deprecated since 1.4.x
0784: * Use getGlobalFilterSet().getFilterHash().
0785: *
0786: * @see #getGlobalFilterSet()
0787: * @see FilterSet#getFilterHash()
0788: */
0789: public Hashtable getFilters() {
0790: // we need to build the hashtable dynamically
0791: return globalFilterSet.getFilterHash();
0792: }
0793:
0794: /**
0795: * Set the base directory for the project, checking that
0796: * the given filename exists and is a directory.
0797: *
0798: * @param baseD The project base directory.
0799: * Must not be <code>null</code>.
0800: *
0801: * @exception BuildException if the directory if invalid.
0802: */
0803: public void setBasedir(String baseD) throws BuildException {
0804: setBaseDir(new File(baseD));
0805: }
0806:
0807: /**
0808: * Set the base directory for the project, checking that
0809: * the given file exists and is a directory.
0810: *
0811: * @param baseDir The project base directory.
0812: * Must not be <code>null</code>.
0813: * @exception BuildException if the specified file doesn't exist or
0814: * isn't a directory.
0815: */
0816: public void setBaseDir(File baseDir) throws BuildException {
0817: baseDir = FILE_UTILS.normalize(baseDir.getAbsolutePath());
0818: if (!baseDir.exists()) {
0819: throw new BuildException("Basedir "
0820: + baseDir.getAbsolutePath() + " does not exist");
0821: }
0822: if (!baseDir.isDirectory()) {
0823: throw new BuildException("Basedir "
0824: + baseDir.getAbsolutePath() + " is not a directory");
0825: }
0826: this .baseDir = baseDir;
0827: setPropertyInternal(MagicNames.PROJECT_BASEDIR, this .baseDir
0828: .getPath());
0829: String msg = "Project base dir set to: " + this .baseDir;
0830: log(msg, MSG_VERBOSE);
0831: }
0832:
0833: /**
0834: * Return the base directory of the project as a file object.
0835: *
0836: * @return the project base directory, or <code>null</code> if the
0837: * base directory has not been successfully set to a valid value.
0838: */
0839: public File getBaseDir() {
0840: if (baseDir == null) {
0841: try {
0842: setBasedir(".");
0843: } catch (BuildException ex) {
0844: ex.printStackTrace();
0845: }
0846: }
0847: return baseDir;
0848: }
0849:
0850: /**
0851: * Set "keep-going" mode. In this mode Ant will try to execute
0852: * as many targets as possible. All targets that do not depend
0853: * on failed target(s) will be executed. If the keepGoing settor/getter
0854: * methods are used in conjunction with the <code>ant.executor.class</code>
0855: * property, they will have no effect.
0856: * @param keepGoingMode "keep-going" mode
0857: * @since Ant 1.6
0858: */
0859: public void setKeepGoingMode(boolean keepGoingMode) {
0860: this .keepGoingMode = keepGoingMode;
0861: }
0862:
0863: /**
0864: * Return the keep-going mode. If the keepGoing settor/getter
0865: * methods are used in conjunction with the <code>ant.executor.class</code>
0866: * property, they will have no effect.
0867: * @return "keep-going" mode
0868: * @since Ant 1.6
0869: */
0870: public boolean isKeepGoingMode() {
0871: return this .keepGoingMode;
0872: }
0873:
0874: /**
0875: * Return the version of Java this class is running under.
0876: * @return the version of Java as a String, e.g. "1.1" .
0877: * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
0878: * @deprecated since 1.5.x.
0879: * Use org.apache.tools.ant.util.JavaEnvUtils instead.
0880: */
0881: public static String getJavaVersion() {
0882: return JavaEnvUtils.getJavaVersion();
0883: }
0884:
0885: /**
0886: * Set the <code>ant.java.version</code> property and tests for
0887: * unsupported JVM versions. If the version is supported,
0888: * verbose log messages are generated to record the Java version
0889: * and operating system name.
0890: *
0891: * @exception BuildException if this Java version is not supported.
0892: *
0893: * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
0894: */
0895: public void setJavaVersionProperty() throws BuildException {
0896: String javaVersion = JavaEnvUtils.getJavaVersion();
0897: setPropertyInternal(MagicNames.ANT_JAVA_VERSION, javaVersion);
0898:
0899: // sanity check
0900: if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_0)
0901: || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
0902: throw new BuildException(
0903: "Ant cannot work on Java 1.0 / 1.1");
0904: }
0905: log("Detected Java version: " + javaVersion + " in: "
0906: + System.getProperty("java.home"), MSG_VERBOSE);
0907:
0908: log("Detected OS: " + System.getProperty("os.name"),
0909: MSG_VERBOSE);
0910: }
0911:
0912: /**
0913: * Add all system properties which aren't already defined as
0914: * user properties to the project properties.
0915: */
0916: public void setSystemProperties() {
0917: Properties systemP = System.getProperties();
0918: Enumeration e = systemP.propertyNames();
0919: while (e.hasMoreElements()) {
0920: String propertyName = (String) e.nextElement();
0921: String value = systemP.getProperty(propertyName);
0922: this .setPropertyInternal(propertyName, value);
0923: }
0924: }
0925:
0926: /**
0927: * Add a new task definition to the project.
0928: * Attempting to override an existing definition with an
0929: * equivalent one (i.e. with the same classname) results in
0930: * a verbose log message. Attempting to override an existing definition
0931: * with a different one results in a warning log message and
0932: * invalidates any tasks which have already been created with the
0933: * old definition.
0934: *
0935: * @param taskName The name of the task to add.
0936: * Must not be <code>null</code>.
0937: * @param taskClass The full name of the class implementing the task.
0938: * Must not be <code>null</code>.
0939: *
0940: * @exception BuildException if the class is unsuitable for being an Ant
0941: * task. An error level message is logged before
0942: * this exception is thrown.
0943: *
0944: * @see #checkTaskClass(Class)
0945: */
0946: public void addTaskDefinition(String taskName, Class taskClass)
0947: throws BuildException {
0948: ComponentHelper.getComponentHelper(this ).addTaskDefinition(
0949: taskName, taskClass);
0950: }
0951:
0952: /**
0953: * Check whether or not a class is suitable for serving as Ant task.
0954: * Ant task implementation classes must be public, concrete, and have
0955: * a no-arg constructor.
0956: *
0957: * @param taskClass The class to be checked.
0958: * Must not be <code>null</code>.
0959: *
0960: * @exception BuildException if the class is unsuitable for being an Ant
0961: * task. An error level message is logged before
0962: * this exception is thrown.
0963: */
0964: public void checkTaskClass(final Class taskClass)
0965: throws BuildException {
0966: ComponentHelper.getComponentHelper(this ).checkTaskClass(
0967: taskClass);
0968:
0969: if (!Modifier.isPublic(taskClass.getModifiers())) {
0970: final String message = taskClass + " is not public";
0971: log(message, Project.MSG_ERR);
0972: throw new BuildException(message);
0973: }
0974: if (Modifier.isAbstract(taskClass.getModifiers())) {
0975: final String message = taskClass + " is abstract";
0976: log(message, Project.MSG_ERR);
0977: throw new BuildException(message);
0978: }
0979: try {
0980: taskClass.getConstructor((Class[]) null);
0981: // don't have to check for public, since
0982: // getConstructor finds public constructors only.
0983: } catch (NoSuchMethodException e) {
0984: final String message = "No public no-arg constructor in "
0985: + taskClass;
0986: log(message, Project.MSG_ERR);
0987: throw new BuildException(message);
0988: } catch (LinkageError e) {
0989: String message = "Could not load " + taskClass + ": " + e;
0990: log(message, Project.MSG_ERR);
0991: throw new BuildException(message, e);
0992: }
0993: if (!Task.class.isAssignableFrom(taskClass)) {
0994: TaskAdapter.checkTaskClass(taskClass, this );
0995: }
0996: }
0997:
0998: /**
0999: * Return the current task definition hashtable. The returned hashtable is
1000: * "live" and so should not be modified.
1001: *
1002: * @return a map of from task name to implementing class
1003: * (String to Class).
1004: */
1005: public Hashtable getTaskDefinitions() {
1006: return ComponentHelper.getComponentHelper(this )
1007: .getTaskDefinitions();
1008: }
1009:
1010: /**
1011: * Add a new datatype definition.
1012: * Attempting to override an existing definition with an
1013: * equivalent one (i.e. with the same classname) results in
1014: * a verbose log message. Attempting to override an existing definition
1015: * with a different one results in a warning log message, but the
1016: * definition is changed.
1017: *
1018: * @param typeName The name of the datatype.
1019: * Must not be <code>null</code>.
1020: * @param typeClass The full name of the class implementing the datatype.
1021: * Must not be <code>null</code>.
1022: */
1023: public void addDataTypeDefinition(String typeName, Class typeClass) {
1024: ComponentHelper.getComponentHelper(this ).addDataTypeDefinition(
1025: typeName, typeClass);
1026: }
1027:
1028: /**
1029: * Return the current datatype definition hashtable. The returned
1030: * hashtable is "live" and so should not be modified.
1031: *
1032: * @return a map of from datatype name to implementing class
1033: * (String to Class).
1034: */
1035: public Hashtable getDataTypeDefinitions() {
1036: return ComponentHelper.getComponentHelper(this )
1037: .getDataTypeDefinitions();
1038: }
1039:
1040: /**
1041: * Add a <em>new</em> target to the project.
1042: *
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(Target)
1049: */
1050: public void addTarget(Target target) throws BuildException {
1051: addTarget(target.getName(), target);
1052: }
1053:
1054: /**
1055: * Add a <em>new</em> target to the project.
1056: *
1057: * @param targetName The name to use for the target.
1058: * Must not be <code>null</code>.
1059: * @param target The target to be added to the project.
1060: * Must not be <code>null</code>.
1061: *
1062: * @exception BuildException if the target already exists in the project.
1063: *
1064: * @see Project#addOrReplaceTarget(String, Target)
1065: */
1066: public void addTarget(String targetName, Target target)
1067: throws BuildException {
1068: if (targets.get(targetName) != null) {
1069: throw new BuildException("Duplicate target: `" + targetName
1070: + "'");
1071: }
1072: addOrReplaceTarget(targetName, target);
1073: }
1074:
1075: /**
1076: * Add a target to the project, or replaces one with the same
1077: * name.
1078: *
1079: * @param target The target to be added or replaced in the project.
1080: * Must not be <code>null</code>.
1081: */
1082: public void addOrReplaceTarget(Target target) {
1083: addOrReplaceTarget(target.getName(), target);
1084: }
1085:
1086: /**
1087: * Add a target to the project, or replaces one with the same
1088: * name.
1089: *
1090: * @param targetName The name to use for the target.
1091: * Must not be <code>null</code>.
1092: * @param target The target to be added or replaced in the project.
1093: * Must not be <code>null</code>.
1094: */
1095: public void addOrReplaceTarget(String targetName, Target target) {
1096: String msg = " +Target: " + targetName;
1097: log(msg, MSG_DEBUG);
1098: target.setProject(this );
1099: targets.put(targetName, target);
1100: }
1101:
1102: /**
1103: * Return the hashtable of targets. The returned hashtable
1104: * is "live" and so should not be modified.
1105: * @return a map from name to target (String to Target).
1106: */
1107: public Hashtable getTargets() {
1108: return targets;
1109: }
1110:
1111: /**
1112: * Create a new instance of a task, adding it to a list of
1113: * created tasks for later invalidation. This causes all tasks
1114: * to be remembered until the containing project is removed
1115: * @param taskType The name of the task to create an instance of.
1116: * Must not be <code>null</code>.
1117: *
1118: * @return an instance of the specified task, or <code>null</code> if
1119: * the task name is not recognised.
1120: *
1121: * @exception BuildException if the task name is recognised but task
1122: * creation fails.
1123: */
1124: public Task createTask(String taskType) throws BuildException {
1125: return ComponentHelper.getComponentHelper(this ).createTask(
1126: taskType);
1127: }
1128:
1129: /**
1130: * Create a new instance of a data type.
1131: *
1132: * @param typeName The name of the data type to create an instance of.
1133: * Must not be <code>null</code>.
1134: *
1135: * @return an instance of the specified data type, or <code>null</code> if
1136: * the data type name is not recognised.
1137: *
1138: * @exception BuildException if the data type name is recognised but
1139: * instance creation fails.
1140: */
1141: public Object createDataType(String typeName) throws BuildException {
1142: return ComponentHelper.getComponentHelper(this ).createDataType(
1143: typeName);
1144: }
1145:
1146: /**
1147: * Set the Executor instance for this Project.
1148: * @param e the Executor to use.
1149: */
1150: public void setExecutor(Executor e) {
1151: addReference(MagicNames.ANT_EXECUTOR_REFERENCE, e);
1152: }
1153:
1154: /**
1155: * Get this Project's Executor (setting it if necessary).
1156: * @return an Executor instance.
1157: */
1158: public Executor getExecutor() {
1159: Object o = getReference(MagicNames.ANT_EXECUTOR_REFERENCE);
1160: if (o == null) {
1161: String classname = getProperty(MagicNames.ANT_EXECUTOR_CLASSNAME);
1162: if (classname == null) {
1163: classname = DefaultExecutor.class.getName();
1164: }
1165: log("Attempting to create object of type " + classname,
1166: MSG_DEBUG);
1167: try {
1168: o = Class.forName(classname, true, coreLoader)
1169: .newInstance();
1170: } catch (ClassNotFoundException seaEnEfEx) {
1171: //try the current classloader
1172: try {
1173: o = Class.forName(classname).newInstance();
1174: } catch (Exception ex) {
1175: log(ex.toString(), MSG_ERR);
1176: }
1177: } catch (Exception ex) {
1178: log(ex.toString(), MSG_ERR);
1179: }
1180: if (o == null) {
1181: throw new BuildException(
1182: "Unable to obtain a Target Executor instance.");
1183: }
1184: setExecutor((Executor) o);
1185: }
1186: return (Executor) o;
1187: }
1188:
1189: /**
1190: * Execute the specified sequence of targets, and the targets
1191: * they depend on.
1192: *
1193: * @param names A vector of target name strings to execute.
1194: * Must not be <code>null</code>.
1195: *
1196: * @exception BuildException if the build failed.
1197: */
1198: public void executeTargets(Vector names) throws BuildException {
1199: getExecutor().executeTargets(this ,
1200: (String[]) (names.toArray(new String[names.size()])));
1201: }
1202:
1203: /**
1204: * Demultiplex output so that each task receives the appropriate
1205: * messages. If the current thread is not currently executing a task,
1206: * the message is logged directly.
1207: *
1208: * @param output Message to handle. Should not be <code>null</code>.
1209: * @param isWarning Whether the text represents an warning (<code>true</code>)
1210: * or information (<code>false</code>).
1211: */
1212: public void demuxOutput(String output, boolean isWarning) {
1213: Task task = getThreadTask(Thread.currentThread());
1214: if (task == null) {
1215: log(output, isWarning ? MSG_WARN : MSG_INFO);
1216: } else {
1217: if (isWarning) {
1218: task.handleErrorOutput(output);
1219: } else {
1220: task.handleOutput(output);
1221: }
1222: }
1223: }
1224:
1225: /**
1226: * Read data from the default input stream. If no default has been
1227: * specified, System.in is used.
1228: *
1229: * @param buffer the buffer into which data is to be read.
1230: * @param offset the offset into the buffer at which data is stored.
1231: * @param length the amount of data to read.
1232: *
1233: * @return the number of bytes read.
1234: *
1235: * @exception IOException if the data cannot be read.
1236: * @since Ant 1.6
1237: */
1238: public int defaultInput(byte[] buffer, int offset, int length)
1239: throws IOException {
1240: if (defaultInputStream != null) {
1241: System.out.flush();
1242: return defaultInputStream.read(buffer, offset, length);
1243: } else {
1244: throw new EOFException("No input provided for project");
1245: }
1246: }
1247:
1248: /**
1249: * Demux an input request to the correct task.
1250: *
1251: * @param buffer the buffer into which data is to be read.
1252: * @param offset the offset into the buffer at which data is stored.
1253: * @param length the amount of data to read.
1254: *
1255: * @return the number of bytes read.
1256: *
1257: * @exception IOException if the data cannot be read.
1258: * @since Ant 1.6
1259: */
1260: public int demuxInput(byte[] buffer, int offset, int length)
1261: throws IOException {
1262: Task task = getThreadTask(Thread.currentThread());
1263: if (task == null) {
1264: return defaultInput(buffer, offset, length);
1265: } else {
1266: return task.handleInput(buffer, offset, length);
1267: }
1268: }
1269:
1270: /**
1271: * Demultiplex flush operations so that each task receives the appropriate
1272: * messages. If the current thread is not currently executing a task,
1273: * the message is logged directly.
1274: *
1275: * @since Ant 1.5.2
1276: *
1277: * @param output Message to handle. Should not be <code>null</code>.
1278: * @param isError Whether the text represents an error (<code>true</code>)
1279: * or information (<code>false</code>).
1280: */
1281: public void demuxFlush(String output, boolean isError) {
1282: Task task = getThreadTask(Thread.currentThread());
1283: if (task == null) {
1284: fireMessageLogged(this , output, isError ? MSG_ERR
1285: : MSG_INFO);
1286: } else {
1287: if (isError) {
1288: task.handleErrorFlush(output);
1289: } else {
1290: task.handleFlush(output);
1291: }
1292: }
1293: }
1294:
1295: /**
1296: * Execute the specified target and any targets it depends on.
1297: *
1298: * @param targetName The name of the target to execute.
1299: * Must not be <code>null</code>.
1300: *
1301: * @exception BuildException if the build failed.
1302: */
1303: public void executeTarget(String targetName) throws BuildException {
1304:
1305: // sanity check ourselves, if we've been asked to build nothing
1306: // then we should complain
1307:
1308: if (targetName == null) {
1309: String msg = "No target specified";
1310: throw new BuildException(msg);
1311: }
1312:
1313: // Sort and run the dependency tree.
1314: // Sorting checks if all the targets (and dependencies)
1315: // exist, and if there is any cycle in the dependency
1316: // graph.
1317: executeSortedTargets(topoSort(targetName, targets, false));
1318: }
1319:
1320: /**
1321: * Execute a <code>Vector</code> of sorted targets.
1322: * @param sortedTargets the aforementioned <code>Vector</code>.
1323: * @throws BuildException on error.
1324: */
1325: public void executeSortedTargets(Vector sortedTargets)
1326: throws BuildException {
1327: Set succeededTargets = new HashSet();
1328: BuildException buildException = null; // first build exception
1329: for (Enumeration iter = sortedTargets.elements(); iter
1330: .hasMoreElements();) {
1331: Target curtarget = (Target) iter.nextElement();
1332: boolean canExecute = true;
1333: for (Enumeration depIter = curtarget.getDependencies(); depIter
1334: .hasMoreElements();) {
1335: String dependencyName = ((String) depIter.nextElement());
1336: if (!succeededTargets.contains(dependencyName)) {
1337: canExecute = false;
1338: log(curtarget, "Cannot execute '"
1339: + curtarget.getName() + "' - '"
1340: + dependencyName
1341: + "' failed or was not executed.", MSG_ERR);
1342: break;
1343: }
1344: }
1345: if (canExecute) {
1346: Throwable thrownException = null;
1347: try {
1348: curtarget.performTasks();
1349: succeededTargets.add(curtarget.getName());
1350: } catch (RuntimeException ex) {
1351: if (!(keepGoingMode)) {
1352: throw ex; // throw further
1353: }
1354: thrownException = ex;
1355: } catch (Throwable ex) {
1356: if (!(keepGoingMode)) {
1357: throw new BuildException(ex);
1358: }
1359: thrownException = ex;
1360: }
1361: if (thrownException != null) {
1362: if (thrownException instanceof BuildException) {
1363: log(curtarget, "Target '" + curtarget.getName()
1364: + "' failed with message '"
1365: + thrownException.getMessage() + "'.",
1366: MSG_ERR);
1367: // only the first build exception is reported
1368: if (buildException == null) {
1369: buildException = (BuildException) thrownException;
1370: }
1371: } else {
1372: log(curtarget, "Target '" + curtarget.getName()
1373: + "' failed with message '"
1374: + thrownException.getMessage() + "'.",
1375: MSG_ERR);
1376: thrownException.printStackTrace(System.err);
1377: if (buildException == null) {
1378: buildException = new BuildException(
1379: thrownException);
1380: }
1381: }
1382: }
1383: }
1384: }
1385: if (buildException != null) {
1386: throw buildException;
1387: }
1388: }
1389:
1390: /**
1391: * Return the canonical form of a filename.
1392: * <p>
1393: * If the specified file name is relative it is resolved
1394: * with respect to the given root directory.
1395: *
1396: * @param fileName The name of the file to resolve.
1397: * Must not be <code>null</code>.
1398: *
1399: * @param rootDir The directory respective to which relative file names
1400: * are resolved. May be <code>null</code>, in which case
1401: * the current directory is used.
1402: *
1403: * @return the resolved File.
1404: *
1405: * @deprecated since 1.4.x
1406: */
1407: public File resolveFile(String fileName, File rootDir) {
1408: return FILE_UTILS.resolveFile(rootDir, fileName);
1409: }
1410:
1411: /**
1412: * Return the canonical form of a filename.
1413: * <p>
1414: * If the specified file name is relative it is resolved
1415: * with respect to the project's base directory.
1416: *
1417: * @param fileName The name of the file to resolve.
1418: * Must not be <code>null</code>.
1419: *
1420: * @return the resolved File.
1421: *
1422: */
1423: public File resolveFile(String fileName) {
1424: return FILE_UTILS.resolveFile(baseDir, fileName);
1425: }
1426:
1427: /**
1428: * Translate a path into its native (platform specific) format.
1429: * <p>
1430: * This method uses PathTokenizer to separate the input path
1431: * into its components. This handles DOS style paths in a relatively
1432: * sensible way. The file separators are then converted to their platform
1433: * specific versions.
1434: *
1435: * @param toProcess The path to be translated.
1436: * May be <code>null</code>.
1437: *
1438: * @return the native version of the specified path or
1439: * an empty string if the path is <code>null</code> or empty.
1440: *
1441: * @deprecated since 1.7
1442: * Use FileUtils.translatePath instead.
1443: *
1444: * @see PathTokenizer
1445: */
1446: public static String translatePath(String toProcess) {
1447: return FileUtils.translatePath(toProcess);
1448: }
1449:
1450: /**
1451: * Convenience method to copy a file from a source to a destination.
1452: * No filtering is performed.
1453: *
1454: * @param sourceFile Name of file to copy from.
1455: * Must not be <code>null</code>.
1456: * @param destFile Name of file to copy to.
1457: * Must not be <code>null</code>.
1458: *
1459: * @exception IOException if the copying fails.
1460: *
1461: * @deprecated since 1.4.x
1462: */
1463: public void copyFile(String sourceFile, String destFile)
1464: throws IOException {
1465: FILE_UTILS.copyFile(sourceFile, destFile);
1466: }
1467:
1468: /**
1469: * Convenience method to copy a file from a source to a destination
1470: * specifying if token filtering should be used.
1471: *
1472: * @param sourceFile Name of file to copy from.
1473: * Must not be <code>null</code>.
1474: * @param destFile Name of file to copy to.
1475: * Must not be <code>null</code>.
1476: * @param filtering Whether or not token filtering should be used during
1477: * the copy.
1478: *
1479: * @exception IOException if the copying fails.
1480: *
1481: * @deprecated since 1.4.x
1482: */
1483: public void copyFile(String sourceFile, String destFile,
1484: boolean filtering) throws IOException {
1485: FILE_UTILS.copyFile(sourceFile, destFile,
1486: filtering ? globalFilters : null);
1487: }
1488:
1489: /**
1490: * Convenience method to copy a file from a source to a
1491: * destination specifying if token filtering should be used and if
1492: * source files may overwrite newer destination files.
1493: *
1494: * @param sourceFile Name of file to copy from.
1495: * Must not be <code>null</code>.
1496: * @param destFile Name of file to copy to.
1497: * Must not be <code>null</code>.
1498: * @param filtering Whether or not token filtering should be used during
1499: * the copy.
1500: * @param overwrite Whether or not the destination file should be
1501: * overwritten if it already exists.
1502: *
1503: * @exception IOException if the copying fails.
1504: *
1505: * @deprecated since 1.4.x
1506: */
1507: public void copyFile(String sourceFile, String destFile,
1508: boolean filtering, boolean overwrite) throws IOException {
1509: FILE_UTILS.copyFile(sourceFile, destFile,
1510: filtering ? globalFilters : null, overwrite);
1511: }
1512:
1513: /**
1514: * Convenience method to copy a file from a source to a
1515: * destination specifying if token filtering should be used, if
1516: * source files may overwrite newer destination files, and if the
1517: * last modified time of the resulting file should be set to
1518: * that of the source file.
1519: *
1520: * @param sourceFile Name of file to copy from.
1521: * Must not be <code>null</code>.
1522: * @param destFile Name of file to copy to.
1523: * Must not be <code>null</code>.
1524: * @param filtering Whether or not token filtering should be used during
1525: * the copy.
1526: * @param overwrite Whether or not the destination file should be
1527: * overwritten if it already exists.
1528: * @param preserveLastModified Whether or not the last modified time of
1529: * the resulting file should be set to that
1530: * of the source file.
1531: *
1532: * @exception IOException if the copying fails.
1533: *
1534: * @deprecated since 1.4.x
1535: */
1536: public void copyFile(String sourceFile, String destFile,
1537: boolean filtering, boolean overwrite,
1538: boolean preserveLastModified) throws IOException {
1539: FILE_UTILS.copyFile(sourceFile, destFile,
1540: filtering ? globalFilters : null, overwrite,
1541: preserveLastModified);
1542: }
1543:
1544: /**
1545: * Convenience method to copy a file from a source to a destination.
1546: * No filtering is performed.
1547: *
1548: * @param sourceFile File to copy from.
1549: * Must not be <code>null</code>.
1550: * @param destFile File to copy to.
1551: * Must not be <code>null</code>.
1552: *
1553: * @exception IOException if the copying fails.
1554: *
1555: * @deprecated since 1.4.x
1556: */
1557: public void copyFile(File sourceFile, File destFile)
1558: throws IOException {
1559: FILE_UTILS.copyFile(sourceFile, destFile);
1560: }
1561:
1562: /**
1563: * Convenience method to copy a file from a source to a destination
1564: * specifying if token filtering should be used.
1565: *
1566: * @param sourceFile File to copy from.
1567: * Must not be <code>null</code>.
1568: * @param destFile File to copy to.
1569: * Must not be <code>null</code>.
1570: * @param filtering Whether or not token filtering should be used during
1571: * the copy.
1572: *
1573: * @exception IOException if the copying fails.
1574: *
1575: * @deprecated since 1.4.x
1576: */
1577: public void copyFile(File sourceFile, File destFile,
1578: boolean filtering) throws IOException {
1579: FILE_UTILS.copyFile(sourceFile, destFile,
1580: filtering ? globalFilters : null);
1581: }
1582:
1583: /**
1584: * Convenience method to copy a file from a source to a
1585: * destination specifying if token filtering should be used and if
1586: * source files may overwrite newer destination files.
1587: *
1588: * @param sourceFile File to copy from.
1589: * Must not be <code>null</code>.
1590: * @param destFile File to copy to.
1591: * Must not be <code>null</code>.
1592: * @param filtering Whether or not token filtering should be used during
1593: * the copy.
1594: * @param overwrite Whether or not the destination file should be
1595: * overwritten if it already exists.
1596: *
1597: * @exception IOException if the file cannot be copied.
1598: *
1599: * @deprecated since 1.4.x
1600: */
1601: public void copyFile(File sourceFile, File destFile,
1602: boolean filtering, boolean overwrite) throws IOException {
1603: FILE_UTILS.copyFile(sourceFile, destFile,
1604: filtering ? globalFilters : null, overwrite);
1605: }
1606:
1607: /**
1608: * Convenience method to copy a file from a source to a
1609: * destination specifying if token filtering should be used, if
1610: * source files may overwrite newer destination files, and if the
1611: * last modified time of the resulting file should be set to
1612: * that of the source file.
1613: *
1614: * @param sourceFile File to copy from.
1615: * Must not be <code>null</code>.
1616: * @param destFile File to copy to.
1617: * Must not be <code>null</code>.
1618: * @param filtering Whether or not token filtering should be used during
1619: * the copy.
1620: * @param overwrite Whether or not the destination file should be
1621: * overwritten if it already exists.
1622: * @param preserveLastModified Whether or not the last modified time of
1623: * the resulting file should be set to that
1624: * of the source file.
1625: *
1626: * @exception IOException if the file cannot be copied.
1627: *
1628: * @deprecated since 1.4.x
1629: */
1630: public void copyFile(File sourceFile, File destFile,
1631: boolean filtering, boolean overwrite,
1632: boolean preserveLastModified) throws IOException {
1633: FILE_UTILS.copyFile(sourceFile, destFile,
1634: filtering ? globalFilters : null, overwrite,
1635: preserveLastModified);
1636: }
1637:
1638: /**
1639: * Call File.setLastModified(long time) on Java above 1.1, and logs
1640: * a warning on Java 1.1.
1641: *
1642: * @param file The file to set the last modified time on.
1643: * Must not be <code>null</code>.
1644: *
1645: * @param time the required modification time.
1646: *
1647: * @deprecated since 1.4.x
1648: *
1649: * @exception BuildException if the last modified time cannot be set
1650: * despite running on a platform with a version
1651: * above 1.1.
1652: */
1653: public void setFileLastModified(File file, long time)
1654: throws BuildException {
1655: FILE_UTILS.setFileLastModified(file, time);
1656: log("Setting modification time for " + file, MSG_VERBOSE);
1657: }
1658:
1659: /**
1660: * Return the boolean equivalent of a string, which is considered
1661: * <code>true</code> if either <code>"on"</code>, <code>"true"</code>,
1662: * or <code>"yes"</code> is found, ignoring case.
1663: *
1664: * @param s The string to convert to a boolean value.
1665: *
1666: * @return <code>true</code> if the given string is <code>"on"</code>,
1667: * <code>"true"</code> or <code>"yes"</code>, or
1668: * <code>false</code> otherwise.
1669: */
1670: public static boolean toBoolean(String s) {
1671: return ("on".equalsIgnoreCase(s) || "true".equalsIgnoreCase(s) || "yes"
1672: .equalsIgnoreCase(s));
1673: }
1674:
1675: /**
1676: * Topologically sort a set of targets. Equivalent to calling
1677: * <code>topoSort(new String[] {root}, targets, true)</code>.
1678: *
1679: * @param root The name of the root target. The sort is created in such
1680: * a way that the sequence of Targets up to the root
1681: * target is the minimum possible such sequence.
1682: * Must not be <code>null</code>.
1683: * @param targetTable A Hashtable mapping names to Targets.
1684: * Must not be <code>null</code>.
1685: * @return a Vector of ALL Target objects in sorted order.
1686: * @exception BuildException if there is a cyclic dependency among the
1687: * targets, or if a named target does not exist.
1688: */
1689: public final Vector topoSort(String root, Hashtable targetTable)
1690: throws BuildException {
1691: return topoSort(new String[] { root }, targetTable, true);
1692: }
1693:
1694: /**
1695: * Topologically sort a set of targets. Equivalent to calling
1696: * <code>topoSort(new String[] {root}, targets, returnAll)</code>.
1697: *
1698: * @param root The name of the root target. The sort is created in such
1699: * a way that the sequence of Targets up to the root
1700: * target is the minimum possible such sequence.
1701: * Must not be <code>null</code>.
1702: * @param targetTable A Hashtable mapping names to Targets.
1703: * Must not be <code>null</code>.
1704: * @param returnAll <code>boolean</code> indicating whether to return all
1705: * targets, or the execution sequence only.
1706: * @return a Vector of Target objects in sorted order.
1707: * @exception BuildException if there is a cyclic dependency among the
1708: * targets, or if a named target does not exist.
1709: * @since Ant 1.6.3
1710: */
1711: public final Vector topoSort(String root, Hashtable targetTable,
1712: boolean returnAll) throws BuildException {
1713: return topoSort(new String[] { root }, targetTable, returnAll);
1714: }
1715:
1716: /**
1717: * Topologically sort a set of targets.
1718: *
1719: * @param root <code>String[]</code> containing the names of the root targets.
1720: * The sort is created in such a way that the ordered sequence of
1721: * Targets is the minimum possible such sequence to the specified
1722: * root targets.
1723: * Must not be <code>null</code>.
1724: * @param targetTable A map of names to targets (String to Target).
1725: * Must not be <code>null</code>.
1726: * @param returnAll <code>boolean</code> indicating whether to return all
1727: * targets, or the execution sequence only.
1728: * @return a Vector of Target objects in sorted order.
1729: * @exception BuildException if there is a cyclic dependency among the
1730: * targets, or if a named target does not exist.
1731: * @since Ant 1.6.3
1732: */
1733: public final Vector topoSort(String[] root, Hashtable targetTable,
1734: boolean returnAll) throws BuildException {
1735: Vector ret = new Vector();
1736: Hashtable state = new Hashtable();
1737: Stack visiting = new Stack();
1738:
1739: // We first run a DFS based sort using each root as a starting node.
1740: // This creates the minimum sequence of Targets to the root node(s).
1741: // We then do a sort on any remaining unVISITED targets.
1742: // This is unnecessary for doing our build, but it catches
1743: // circular dependencies or missing Targets on the entire
1744: // dependency tree, not just on the Targets that depend on the
1745: // build Target.
1746:
1747: for (int i = 0; i < root.length; i++) {
1748: String st = (String) (state.get(root[i]));
1749: if (st == null) {
1750: tsort(root[i], targetTable, state, visiting, ret);
1751: } else if (st == VISITING) {
1752: throw new RuntimeException(
1753: "Unexpected node in visiting state: " + root[i]);
1754: }
1755: }
1756: StringBuffer buf = new StringBuffer(
1757: "Build sequence for target(s)");
1758:
1759: for (int j = 0; j < root.length; j++) {
1760: buf.append((j == 0) ? " `" : ", `").append(root[j]).append(
1761: '\'');
1762: }
1763: buf.append(" is " + ret);
1764: log(buf.toString(), MSG_VERBOSE);
1765:
1766: Vector complete = (returnAll) ? ret : new Vector(ret);
1767: for (Enumeration en = targetTable.keys(); en.hasMoreElements();) {
1768: String curTarget = (String) en.nextElement();
1769: String st = (String) state.get(curTarget);
1770: if (st == null) {
1771: tsort(curTarget, targetTable, state, visiting, complete);
1772: } else if (st == VISITING) {
1773: throw new RuntimeException(
1774: "Unexpected node in visiting state: "
1775: + curTarget);
1776: }
1777: }
1778: log("Complete build sequence is " + complete, MSG_VERBOSE);
1779: return ret;
1780: }
1781:
1782: /**
1783: * Perform a single step in a recursive depth-first-search traversal of
1784: * the target dependency tree.
1785: * <p>
1786: * The current target is first set to the "visiting" state, and
1787: * pushed onto the "visiting" stack.
1788: * <p>
1789: * An exception is then thrown if any child of the current node is in the
1790: * visiting state, as that implies a circular dependency. The exception
1791: * contains details of the cycle, using elements of the "visiting"
1792: * stack.
1793: * <p>
1794: * If any child has not already been "visited", this method is
1795: * called recursively on it.
1796: * <p>
1797: * The current target is then added to the ordered list of targets. Note
1798: * that this is performed after the children have been visited in order
1799: * to get the correct order. The current target is set to the
1800: * "visited" state.
1801: * <p>
1802: * By the time this method returns, the ordered list contains the sequence
1803: * of targets up to and including the current target.
1804: *
1805: * @param root The current target to inspect.
1806: * Must not be <code>null</code>.
1807: * @param targetTable A mapping from names to targets (String to Target).
1808: * Must not be <code>null</code>.
1809: * @param state A mapping from target names to states (String to String).
1810: * The states in question are "VISITING" and
1811: * "VISITED". Must not be <code>null</code>.
1812: * @param visiting A stack of targets which are currently being visited.
1813: * Must not be <code>null</code>.
1814: * @param ret The list to add target names to. This will end up
1815: * containing the complete list of dependencies in
1816: * dependency order.
1817: * Must not be <code>null</code>.
1818: *
1819: * @exception BuildException if a non-existent target is specified or if
1820: * a circular dependency is detected.
1821: */
1822: private void tsort(String root, Hashtable targetTable,
1823: Hashtable state, Stack visiting, Vector ret)
1824: throws BuildException {
1825: state.put(root, VISITING);
1826: visiting.push(root);
1827:
1828: Target target = (Target) targetTable.get(root);
1829:
1830: // Make sure we exist
1831: if (target == null) {
1832: StringBuffer sb = new StringBuffer("Target \"");
1833: sb.append(root);
1834: sb.append("\" does not exist in the project \"");
1835: sb.append(name);
1836: sb.append("\". ");
1837: visiting.pop();
1838: if (!visiting.empty()) {
1839: String parent = (String) visiting.peek();
1840: sb.append("It is used from target \"");
1841: sb.append(parent);
1842: sb.append("\".");
1843: }
1844: throw new BuildException(new String(sb));
1845: }
1846: for (Enumeration en = target.getDependencies(); en
1847: .hasMoreElements();) {
1848: String cur = (String) en.nextElement();
1849: String m = (String) state.get(cur);
1850: if (m == null) {
1851: // Not been visited
1852: tsort(cur, targetTable, state, visiting, ret);
1853: } else if (m == VISITING) {
1854: // Currently visiting this node, so have a cycle
1855: throw makeCircularException(cur, visiting);
1856: }
1857: }
1858: String p = (String) visiting.pop();
1859: if (root != p) {
1860: throw new RuntimeException(
1861: "Unexpected internal error: expected to " + "pop "
1862: + root + " but got " + p);
1863: }
1864: state.put(root, VISITED);
1865: ret.addElement(target);
1866: }
1867:
1868: /**
1869: * Build an appropriate exception detailing a specified circular
1870: * dependency.
1871: *
1872: * @param end The dependency to stop at. Must not be <code>null</code>.
1873: * @param stk A stack of dependencies. Must not be <code>null</code>.
1874: *
1875: * @return a BuildException detailing the specified circular dependency.
1876: */
1877: private static BuildException makeCircularException(String end,
1878: Stack stk) {
1879: StringBuffer sb = new StringBuffer("Circular dependency: ");
1880: sb.append(end);
1881: String c;
1882: do {
1883: c = (String) stk.pop();
1884: sb.append(" <- ");
1885: sb.append(c);
1886: } while (!c.equals(end));
1887: return new BuildException(new String(sb));
1888: }
1889:
1890: /**
1891: * Inherit the id references.
1892: * @param parent the parent project of this project.
1893: */
1894: public void inheritIDReferences(Project parent) {
1895: parentIdProject = parent;
1896: }
1897:
1898: /**
1899: * Attempt to resolve an Unknown Reference using the
1900: * parsed id's - for BC.
1901: */
1902: private Object resolveIdReference(String key, Project callerProject) {
1903: UnknownElement origUE = (UnknownElement) idReferences.get(key);
1904: if (origUE == null) {
1905: return parentIdProject == null ? null : parentIdProject
1906: .resolveIdReference(key, callerProject);
1907: }
1908: callerProject.log("Warning: Reference " + key
1909: + " has not been set at runtime,"
1910: + " but was found during" + LINE_SEP
1911: + "build file parsing, attempting to resolve."
1912: + " Future versions of Ant may support" + LINE_SEP
1913: + " referencing ids defined in non-executed targets.",
1914: MSG_WARN);
1915: UnknownElement copyUE = origUE.copy(callerProject);
1916: copyUE.maybeConfigure();
1917: return copyUE.getRealThing();
1918: }
1919:
1920: /**
1921: * Add an id reference.
1922: * Used for broken build files.
1923: * @param id the id to set.
1924: * @param value the value to set it to (Unknown element in this case.
1925: */
1926: public void addIdReference(String id, Object value) {
1927: idReferences.put(id, value);
1928: }
1929:
1930: /**
1931: * Add a reference to the project.
1932: *
1933: * @param referenceName The name of the reference. Must not be <code>null</code>.
1934: * @param value The value of the reference.
1935: */
1936: public void addReference(String referenceName, Object value) {
1937: synchronized (references) {
1938: Object old = ((AntRefTable) references)
1939: .getReal(referenceName);
1940: if (old == value) {
1941: // no warning, this is not changing anything
1942: return;
1943: }
1944: if (old != null && !(old instanceof UnknownElement)) {
1945: log("Overriding previous definition of reference to "
1946: + referenceName, MSG_VERBOSE);
1947: }
1948: log("Adding reference: " + referenceName, MSG_DEBUG);
1949: references.put(referenceName, value);
1950: }
1951: }
1952:
1953: /**
1954: * Return a map of the references in the project (String to Object).
1955: * The returned hashtable is "live" and so must not be modified.
1956: *
1957: * @return a map of the references in the project (String to Object).
1958: */
1959: public Hashtable getReferences() {
1960: return references;
1961: }
1962:
1963: /**
1964: * Look up a reference by its key (ID).
1965: *
1966: * @param key The key for the desired reference.
1967: * Must not be <code>null</code>.
1968: *
1969: * @return the reference with the specified ID, or <code>null</code> if
1970: * there is no such reference in the project.
1971: */
1972: public Object getReference(String key) {
1973: Object ret = references.get(key);
1974: if (ret != null) {
1975: return ret;
1976: }
1977: // Check for old id behaviour
1978: ret = resolveIdReference(key, this );
1979: if (ret == null
1980: && !key.equals(MagicNames.REFID_PROPERTY_HELPER)) {
1981: Vector p = new Vector();
1982: PropertyHelper.getPropertyHelper(this ).parsePropertyString(
1983: key, new Vector(), p);
1984: if (p.size() == 1) {
1985: log(
1986: "Unresolvable reference "
1987: + key
1988: + " might be a misuse of property expansion syntax.",
1989: MSG_WARN);
1990: }
1991: }
1992: return ret;
1993: }
1994:
1995: /**
1996: * Return a description of the type of the given element, with
1997: * special handling for instances of tasks and data types.
1998: * <p>
1999: * This is useful for logging purposes.
2000: *
2001: * @param element The element to describe.
2002: * Must not be <code>null</code>.
2003: *
2004: * @return a description of the element type.
2005: *
2006: * @since 1.95, Ant 1.5
2007: */
2008: public String getElementName(Object element) {
2009: return ComponentHelper.getComponentHelper(this ).getElementName(
2010: element);
2011: }
2012:
2013: /**
2014: * Send a "build started" event
2015: * to the build listeners for this project.
2016: */
2017: public void fireBuildStarted() {
2018: BuildEvent event = new BuildEvent(this );
2019: Iterator iter = listeners.iterator();
2020: while (iter.hasNext()) {
2021: BuildListener listener = (BuildListener) iter.next();
2022: listener.buildStarted(event);
2023: }
2024: }
2025:
2026: /**
2027: * Send a "build finished" event to the build listeners
2028: * for this project.
2029: * @param exception an exception indicating a reason for a build
2030: * failure. May be <code>null</code>, indicating
2031: * a successful build.
2032: */
2033: public void fireBuildFinished(Throwable exception) {
2034: BuildEvent event = new BuildEvent(this );
2035: event.setException(exception);
2036: Iterator iter = listeners.iterator();
2037: while (iter.hasNext()) {
2038: BuildListener listener = (BuildListener) iter.next();
2039: listener.buildFinished(event);
2040: }
2041: // Inform IH to clear the cache
2042: IntrospectionHelper.clearCache();
2043: }
2044:
2045: /**
2046: * Send a "subbuild started" event to the build listeners for
2047: * this project.
2048: *
2049: * @since Ant 1.6.2
2050: */
2051: public void fireSubBuildStarted() {
2052: BuildEvent event = new BuildEvent(this );
2053: Iterator iter = listeners.iterator();
2054: while (iter.hasNext()) {
2055: Object listener = iter.next();
2056: if (listener instanceof SubBuildListener) {
2057: ((SubBuildListener) listener).subBuildStarted(event);
2058: }
2059: }
2060: }
2061:
2062: /**
2063: * Send a "subbuild finished" event to the build listeners for
2064: * this project.
2065: * @param exception an exception indicating a reason for a build
2066: * failure. May be <code>null</code>, indicating
2067: * a successful build.
2068: *
2069: * @since Ant 1.6.2
2070: */
2071: public void fireSubBuildFinished(Throwable exception) {
2072: BuildEvent event = new BuildEvent(this );
2073: event.setException(exception);
2074: Iterator iter = listeners.iterator();
2075: while (iter.hasNext()) {
2076: Object listener = iter.next();
2077: if (listener instanceof SubBuildListener) {
2078: ((SubBuildListener) listener).subBuildFinished(event);
2079: }
2080: }
2081: }
2082:
2083: /**
2084: * Send a "target started" event to the build listeners
2085: * for this project.
2086: *
2087: * @param target The target which is starting to build.
2088: * Must not be <code>null</code>.
2089: */
2090: protected void fireTargetStarted(Target target) {
2091: BuildEvent event = new BuildEvent(target);
2092: Iterator iter = listeners.iterator();
2093: while (iter.hasNext()) {
2094: BuildListener listener = (BuildListener) iter.next();
2095: listener.targetStarted(event);
2096: }
2097: }
2098:
2099: /**
2100: * Send a "target finished" event to the build listeners
2101: * for this project.
2102: *
2103: * @param target The target which has finished building.
2104: * Must not be <code>null</code>.
2105: * @param exception an exception indicating a reason for a build
2106: * failure. May be <code>null</code>, indicating
2107: * a successful build.
2108: */
2109: protected void fireTargetFinished(Target target, Throwable exception) {
2110: BuildEvent event = new BuildEvent(target);
2111: event.setException(exception);
2112: Iterator iter = listeners.iterator();
2113: while (iter.hasNext()) {
2114: BuildListener listener = (BuildListener) iter.next();
2115: listener.targetFinished(event);
2116: }
2117: }
2118:
2119: /**
2120: * Send a "task started" event to the build listeners
2121: * for this project.
2122: *
2123: * @param task The target which is starting to execute.
2124: * Must not be <code>null</code>.
2125: */
2126: protected void fireTaskStarted(Task task) {
2127: // register this as the current task on the current thread.
2128: registerThreadTask(Thread.currentThread(), task);
2129: BuildEvent event = new BuildEvent(task);
2130: Iterator iter = listeners.iterator();
2131: while (iter.hasNext()) {
2132: BuildListener listener = (BuildListener) iter.next();
2133: listener.taskStarted(event);
2134: }
2135: }
2136:
2137: /**
2138: * Send a "task finished" event to the build listeners for this
2139: * project.
2140: *
2141: * @param task The task which has finished executing.
2142: * Must not be <code>null</code>.
2143: * @param exception an exception indicating a reason for a build
2144: * failure. May be <code>null</code>, indicating
2145: * a successful build.
2146: */
2147: protected void fireTaskFinished(Task task, Throwable exception) {
2148: registerThreadTask(Thread.currentThread(), null);
2149: System.out.flush();
2150: System.err.flush();
2151: BuildEvent event = new BuildEvent(task);
2152: event.setException(exception);
2153: Iterator iter = listeners.iterator();
2154: while (iter.hasNext()) {
2155: BuildListener listener = (BuildListener) iter.next();
2156: listener.taskFinished(event);
2157: }
2158: }
2159:
2160: /**
2161: * Send a "message logged" event to the build listeners
2162: * for this project.
2163: *
2164: * @param event The event to send. This should be built up with the
2165: * appropriate task/target/project by the caller, so that
2166: * this method can set the message and priority, then send
2167: * the event. Must not be <code>null</code>.
2168: * @param message The message to send. Should not be <code>null</code>.
2169: * @param priority The priority of the message.
2170: */
2171: private void fireMessageLoggedEvent(BuildEvent event,
2172: String message, int priority) {
2173:
2174: if (message.endsWith(StringUtils.LINE_SEP)) {
2175: int endIndex = message.length()
2176: - StringUtils.LINE_SEP.length();
2177: event.setMessage(message.substring(0, endIndex), priority);
2178: } else {
2179: event.setMessage(message, priority);
2180: }
2181: synchronized (this ) {
2182: if (loggingMessage) {
2183: /*
2184: * One of the Listeners has attempted to access
2185: * System.err or System.out.
2186: *
2187: * We used to throw an exception in this case, but
2188: * sometimes Listeners can't prevent it(like our own
2189: * Log4jListener which invokes getLogger() which in
2190: * turn wants to write to the console).
2191: *
2192: * @see http://marc.theaimsgroup.com/?t=110538624200006&r=1&w=2
2193: *
2194: * We now (Ant 1.7 and 1.6.3) simply swallow the message.
2195: */
2196: return;
2197: }
2198: try {
2199: loggingMessage = true;
2200: Iterator iter = listeners.iterator();
2201: while (iter.hasNext()) {
2202: BuildListener listener = (BuildListener) iter
2203: .next();
2204: listener.messageLogged(event);
2205: }
2206: } finally {
2207: loggingMessage = false;
2208: }
2209: }
2210: }
2211:
2212: /**
2213: * Send a "message logged" project level event
2214: * to the build listeners for this project.
2215: *
2216: * @param project The project generating the event.
2217: * Should not be <code>null</code>.
2218: * @param message The message to send. Should not be <code>null</code>.
2219: * @param priority The priority of the message.
2220: */
2221: protected void fireMessageLogged(Project project, String message,
2222: int priority) {
2223: fireMessageLogged(project, message, null, priority);
2224: }
2225:
2226: /**
2227: * Send a "message logged" project level event
2228: * to the build listeners for this project.
2229: *
2230: * @param project The project generating the event.
2231: * Should not be <code>null</code>.
2232: * @param message The message to send. Should not be <code>null</code>.
2233: * @param throwable The exception that caused this message. May be <code>null</code>.
2234: * @param priority The priority of the message.
2235: * @since 1.7
2236: */
2237: protected void fireMessageLogged(Project project, String message,
2238: Throwable throwable, int priority) {
2239: BuildEvent event = new BuildEvent(project);
2240: event.setException(throwable);
2241: fireMessageLoggedEvent(event, message, priority);
2242: }
2243:
2244: /**
2245: * Send a "message logged" target level event
2246: * to the build listeners for this project.
2247: *
2248: * @param target The target generating the event.
2249: * Must not be <code>null</code>.
2250: * @param message The message to send. Should not be <code>null</code>.
2251: * @param priority The priority of the message.
2252: */
2253: protected void fireMessageLogged(Target target, String message,
2254: int priority) {
2255: fireMessageLogged(target, message, null, priority);
2256: }
2257:
2258: /**
2259: * Send a "message logged" target level event
2260: * to the build listeners for this project.
2261: *
2262: * @param target The target generating the event.
2263: * Must not be <code>null</code>.
2264: * @param message The message to send. Should not be <code>null</code>.
2265: * @param throwable The exception that caused this message. May be <code>null</code>.
2266: * @param priority The priority of the message.
2267: * @since 1.7
2268: */
2269: protected void fireMessageLogged(Target target, String message,
2270: Throwable throwable, int priority) {
2271: BuildEvent event = new BuildEvent(target);
2272: event.setException(throwable);
2273: fireMessageLoggedEvent(event, message, priority);
2274: }
2275:
2276: /**
2277: * Send a "message logged" task level event
2278: * to the build listeners for this project.
2279: *
2280: * @param task The task generating the event.
2281: * Must not be <code>null</code>.
2282: * @param message The message to send. Should not be <code>null</code>.
2283: * @param priority The priority of the message.
2284: */
2285: protected void fireMessageLogged(Task task, String message,
2286: int priority) {
2287: fireMessageLogged(task, message, null, priority);
2288: }
2289:
2290: /**
2291: * Send a "message logged" task level event
2292: * to the build listeners for this project.
2293: *
2294: * @param task The task generating the event.
2295: * Must not be <code>null</code>.
2296: * @param message The message to send. Should not be <code>null</code>.
2297: * @param throwable The exception that caused this message. May be <code>null</code>.
2298: * @param priority The priority of the message.
2299: * @since 1.7
2300: */
2301: protected void fireMessageLogged(Task task, String message,
2302: Throwable throwable, int priority) {
2303: BuildEvent event = new BuildEvent(task);
2304: event.setException(throwable);
2305: fireMessageLoggedEvent(event, message, priority);
2306: }
2307:
2308: /**
2309: * Register a task as the current task for a thread.
2310: * If the task is null, the thread's entry is removed.
2311: *
2312: * @param thread the thread on which the task is registered.
2313: * @param task the task to be registered.
2314: * @since Ant 1.5
2315: */
2316: public synchronized void registerThreadTask(Thread thread, Task task) {
2317: if (task != null) {
2318: threadTasks.put(thread, task);
2319: threadGroupTasks.put(thread.getThreadGroup(), task);
2320: } else {
2321: threadTasks.remove(thread);
2322: threadGroupTasks.remove(thread.getThreadGroup());
2323: }
2324: }
2325:
2326: /**
2327: * Get the current task associated with a thread, if any.
2328: *
2329: * @param thread the thread for which the task is required.
2330: * @return the task which is currently registered for the given thread or
2331: * null if no task is registered.
2332: */
2333: public Task getThreadTask(Thread thread) {
2334: Task task = (Task) threadTasks.get(thread);
2335: if (task == null) {
2336: ThreadGroup group = thread.getThreadGroup();
2337: while (task == null && group != null) {
2338: task = (Task) threadGroupTasks.get(group);
2339: group = group.getParent();
2340: }
2341: }
2342: return task;
2343: }
2344:
2345: // Should move to a separate public class - and have API to add
2346: // listeners, etc.
2347: private static class AntRefTable extends Hashtable {
2348:
2349: AntRefTable() {
2350: super ();
2351: }
2352:
2353: /** Returns the unmodified original object.
2354: * This method should be called internally to
2355: * get the "real" object.
2356: * The normal get method will do the replacement
2357: * of UnknownElement (this is similar with the JDNI
2358: * refs behavior).
2359: */
2360: private Object getReal(Object key) {
2361: return super .get(key);
2362: }
2363:
2364: /** Get method for the reference table.
2365: * It can be used to hook dynamic references and to modify
2366: * some references on the fly--for example for delayed
2367: * evaluation.
2368: *
2369: * It is important to make sure that the processing that is
2370: * done inside is not calling get indirectly.
2371: *
2372: * @param key lookup key.
2373: * @return mapped value.
2374: */
2375: public Object get(Object key) {
2376: //System.out.println("AntRefTable.get " + key);
2377: Object o = getReal(key);
2378: if (o instanceof UnknownElement) {
2379: // Make sure that
2380: UnknownElement ue = (UnknownElement) o;
2381: ue.maybeConfigure();
2382: o = ue.getRealThing();
2383: }
2384: return o;
2385: }
2386: }
2387:
2388: /**
2389: * Set a reference to this Project on the parameterized object.
2390: * Need to set the project before other set/add elements
2391: * are called.
2392: * @param obj the object to invoke setProject(this) on.
2393: */
2394: public final void setProjectReference(final Object obj) {
2395: if (obj instanceof ProjectComponent) {
2396: ((ProjectComponent) obj).setProject(this );
2397: return;
2398: }
2399: try {
2400: Method method = obj.getClass().getMethod("setProject",
2401: new Class[] { Project.class });
2402: if (method != null) {
2403: method.invoke(obj, new Object[] { this });
2404: }
2405: } catch (Throwable e) {
2406: // ignore this if the object does not have
2407: // a set project method or the method
2408: // is private/protected.
2409: }
2410: }
2411:
2412: /**
2413: * Resolve the file relative to the project's basedir and return it as a
2414: * FileResource.
2415: * @param name the name of the file to resolve.
2416: * @return the file resource.
2417: * @since Ant 1.7
2418: */
2419: public Resource getResource(String name) {
2420: return new FileResource(getBaseDir(), name);
2421: }
2422: }
|