Sources for implementing PoC for debugger
git-svn-id: https://svn.apache.org/repos/asf/incubator/easyant/tasks/trunk@1132876 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/command-line-debugger/src/main/org/apache/tools/ant/DebugTask.java b/command-line-debugger/src/main/org/apache/tools/ant/DebugTask.java
new file mode 100644
index 0000000..6049b1b
--- /dev/null
+++ b/command-line-debugger/src/main/org/apache/tools/ant/DebugTask.java
@@ -0,0 +1,329 @@
+package org.apache.tools.ant;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.input.InputRequest;
+import org.apache.tools.ant.taskdefs.PathConvert;
+import org.apache.tools.ant.taskdefs.Property;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * A stand alone debug task that plugs into the build through projecthelper
+ * class by injecting a dependency at the end of a target. This is a POC and
+ * does not yet work for Extension-Points.
+ */
+public class DebugTask extends Task {
+ /**
+ * Debugger prompt
+ */
+ public static final String PROMPT = "DEBUGGER> ";
+
+ /**
+ * Standard target name for the internal debugger
+ */
+ public static final String DEBUG_TARGET_NAME = "-internal-debugger";
+
+ /*
+ * A static final debug target that is injected as a dependency. We may need
+ * to have more than one such target if we are to allow multiple
+ * break-points in the build.
+ */
+ private static final Target debugTarget = new Target();
+
+ /*
+ * Set of all commands that can be interpreted at runtime.
+ */
+ private static Set supportedCommands = new HashSet();
+
+ static {
+ // add any more commands to be supported here
+ // if need be, this can be considered to be
+ // moved to a separate properties file
+ supportedCommands.add("locate");
+ supportedCommands.add("inspect");
+ supportedCommands.add("return");
+ }
+
+ public void execute() throws BuildException {
+ // expect input here
+ InputRequest ir = new InputRequest(PROMPT);
+ InputHandler ih = new DefaultInputHandler();
+ String command = null;
+ getProject().log(
+ StringUtils.LINE_SEP
+ + "-------- Ant Command Line Debugger --------"
+ + StringUtils.LINE_SEP + StringUtils.LINE_SEP);
+
+ // keep accepting inputs, until the user enters the return command
+ do {
+ ih.handleInput(ir);
+ command = ir.getInput();
+ handleCommand(command);
+ getProject().log(""); // log a new line
+ } while (!"return".equals(command));
+
+ // resume build execution on this
+ getProject().log(
+ StringUtils.LINE_SEP
+ + "--------- Resuming Ant Execution ----------"
+ + StringUtils.LINE_SEP);
+ }
+
+ /**
+ * Static method to create a runtime-debug target. For purposes of Command
+ * Line Debugger (CLD), the target returned by this method, and identified
+ * by DEBUG_TARGET_NAME must be added at the end of the dependency list of
+ * the target where the break point exists.
+ *
+ * @param project
+ * @return
+ */
+ public static Target createDebugTarget(Project project) {
+ // see what is the best value for the Location to assume?
+ // Location loc = new Location(??);
+
+ debugTarget.setProject(project);
+ debugTarget.setName(DEBUG_TARGET_NAME);
+ project.addTarget(debugTarget);
+ // create an instance of debug task and attach it to this project
+ Task debugtask = project.createTask("debug");
+ debugtask.setProject(project);
+ debugtask.setTaskName("Debugger");
+ debugTarget.addTask(debugtask);
+ return debugTarget;
+ }
+
+ /*
+ * Interprets user input and decides if the command is supported or should
+ * be rejected.
+ */
+ protected void handleCommand(String command) {
+ if (command == null || command.trim().length() == 0) {
+ getProject().log("Invalid command. Use /? for more information.");
+ }
+ command = command.trim();
+ if (command.equals("/?")) {
+ printUsage();
+ return;
+ }
+
+ String[] tokens = command.split(" ");
+ if (!supportedCommands.contains(tokens[0])) {
+ printUsage();
+ return;
+ }
+
+ DebugSupport[] debuggers = new DebugSupport[] { new NoOp(),
+ new Inspector(), new Locator() };
+ DebugSupport selected = null;
+ for (int j = 0; j < debuggers.length; j++) {
+ if (debuggers[j].commandSupported(tokens[0]))
+ selected = debuggers[j];
+ }
+ selected.execute(getProject(), tokens);
+ }
+
+ protected void printUsage() {
+ // log all help stuff here
+ getProject()
+ .log(
+ "You may use one of the following commands: locate, inspect, return");
+ getProject()
+ .log(
+ "Type the command followed by /? for more information. Eg. inspect /?");
+ }
+
+ protected static List searchTask(Class expectedTaskClass, Project project) {
+ List result = new ArrayList();
+ for (Iterator iterator = project.getTargets().values().iterator(); iterator
+ .hasNext();) {
+ Target t = (Target) iterator.next();
+ for (int i = 0; i < t.getTasks().length; i++) {
+ Task task = t.getTasks()[i];
+ Class taskClass = ComponentHelper.getComponentHelper(project)
+ .getComponentClass(task.getTaskType());
+ // will need to see in what cases it could return a null type
+ // perhaps failing when the task is using a custom antlib
+ // defined task
+ if (taskClass != null && taskClass.equals(expectedTaskClass)) {
+ result.add(task);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * An interface for supporting debug commands.
+ */
+ public static interface DebugSupport {
+
+ /**
+ * Check if this command is supported.
+ *
+ * @param command
+ * @return
+ */
+ public boolean commandSupported(String command);
+
+ /**
+ * Main execution body of the class. Pass all command parameters.
+ *
+ * @param project
+ * @param params
+ */
+ public void execute(Project project, String[] params);
+
+ /**
+ * Prints usage of the command.
+ *
+ * @param project
+ */
+ public void printUsage(Project project);
+
+ }
+
+ /**
+ * Used to implement commands that should not be handled by
+ * {@link DebugSupport} at all. Example, the 'return' command
+ */
+ public static final class NoOp implements DebugSupport {
+
+ public boolean commandSupported(String command) {
+ return "return".equalsIgnoreCase(command);
+ }
+
+ public void execute(Project project, String[] params) {
+ // do nothing
+ }
+
+ public void printUsage(Project project) {
+ };
+ }
+
+ /**
+ * Locates properties / paths in static build sources
+ */
+ public static final class Locator implements DebugSupport {
+
+ public boolean commandSupported(String command) {
+ return "locate".equalsIgnoreCase(command);
+ }
+
+ public void execute(Project project, String[] params) {
+ // the command syntax is 'locate property some.property'
+ // or 'locate path some.path
+ if (params.length != 3 || "/?".equals(params[1])) {
+ printUsage(project);
+ return;
+ }
+
+ List matches = null;
+ String key = null;
+ if ("property".equalsIgnoreCase(params[1])) {
+ // locate and publish the property
+ matches = DebugTask.searchTask(Property.class, project);
+ key = "name";
+ } else if ("path".equalsIgnoreCase(params[1])) {
+ // locate and publish the path
+ matches = DebugTask.searchTask(Path.class, project);
+ key = "id";
+ } else {
+ // see if any other component may be supported
+ project.log("Unexpected component: " + params[1]);
+ project.log("Supported components are property, path.");
+ return;
+ }
+
+ // probably accept some kind of a query from end user and select the
+ // target object based on the query
+ for (Iterator iterator = matches.iterator(); iterator.hasNext();) {
+ Task task = (Task) iterator.next();
+ // display attributes
+ Map attributeMap = task.getWrapper().getAttributeMap();
+ if (!params[2].equals(attributeMap.get(key))) {
+ continue;
+ }
+ String value = (String) attributeMap.get("value");
+ project.log("Detected a property by name [" + params[2]
+ + "]. Build file value: " + value);
+ // and their respected location
+ project.log("Located at: " + task.getLocation().toString());
+ }
+ }
+
+ public void printUsage(Project project) {
+ project.log("Incorrect Parameters");
+ project.log("Usage: locate property/path propertyname/pathname");
+ }
+ }
+
+ /**
+ * Inspects the current value of a property, path or some reference.
+ */
+ public static final class Inspector implements DebugSupport {
+
+ public boolean commandSupported(String command) {
+ return "inspect".equalsIgnoreCase(command);
+ }
+
+ public void execute(Project project, String[] params) {
+ if (params.length < 3 || "/?".equals(params[1])) {
+ printUsage(project);
+ }
+
+ if ("property".equalsIgnoreCase(params[1])) {
+ // show all matches for a property
+ Object value = PropertyHelper.getProperty(project, params[2]);
+ if (value != null) {
+ project.log("Detected a property by name [" + params[1]
+ + "]. Current value: " + value);
+ } else {
+ project.log("Found no such property.");
+ }
+ } else if ("path".equalsIgnoreCase(params[1])) {
+ // look optional component
+ // the remaining part of the string could be:
+ // id=<someid> or refid=<somerefid>
+ Object ref = project.getReference(params[2]);
+ if (ref instanceof ResourceCollection) {
+ if (ref != null) {
+ PathConvert path = (PathConvert) project
+ .createTask("pathconvert");
+ path.setProject(project);
+ path.setPathSep(StringUtils.LINE_SEP + " - ");
+ path.add((ResourceCollection) ref);
+ path.execute();
+ } else {
+ project.log("No path-reference found for " + params[2]);
+ }
+ } else {
+ project.log("No path found for reference id: " + params[2]);
+ }
+ }
+
+ }
+
+ public void printUsage(Project project) {
+ project.log("Incorrect Parameters");
+ project.log("Usage: inspect property some.property");
+ project.log(" inspect path path.id");
+ }
+ }
+}
diff --git a/command-line-debugger/src/main/org/apache/tools/ant/Main.java b/command-line-debugger/src/main/org/apache/tools/ant/Main.java
new file mode 100644
index 0000000..d85ddf9
--- /dev/null
+++ b/command-line-debugger/src/main/org/apache/tools/ant/Main.java
@@ -0,0 +1,1235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.launch.AntMain;
+import org.apache.tools.ant.property.ResolvePropertyMap;
+import org.apache.tools.ant.util.ClasspathUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.ProxySetup;
+
+
+/**
+ * Command line entry point into Ant. This class is entered via the
+ * canonical `public static void main` entry point and reads the
+ * command line arguments. It then assembles and executes an Ant
+ * project.
+ * <p>
+ * If you integrating Ant into some other tool, this is not the class
+ * to use as an entry point. Please see the source code of this
+ * class to see how it manipulates the Ant project classes.
+ *
+ */
+public class Main implements AntMain {
+
+ /**
+ * A Set of args are are handled by the launcher and should
+ * not be seen by Main.
+ */
+ private static final Set LAUNCH_COMMANDS = new HashSet();
+ static {
+ LAUNCH_COMMANDS.add("-lib");
+ LAUNCH_COMMANDS.add("-cp");
+ LAUNCH_COMMANDS.add("-noclasspath");
+ LAUNCH_COMMANDS.add("--noclasspath");
+ LAUNCH_COMMANDS.add("-nouserlib");
+ LAUNCH_COMMANDS.add("-main");
+ }
+
+ /** The default build file name. {@value} */
+ public static final String DEFAULT_BUILD_FILENAME = "build.xml";
+
+ /** Our current message output status. Follows Project.MSG_XXX. */
+ private int msgOutputLevel = Project.MSG_INFO;
+
+ /** File that we are using for configuration. */
+ private File buildFile; /* null */
+
+ /** Stream to use for logging. */
+ private static PrintStream out = System.out;
+
+ /** Stream that we are using for logging error messages. */
+ private static PrintStream err = System.err;
+
+ /** The build targets. */
+ private Vector targets = new Vector();
+
+ /** Set of properties that can be used by tasks. */
+ private Properties definedProps = new Properties();
+
+ /** Names of classes to add as listeners to project. */
+ private Vector listeners = new Vector(1);
+
+ /** File names of property files to load on startup. */
+ private Vector propertyFiles = new Vector(1);
+
+ /** Indicates whether this build is to support interactive input */
+ private boolean allowInput = true;
+
+ /** keep going mode */
+ private boolean keepGoingMode = false;
+
+ /**
+ * The Ant logger class. There may be only one logger. It will have
+ * the right to use the 'out' PrintStream. The class must implements the
+ * BuildLogger interface.
+ */
+ private String loggerClassname = null;
+
+ /**
+ * The Ant InputHandler class. There may be only one input
+ * handler.
+ */
+ private String inputHandlerClassname = null;
+
+ /**
+ * Whether or not output to the log is to be unadorned.
+ */
+ private boolean emacsMode = false;
+
+ /**
+ * Whether or not this instance has successfully been
+ * constructed and is ready to run.
+ */
+ private boolean readyToRun = false;
+
+ /**
+ * Whether or not we should only parse and display the project help
+ * information.
+ */
+ private boolean projectHelp = false;
+
+ /**
+ * Whether or not a logfile is being used. This is used to
+ * check if the output streams must be closed.
+ */
+ private static boolean isLogFileUsed = false;
+
+ /**
+ * optional thread priority
+ */
+ private Integer threadPriority = null;
+
+ /**
+ * proxy flag: default is false
+ */
+ private boolean proxy = false;
+
+ /**
+ * Target at which to break execution sequence
+ */
+ private String breakAt = null;
+
+ /**
+ * Prints the message of the Throwable if it (the message) is not
+ * <code>null</code>.
+ *
+ * @param t Throwable to print the message of.
+ * Must not be <code>null</code>.
+ */
+ private static void printMessage(Throwable t) {
+ String message = t.getMessage();
+ if (message != null) {
+ System.err.println(message);
+ }
+ }
+
+ /**
+ * Creates a new instance of this class using the
+ * arguments specified, gives it any extra user properties which have been
+ * specified, and then runs the build using the classloader provided.
+ *
+ * @param args Command line arguments. Must not be <code>null</code>.
+ * @param additionalUserProperties Any extra properties to use in this
+ * build. May be <code>null</code>, which is the equivalent to
+ * passing in an empty set of properties.
+ * @param coreLoader Classloader used for core classes. May be
+ * <code>null</code> in which case the system classloader is used.
+ */
+ public static void start(String[] args, Properties additionalUserProperties,
+ ClassLoader coreLoader) {
+ Main m = new Main();
+ m.startAnt(args, additionalUserProperties, coreLoader);
+ }
+
+ /**
+ * Start Ant
+ * @param args command line args
+ * @param additionalUserProperties properties to set beyond those that
+ * may be specified on the args list
+ * @param coreLoader - not used
+ *
+ * @since Ant 1.6
+ */
+ public void startAnt(String[] args, Properties additionalUserProperties,
+ ClassLoader coreLoader) {
+
+ try {
+ processArgs(args);
+ } catch (Throwable exc) {
+ handleLogfile();
+ printMessage(exc);
+ exit(1);
+ return;
+ }
+
+ if (additionalUserProperties != null) {
+ for (Enumeration e = additionalUserProperties.keys();
+ e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ String property = additionalUserProperties.getProperty(key);
+ definedProps.put(key, property);
+ }
+ }
+
+ // expect the worst
+ int exitCode = 1;
+ try {
+ try {
+ runBuild(coreLoader);
+ exitCode = 0;
+ } catch (ExitStatusException ese) {
+ exitCode = ese.getStatus();
+ if (exitCode != 0) {
+ throw ese;
+ }
+ }
+ } catch (BuildException be) {
+ if (err != System.err) {
+ printMessage(be);
+ }
+ } catch (Throwable exc) {
+ exc.printStackTrace();
+ printMessage(exc);
+ } finally {
+ handleLogfile();
+ }
+ exit(exitCode);
+ }
+
+ /**
+ * This operation is expected to call {@link System#exit(int)}, which
+ * is what the base version does.
+ * However, it is possible to do something else.
+ * @param exitCode code to exit with
+ */
+ protected void exit(int exitCode) {
+ System.exit(exitCode);
+ }
+
+ /**
+ * Close logfiles, if we have been writing to them.
+ *
+ * @since Ant 1.6
+ */
+ private static void handleLogfile() {
+ if (isLogFileUsed) {
+ FileUtils.close(out);
+ FileUtils.close(err);
+ }
+ }
+
+ /**
+ * Command line entry point. This method kicks off the building
+ * of a project object and executes a build using either a given
+ * target or the default target.
+ *
+ * @param args Command line arguments. Must not be <code>null</code>.
+ */
+ public static void main(String[] args) {
+ start(args, null, null);
+ }
+
+ /**
+ * Constructor used when creating Main for later arg processing
+ * and startup
+ */
+ public Main() {
+ }
+
+ /**
+ * Sole constructor, which parses and deals with command line
+ * arguments.
+ *
+ * @param args Command line arguments. Must not be <code>null</code>.
+ *
+ * @exception BuildException if the specified build file doesn't exist
+ * or is a directory.
+ *
+ * @deprecated since 1.6.x
+ */
+ protected Main(String[] args) throws BuildException {
+ processArgs(args);
+ }
+
+ /**
+ * Process command line arguments.
+ * When ant is started from Launcher, launcher-only arguments do not get
+ * passed through to this routine.
+ *
+ * @param args the command line arguments.
+ *
+ * @since Ant 1.6
+ */
+ private void processArgs(String[] args) {
+ String searchForThis = null;
+ boolean searchForFile = false;
+ PrintStream logTo = null;
+
+ // cycle through given args
+
+ boolean justPrintUsage = false;
+ boolean justPrintVersion = false;
+ boolean justPrintDiagnostics = false;
+
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+
+ if (arg.equals("-help") || arg.equals("-h")) {
+ justPrintUsage = true;
+ } else if (arg.equals("-version")) {
+ justPrintVersion = true;
+ } else if (arg.equals("-diagnostics")) {
+ justPrintDiagnostics = true;
+ } else if (arg.equals("-quiet") || arg.equals("-q")) {
+ msgOutputLevel = Project.MSG_WARN;
+ } else if (arg.equals("-verbose") || arg.equals("-v")) {
+ msgOutputLevel = Project.MSG_VERBOSE;
+ } else if (arg.equals("-debug") || arg.equals("-d")) {
+ msgOutputLevel = Project.MSG_DEBUG;
+ } else if (arg.equals("-noinput")) {
+ allowInput = false;
+ } else if (arg.equals("-logfile") || arg.equals("-l")) {
+ try {
+ File logFile = new File(args[i + 1]);
+ i++;
+ logTo = new PrintStream(new FileOutputStream(logFile));
+ isLogFileUsed = true;
+ } catch (IOException ioe) {
+ String msg = "Cannot write on the specified log file. "
+ + "Make sure the path exists and you have write "
+ + "permissions.";
+ throw new BuildException(msg);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ String msg = "You must specify a log file when "
+ + "using the -log argument";
+ throw new BuildException(msg);
+ }
+ } else if (arg.equals("-buildfile") || arg.equals("-file")
+ || arg.equals("-f")) {
+ i = handleArgBuildFile(args, i);
+ } else if (arg.equals("-listener")) {
+ i = handleArgListener(args, i);
+ } else if (arg.startsWith("-D")) {
+ i = handleArgDefine(args, i);
+ } else if (arg.equals("-logger")) {
+ i = handleArgLogger(args, i);
+ } else if (arg.equals("-inputhandler")) {
+ i = handleArgInputHandler(args, i);
+ } else if (arg.equals("-emacs") || arg.equals("-e")) {
+ emacsMode = true;
+ } else if (arg.equals("-projecthelp") || arg.equals("-p")) {
+ // set the flag to display the targets and quit
+ projectHelp = true;
+ } else if (arg.equals("-find") || arg.equals("-s")) {
+ searchForFile = true;
+ // eat up next arg if present, default to build.xml
+ if (i < args.length - 1) {
+ searchForThis = args[++i];
+ }
+ } else if (arg.startsWith("-propertyfile")) {
+ i = handleArgPropertyFile(args, i);
+ } else if (arg.equals("-k") || arg.equals("-keep-going")) {
+ keepGoingMode = true;
+ } else if (arg.equals("-nice")) {
+ i = handleArgNice(args, i);
+ } else if (LAUNCH_COMMANDS.contains(arg)) {
+ //catch script/ant mismatch with a meaningful message
+ //we could ignore it, but there are likely to be other
+ //version problems, so we stamp down on the configuration now
+ String msg = "Ant's Main method is being handed "
+ + "an option " + arg + " that is only for the launcher class."
+ + "\nThis can be caused by a version mismatch between "
+ + "the ant script/.bat file and Ant itself.";
+ throw new BuildException(msg);
+ } else if (arg.equals("-autoproxy")) {
+ proxy = true;
+ } else if (arg.equals("-breakAt")) {
+ breakAt = args[++i];
+ } else if (arg.startsWith("-")) {
+ // we don't have any more args to recognize!
+ String msg = "Unknown argument: " + arg;
+ System.err.println(msg);
+ printUsage();
+ throw new BuildException("");
+ } else {
+ // if it's no other arg, it may be the target
+ targets.addElement(arg);
+ }
+ }
+
+ if (msgOutputLevel >= Project.MSG_VERBOSE || justPrintVersion) {
+ printVersion(msgOutputLevel);
+ }
+
+ if (justPrintUsage || justPrintVersion || justPrintDiagnostics) {
+ if (justPrintUsage) {
+ printUsage();
+ }
+ if (justPrintDiagnostics) {
+ Diagnostics.doReport(System.out, msgOutputLevel);
+ }
+ return;
+ }
+
+ // if buildFile was not specified on the command line,
+ if (buildFile == null) {
+ // but -find then search for it
+ if (searchForFile) {
+ if (searchForThis != null) {
+ buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis);
+ if (buildFile == null) {
+ throw new BuildException("Could not locate a build file!");
+ }
+ } else {
+ // no search file specified: so search an existing default file
+ Iterator it = ProjectHelperRepository.getInstance().getHelpers();
+ do {
+ ProjectHelper helper = (ProjectHelper) it.next();
+ searchForThis = helper.getDefaultBuildFile();
+ if (msgOutputLevel >= Project.MSG_VERBOSE) {
+ System.out.println("Searching the default build file: " + searchForThis);
+ }
+ buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis);
+ } while (buildFile == null && it.hasNext());
+ if (buildFile == null) {
+ throw new BuildException("Could not locate a build file!");
+ }
+ }
+ } else {
+ // no build file specified: so search an existing default file
+ Iterator it = ProjectHelperRepository.getInstance().getHelpers();
+ do {
+ ProjectHelper helper = (ProjectHelper) it.next();
+ buildFile = new File(helper.getDefaultBuildFile());
+ if (msgOutputLevel >= Project.MSG_VERBOSE) {
+ System.out.println("Trying the default build file: " + buildFile);
+ }
+ } while (!buildFile.exists() && it.hasNext());
+ }
+ }
+
+ // make sure buildfile exists
+ if (!buildFile.exists()) {
+ System.out.println("Buildfile: " + buildFile + " does not exist!");
+ throw new BuildException("Build failed");
+ }
+
+ // make sure it's not a directory (this falls into the ultra
+ // paranoid lets check everything category
+
+ if (buildFile.isDirectory()) {
+ System.out.println("What? Buildfile: " + buildFile + " is a dir!");
+ throw new BuildException("Build failed");
+ }
+
+ // Normalize buildFile for re-import detection
+ buildFile =
+ FileUtils.getFileUtils().normalize(buildFile.getAbsolutePath());
+
+ // Load the property files specified by -propertyfile
+ loadPropertyFiles();
+
+ if (msgOutputLevel >= Project.MSG_INFO) {
+ System.out.println("Buildfile: " + buildFile);
+ }
+
+ if (logTo != null) {
+ out = logTo;
+ err = logTo;
+ System.setOut(out);
+ System.setErr(err);
+ }
+ readyToRun = true;
+ }
+
+ // --------------------------------------------------------
+ // Methods for handling the command line arguments
+ // --------------------------------------------------------
+
+ /** Handle the -buildfile, -file, -f argument */
+ private int handleArgBuildFile(String[] args, int pos) {
+ try {
+ buildFile = new File(
+ args[++pos].replace('/', File.separatorChar));
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ throw new BuildException(
+ "You must specify a buildfile when using the -buildfile argument");
+ }
+ return pos;
+ }
+
+ /** Handle -listener argument */
+ private int handleArgListener(String[] args, int pos) {
+ try {
+ listeners.addElement(args[pos + 1]);
+ pos++;
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ String msg = "You must specify a classname when "
+ + "using the -listener argument";
+ throw new BuildException(msg);
+ }
+ return pos;
+ }
+
+ /** Handler -D argument */
+ private int handleArgDefine(String[] args, int argPos) {
+ /* Interestingly enough, we get to here when a user
+ * uses -Dname=value. However, in some cases, the OS
+ * goes ahead and parses this out to args
+ * {"-Dname", "value"}
+ * so instead of parsing on "=", we just make the "-D"
+ * characters go away and skip one argument forward.
+ *
+ * I don't know how to predict when the JDK is going
+ * to help or not, so we simply look for the equals sign.
+ */
+ String arg = args[argPos];
+ String name = arg.substring(2, arg.length());
+ String value = null;
+ int posEq = name.indexOf("=");
+ if (posEq > 0) {
+ value = name.substring(posEq + 1);
+ name = name.substring(0, posEq);
+ } else if (argPos < args.length - 1) {
+ value = args[++argPos];
+ } else {
+ throw new BuildException("Missing value for property "
+ + name);
+ }
+ definedProps.put(name, value);
+ return argPos;
+ }
+
+ /** Handle the -logger argument. */
+ private int handleArgLogger(String[] args, int pos) {
+ if (loggerClassname != null) {
+ throw new BuildException(
+ "Only one logger class may be specified.");
+ }
+ try {
+ loggerClassname = args[++pos];
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ throw new BuildException(
+ "You must specify a classname when using the -logger argument");
+ }
+ return pos;
+ }
+
+ /** Handle the -inputhandler argument. */
+ private int handleArgInputHandler(String[] args, int pos) {
+ if (inputHandlerClassname != null) {
+ throw new BuildException("Only one input handler class may "
+ + "be specified.");
+ }
+ try {
+ inputHandlerClassname = args[++pos];
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ throw new BuildException("You must specify a classname when"
+ + " using the -inputhandler"
+ + " argument");
+ }
+ return pos;
+ }
+
+ /** Handle the -propertyfile argument. */
+ private int handleArgPropertyFile(String[] args, int pos) {
+ try {
+ propertyFiles.addElement(args[++pos]);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ String msg = "You must specify a property filename when "
+ + "using the -propertyfile argument";
+ throw new BuildException(msg);
+ }
+ return pos;
+ }
+
+ /** Handle the -nice argument. */
+ private int handleArgNice(String[] args, int pos) {
+ try {
+ threadPriority = Integer.decode(args[++pos]);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ throw new BuildException(
+ "You must supply a niceness value (1-10)"
+ + " after the -nice option");
+ } catch (NumberFormatException e) {
+ throw new BuildException("Unrecognized niceness value: "
+ + args[pos]);
+ }
+
+ if (threadPriority.intValue() < Thread.MIN_PRIORITY
+ || threadPriority.intValue() > Thread.MAX_PRIORITY) {
+ throw new BuildException(
+ "Niceness value is out of the range 1-10");
+ }
+ return pos;
+ }
+
+ // --------------------------------------------------------
+ // other methods
+ // --------------------------------------------------------
+
+ /** Load the property files specified by -propertyfile */
+ private void loadPropertyFiles() {
+ for (int propertyFileIndex = 0;
+ propertyFileIndex < propertyFiles.size();
+ propertyFileIndex++) {
+ String filename
+ = (String) propertyFiles.elementAt(propertyFileIndex);
+ Properties props = new Properties();
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(filename);
+ props.load(fis);
+ } catch (IOException e) {
+ System.out.println("Could not load property file "
+ + filename + ": " + e.getMessage());
+ } finally {
+ FileUtils.close(fis);
+ }
+
+ // ensure that -D properties take precedence
+ Enumeration propertyNames = props.propertyNames();
+ while (propertyNames.hasMoreElements()) {
+ String name = (String) propertyNames.nextElement();
+ if (definedProps.getProperty(name) == null) {
+ definedProps.put(name, props.getProperty(name));
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper to get the parent file for a given file.
+ * <p>
+ * Added to simulate File.getParentFile() from JDK 1.2.
+ * @deprecated since 1.6.x
+ *
+ * @param file File to find parent of. Must not be <code>null</code>.
+ * @return Parent file or null if none
+ */
+ private File getParentFile(File file) {
+ File parent = file.getParentFile();
+
+ if (parent != null && msgOutputLevel >= Project.MSG_VERBOSE) {
+ System.out.println("Searching in " + parent.getAbsolutePath());
+ }
+
+ return parent;
+ }
+
+ /**
+ * Search parent directories for the build file.
+ * <p>
+ * Takes the given target as a suffix to append to each
+ * parent directory in search of a build file. Once the
+ * root of the file-system has been reached <code>null</code>
+ * is returned.
+ *
+ * @param start Leaf directory of search.
+ * Must not be <code>null</code>.
+ * @param suffix Suffix filename to look for in parents.
+ * Must not be <code>null</code>.
+ *
+ * @return A handle to the build file if one is found, <code>null</code> if not
+ */
+ private File findBuildFile(String start, String suffix) {
+ if (msgOutputLevel >= Project.MSG_INFO) {
+ System.out.println("Searching for " + suffix + " ...");
+ }
+
+ File parent = new File(new File(start).getAbsolutePath());
+ File file = new File(parent, suffix);
+
+ // check if the target file exists in the current directory
+ while (!file.exists()) {
+ // change to parent directory
+ parent = getParentFile(parent);
+
+ // if parent is null, then we are at the root of the fs,
+ // complain that we can't find the build file.
+ if (parent == null) {
+ return null;
+ }
+
+ // refresh our file handle
+ file = new File(parent, suffix);
+ }
+
+ return file;
+ }
+
+ /**
+ * Executes the build. If the constructor for this instance failed
+ * (e.g. returned after issuing a warning), this method returns
+ * immediately.
+ *
+ * @param coreLoader The classloader to use to find core classes.
+ * May be <code>null</code>, in which case the
+ * system classloader is used.
+ *
+ * @exception BuildException if the build fails
+ */
+ private void runBuild(ClassLoader coreLoader) throws BuildException {
+
+ if (!readyToRun) {
+ return;
+ }
+
+ final Project project = new Project();
+ project.setCoreLoader(coreLoader);
+
+ Throwable error = null;
+
+ try {
+ addBuildListeners(project);
+ addInputHandler(project);
+
+ PrintStream savedErr = System.err;
+ PrintStream savedOut = System.out;
+ InputStream savedIn = System.in;
+
+ // use a system manager that prevents from System.exit()
+ SecurityManager oldsm = null;
+ oldsm = System.getSecurityManager();
+
+ //SecurityManager can not be installed here for backwards
+ //compatibility reasons (PD). Needs to be loaded prior to
+ //ant class if we are going to implement it.
+ //System.setSecurityManager(new NoExitSecurityManager());
+ try {
+ if (allowInput) {
+ project.setDefaultInputStream(System.in);
+ }
+ System.setIn(new DemuxInputStream(project));
+ System.setOut(new PrintStream(new DemuxOutputStream(project, false)));
+ System.setErr(new PrintStream(new DemuxOutputStream(project, true)));
+
+
+ if (!projectHelp) {
+ project.fireBuildStarted();
+ }
+
+ // set the thread priorities
+ if (threadPriority != null) {
+ try {
+ project.log("Setting Ant's thread priority to "
+ + threadPriority, Project.MSG_VERBOSE);
+ Thread.currentThread().setPriority(threadPriority.intValue());
+ } catch (SecurityException swallowed) {
+ //we cannot set the priority here.
+ project.log("A security manager refused to set the -nice value");
+ }
+ }
+
+
+
+ project.init();
+ project.setBreakAt(breakAt);
+ // resolve properties
+ PropertyHelper propertyHelper
+ = (PropertyHelper) PropertyHelper.getPropertyHelper(project);
+ HashMap props = new HashMap(definedProps);
+ new ResolvePropertyMap(project, propertyHelper,
+ propertyHelper.getExpanders())
+ .resolveAllProperties(props, null, false);
+
+ // set user-define properties
+ for (Iterator e = props.entrySet().iterator(); e.hasNext(); ) {
+ Map.Entry ent = (Map.Entry) e.next();
+ String arg = (String) ent.getKey();
+ Object value = ent.getValue();
+ project.setUserProperty(arg, String.valueOf(value));
+ }
+
+ project.setUserProperty(MagicNames.ANT_FILE,
+ buildFile.getAbsolutePath());
+ project.setUserProperty(MagicNames.ANT_FILE_TYPE,
+ MagicNames.ANT_FILE_TYPE_FILE);
+
+ project.setKeepGoingMode(keepGoingMode);
+ if (proxy) {
+ //proxy setup if enabled
+ ProxySetup proxySetup = new ProxySetup(project);
+ proxySetup.enableProxies();
+ }
+
+ ProjectHelper.configureProject(project, buildFile);
+
+ if (projectHelp) {
+ printDescription(project);
+ printTargets(project, msgOutputLevel > Project.MSG_INFO,
+ msgOutputLevel > Project.MSG_VERBOSE);
+ return;
+ }
+
+ // make sure that we have a target to execute
+ if (targets.size() == 0) {
+ if (project.getDefaultTarget() != null) {
+ targets.addElement(project.getDefaultTarget());
+ }
+ }
+
+ project.executeTargets(targets);
+ } finally {
+ // put back the original security manager
+ //The following will never eval to true. (PD)
+ if (oldsm != null) {
+ System.setSecurityManager(oldsm);
+ }
+
+ System.setOut(savedOut);
+ System.setErr(savedErr);
+ System.setIn(savedIn);
+ }
+ } catch (RuntimeException exc) {
+ error = exc;
+ throw exc;
+ } catch (Error e) {
+ error = e;
+ throw e;
+ } finally {
+ if (!projectHelp) {
+ try {
+ project.fireBuildFinished(error);
+ } catch (Throwable t) {
+ // yes, I know it is bad style to catch Throwable,
+ // but if we don't, we lose valuable information
+ System.err.println("Caught an exception while logging the"
+ + " end of the build. Exception was:");
+ t.printStackTrace();
+ if (error != null) {
+ System.err.println("There has been an error prior to"
+ + " that:");
+ error.printStackTrace();
+ }
+ throw new BuildException(t);
+ }
+ } else if (error != null) {
+ project.log(error.toString(), Project.MSG_ERR);
+ }
+ }
+ }
+
+ /**
+ * Adds the listeners specified in the command line arguments,
+ * along with the default listener, to the specified project.
+ *
+ * @param project The project to add listeners to.
+ * Must not be <code>null</code>.
+ */
+ protected void addBuildListeners(Project project) {
+
+ // Add the default listener
+ project.addBuildListener(createLogger());
+
+ final int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ String className = (String) listeners.elementAt(i);
+ BuildListener listener =
+ (BuildListener) ClasspathUtils.newInstance(className,
+ Main.class.getClassLoader(), BuildListener.class);
+ project.setProjectReference(listener);
+
+ project.addBuildListener(listener);
+ }
+ }
+
+ /**
+ * Creates the InputHandler and adds it to the project.
+ *
+ * @param project the project instance.
+ *
+ * @exception BuildException if a specified InputHandler
+ * implementation could not be loaded.
+ */
+ private void addInputHandler(Project project) throws BuildException {
+ InputHandler handler = null;
+ if (inputHandlerClassname == null) {
+ handler = new DefaultInputHandler();
+ } else {
+ handler = (InputHandler) ClasspathUtils.newInstance(
+ inputHandlerClassname, Main.class.getClassLoader(),
+ InputHandler.class);
+ project.setProjectReference(handler);
+ }
+ project.setInputHandler(handler);
+ }
+
+ // XXX: (Jon Skeet) Any reason for writing a message and then using a bare
+ // RuntimeException rather than just using a BuildException here? Is it
+ // in case the message could end up being written to no loggers (as the
+ // loggers could have failed to be created due to this failure)?
+ /**
+ * Creates the default build logger for sending build events to the ant
+ * log.
+ *
+ * @return the logger instance for this build.
+ */
+ private BuildLogger createLogger() {
+ BuildLogger logger = null;
+ if (loggerClassname != null) {
+ try {
+ logger = (BuildLogger) ClasspathUtils.newInstance(
+ loggerClassname, Main.class.getClassLoader(),
+ BuildLogger.class);
+ } catch (BuildException e) {
+ System.err.println("The specified logger class "
+ + loggerClassname
+ + " could not be used because " + e.getMessage());
+ throw new RuntimeException();
+ }
+ } else {
+ logger = new DefaultLogger();
+ }
+
+ logger.setMessageOutputLevel(msgOutputLevel);
+ logger.setOutputPrintStream(out);
+ logger.setErrorPrintStream(err);
+ logger.setEmacsMode(emacsMode);
+
+ return logger;
+ }
+
+ /**
+ * Prints the usage information for this class to <code>System.out</code>.
+ */
+ private static void printUsage() {
+ String lSep = System.getProperty("line.separator");
+ StringBuffer msg = new StringBuffer();
+ msg.append("ant [options] [target [target2 [target3] ...]]" + lSep);
+ msg.append("Options: " + lSep);
+ msg.append(" -help, -h print this message" + lSep);
+ msg.append(" -projecthelp, -p print project help information" + lSep);
+ msg.append(" -version print the version information and exit" + lSep);
+ msg.append(" -diagnostics print information that might be helpful to" + lSep);
+ msg.append(" diagnose or report problems." + lSep);
+ msg.append(" -quiet, -q be extra quiet" + lSep);
+ msg.append(" -verbose, -v be extra verbose" + lSep);
+ msg.append(" -debug, -d print debugging information" + lSep);
+ msg.append(" -emacs, -e produce logging information without adornments"
+ + lSep);
+ msg.append(" -lib <path> specifies a path to search for jars and classes"
+ + lSep);
+ msg.append(" -logfile <file> use given file for log" + lSep);
+ msg.append(" -l <file> ''" + lSep);
+ msg.append(" -logger <classname> the class which is to perform logging" + lSep);
+ msg.append(" -listener <classname> add an instance of class as a project listener"
+ + lSep);
+ msg.append(" -noinput do not allow interactive input" + lSep);
+ msg.append(" -buildfile <file> use given buildfile" + lSep);
+ msg.append(" -file <file> ''" + lSep);
+ msg.append(" -f <file> ''" + lSep);
+ msg.append(" -D<property>=<value> use value for given property" + lSep);
+ msg.append(" -keep-going, -k execute all targets that do not depend" + lSep);
+ msg.append(" on failed target(s)" + lSep);
+ msg.append(" -propertyfile <name> load all properties from file with -D" + lSep);
+ msg.append(" properties taking precedence" + lSep);
+ msg.append(" -inputhandler <class> the class which will handle input requests" + lSep);
+ msg.append(" -find <file> (s)earch for buildfile towards the root of" + lSep);
+ msg.append(" -s <file> the filesystem and use it" + lSep);
+ msg.append(" -nice number A niceness value for the main thread:" + lSep
+ + " 1 (lowest) to 10 (highest); 5 is the default"
+ + lSep);
+ msg.append(" -nouserlib Run ant without using the jar files from" + lSep
+ + " ${user.home}/.ant/lib" + lSep);
+ msg.append(" -noclasspath Run ant without using CLASSPATH" + lSep);
+ msg.append(" -autoproxy Java1.5+: use the OS proxy settings"
+ + lSep);
+ msg.append(" -main <class> override Ant's normal entry point");
+ System.out.println(msg.toString());
+ }
+
+ /**
+ * Prints the Ant version information to <code>System.out</code>.
+ *
+ * @exception BuildException if the version information is unavailable
+ */
+ private static void printVersion(int logLevel) throws BuildException {
+ System.out.println(getAntVersion());
+ }
+
+ /**
+ * Cache of the Ant version information when it has been loaded.
+ */
+ private static String antVersion = null;
+
+ /**
+ * Returns the Ant version information, if available. Once the information
+ * has been loaded once, it's cached and returned from the cache on future
+ * calls.
+ *
+ * @return the Ant version information as a String
+ * (always non-<code>null</code>)
+ *
+ * @exception BuildException if the version information is unavailable
+ */
+ public static synchronized String getAntVersion() throws BuildException {
+ if (antVersion == null) {
+ try {
+ Properties props = new Properties();
+ InputStream in =
+ Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt");
+ props.load(in);
+ in.close();
+
+ StringBuffer msg = new StringBuffer();
+ msg.append("Apache Ant(TM) version ");
+ msg.append(props.getProperty("VERSION"));
+ msg.append(" compiled on ");
+ msg.append(props.getProperty("DATE"));
+ antVersion = msg.toString();
+ } catch (IOException ioe) {
+ throw new BuildException("Could not load the version information:"
+ + ioe.getMessage());
+ } catch (NullPointerException npe) {
+ throw new BuildException("Could not load the version information.");
+ }
+ }
+ return antVersion;
+ }
+
+ /**
+ * Prints the description of a project (if there is one) to
+ * <code>System.out</code>.
+ *
+ * @param project The project to display a description of.
+ * Must not be <code>null</code>.
+ */
+ private static void printDescription(Project project) {
+ if (project.getDescription() != null) {
+ project.log(project.getDescription());
+ }
+ }
+
+ /**
+ * Targets in imported files with a project name
+ * and not overloaded by the main build file will
+ * be in the target map twice. This method
+ * removes the duplicate target.
+ * @param targets the targets to filter.
+ * @return the filtered targets.
+ */
+ private static Map removeDuplicateTargets(Map targets) {
+ Map locationMap = new HashMap();
+ for (Iterator i = targets.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String name = (String) entry.getKey();
+ Target target = (Target) entry.getValue();
+ Target otherTarget =
+ (Target) locationMap.get(target.getLocation());
+ // Place this entry in the location map if
+ // a) location is not in the map
+ // b) location is in map, but it's name is longer
+ // (an imported target will have a name. prefix)
+ if (otherTarget == null
+ || otherTarget.getName().length() > name.length()) {
+ locationMap.put(
+ target.getLocation(), target); // Smallest name wins
+ }
+ }
+ Map ret = new HashMap();
+ for (Iterator i = locationMap.values().iterator(); i.hasNext();) {
+ Target target = (Target) i.next();
+ ret.put(target.getName(), target);
+ }
+ return ret;
+ }
+
+ /**
+ * Prints a list of all targets in the specified project to
+ * <code>System.out</code>, optionally including subtargets.
+ *
+ * @param project The project to display a description of.
+ * Must not be <code>null</code>.
+ * @param printSubTargets Whether or not subtarget names should also be
+ * printed.
+ */
+ private static void printTargets(Project project, boolean printSubTargets,
+ boolean printDependencies) {
+ // find the target with the longest name
+ int maxLength = 0;
+ Map ptargets = removeDuplicateTargets(project.getTargets());
+ String targetName;
+ String targetDescription;
+ Target currentTarget;
+ // split the targets in top-level and sub-targets depending
+ // on the presence of a description
+ Vector topNames = new Vector();
+ Vector topDescriptions = new Vector();
+ Vector/*<Enumeration<String>>*/ topDependencies = new Vector();
+ Vector subNames = new Vector();
+ Vector/*<Enumeration<String>>*/ subDependencies = new Vector();
+
+ for (Iterator i = ptargets.values().iterator(); i.hasNext();) {
+ currentTarget = (Target) i.next();
+ targetName = currentTarget.getName();
+ if (targetName.equals("")) {
+ continue;
+ }
+ targetDescription = currentTarget.getDescription();
+ // maintain a sorted list of targets
+ if (targetDescription == null) {
+ int pos = findTargetPosition(subNames, targetName);
+ subNames.insertElementAt(targetName, pos);
+ if (printDependencies) {
+ subDependencies.insertElementAt(currentTarget.getDependencies(), pos);
+ }
+ } else {
+ int pos = findTargetPosition(topNames, targetName);
+ topNames.insertElementAt(targetName, pos);
+ topDescriptions.insertElementAt(targetDescription, pos);
+ if (targetName.length() > maxLength) {
+ maxLength = targetName.length();
+ }
+ if (printDependencies) {
+ topDependencies.insertElementAt(currentTarget.getDependencies(), pos);
+ }
+ }
+ }
+
+ printTargets(project, topNames, topDescriptions, topDependencies,
+ "Main targets:", maxLength);
+ //if there were no main targets, we list all subtargets
+ //as it means nothing has a description
+ if (topNames.size() == 0) {
+ printSubTargets = true;
+ }
+ if (printSubTargets) {
+ printTargets(project, subNames, null, subDependencies, "Other targets:", 0);
+ }
+
+ String defaultTarget = project.getDefaultTarget();
+ if (defaultTarget != null && !"".equals(defaultTarget)) {
+ // shouldn't need to check but...
+ project.log("Default target: " + defaultTarget);
+ }
+ }
+
+ /**
+ * Searches for the correct place to insert a name into a list so as
+ * to keep the list sorted alphabetically.
+ *
+ * @param names The current list of names. Must not be <code>null</code>.
+ * @param name The name to find a place for.
+ * Must not be <code>null</code>.
+ *
+ * @return the correct place in the list for the given name
+ */
+ private static int findTargetPosition(Vector names, String name) {
+ final int size = names.size();
+ int res = size;
+ for (int i = 0; i < size && res == size; i++) {
+ if (name.compareTo((String) names.elementAt(i)) < 0) {
+ res = i;
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Writes a formatted list of target names to <code>System.out</code>
+ * with an optional description.
+ *
+ *
+ * @param project the project instance.
+ * @param names The names to be printed.
+ * Must not be <code>null</code>.
+ * @param descriptions The associated target descriptions.
+ * May be <code>null</code>, in which case
+ * no descriptions are displayed.
+ * If non-<code>null</code>, this should have
+ * as many elements as <code>names</code>.
+ * @param topDependencies The list of dependencies for each target.
+ * The dependencies are listed as a non null
+ * enumeration of String.
+ * @param heading The heading to display.
+ * Should not be <code>null</code>.
+ * @param maxlen The maximum length of the names of the targets.
+ * If descriptions are given, they are padded to this
+ * position so they line up (so long as the names really
+ * <i>are</i> shorter than this).
+ */
+ private static void printTargets(Project project, Vector names,
+ Vector descriptions, Vector dependencies,
+ String heading,
+ int maxlen) {
+ // now, start printing the targets and their descriptions
+ String lSep = System.getProperty("line.separator");
+ // got a bit annoyed that I couldn't find a pad function
+ String spaces = " ";
+ while (spaces.length() <= maxlen) {
+ spaces += spaces;
+ }
+ StringBuffer msg = new StringBuffer();
+ msg.append(heading + lSep + lSep);
+ final int size = names.size();
+ for (int i = 0; i < size; i++) {
+ msg.append(" ");
+ msg.append(names.elementAt(i));
+ if (descriptions != null) {
+ msg.append(
+ spaces.substring(0, maxlen - ((String) names.elementAt(i)).length() + 2));
+ msg.append(descriptions.elementAt(i));
+ }
+ msg.append(lSep);
+ if (!dependencies.isEmpty()) {
+ Enumeration deps = (Enumeration) dependencies.elementAt(i);
+ if (deps.hasMoreElements()) {
+ msg.append(" depends on: ");
+ while (deps.hasMoreElements()) {
+ msg.append(deps.nextElement());
+ if (deps.hasMoreElements()) {
+ msg.append(", ");
+ }
+ }
+ msg.append(lSep);
+ }
+ }
+ }
+ project.log(msg.toString(), Project.MSG_WARN);
+ }
+}
diff --git a/command-line-debugger/src/main/org/apache/tools/ant/Project.java b/command-line-debugger/src/main/org/apache/tools/ant/Project.java
new file mode 100644
index 0000000..5a0a0a9
--- /dev/null
+++ b/command-line-debugger/src/main/org/apache/tools/ant/Project.java
@@ -0,0 +1,2637 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.WeakHashMap;
+
+import org.apache.tools.ant.helper.DefaultExecutor;
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.types.Description;
+import org.apache.tools.ant.types.FilterSet;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.VectorSet;
+
+/**
+ * Central representation of an Ant project. This class defines an Ant project
+ * with all of its targets, tasks and various other properties. It also provides
+ * the mechanism to kick off a build using a particular target name.
+ * <p>
+ * This class also encapsulates methods which allow files to be referred to
+ * using abstract path names which are translated to native system file paths at
+ * runtime.
+ *
+ */
+public class Project implements ResourceFactory {
+ /** Message priority of "error". */
+ public static final int MSG_ERR = 0;
+ /** Message priority of "warning". */
+ public static final int MSG_WARN = 1;
+ /** Message priority of "information". */
+ public static final int MSG_INFO = 2;
+ /** Message priority of "verbose". */
+ public static final int MSG_VERBOSE = 3;
+ /** Message priority of "debug". */
+ public static final int MSG_DEBUG = 4;
+
+ /**
+ * Constant for the "visiting" state, used when traversing a DFS
+ * of target dependencies.
+ */
+ private static final String VISITING = "VISITING";
+ /**
+ * Constant for the "visited" state, used when traversing a DFS of
+ * target dependencies.
+ */
+ private static final String VISITED = "VISITED";
+
+ /**
+ * Version constant for Java 1.0 .
+ *
+ * @deprecated since 1.5.x. Use {@link JavaEnvUtils#JAVA_1_0} instead.
+ */
+ public static final String JAVA_1_0 = JavaEnvUtils.JAVA_1_0;
+ /**
+ * Version constant for Java 1.1 .
+ *
+ * @deprecated since 1.5.x. Use {@link JavaEnvUtils#JAVA_1_1} instead.
+ */
+ public static final String JAVA_1_1 = JavaEnvUtils.JAVA_1_1;
+ /**
+ * Version constant for Java 1.2 .
+ *
+ * @deprecated since 1.5.x. Use {@link JavaEnvUtils#JAVA_1_2} instead.
+ */
+ public static final String JAVA_1_2 = JavaEnvUtils.JAVA_1_2;
+ /**
+ * Version constant for Java 1.3 .
+ *
+ * @deprecated since 1.5.x. Use {@link JavaEnvUtils#JAVA_1_3} instead.
+ */
+ public static final String JAVA_1_3 = JavaEnvUtils.JAVA_1_3;
+ /**
+ * Version constant for Java 1.4 .
+ *
+ * @deprecated since 1.5.x. Use {@link JavaEnvUtils#JAVA_1_4} instead.
+ */
+ public static final String JAVA_1_4 = JavaEnvUtils.JAVA_1_4;
+
+ /** Default filter start token. */
+ public static final String TOKEN_START = FilterSet.DEFAULT_TOKEN_START;
+ /** Default filter end token. */
+ public static final String TOKEN_END = FilterSet.DEFAULT_TOKEN_END;
+
+ /** Instance of a utility class to use for file operations. */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Name of this project. */
+ private String name;
+ /** Description for this project (if any). */
+ private String description;
+
+ /** Map of references within the project (paths etc) (String to Object). */
+ private Hashtable references = new AntRefTable();
+
+ /** Map of id references - used for indicating broken build files */
+ private HashMap idReferences = new HashMap();
+
+ /** the parent project for old id resolution (if inheritreferences is set) */
+ private Project parentIdProject = null;
+
+ /** Name of the project's default target. */
+ private String defaultTarget;
+
+ /** Map from target names to targets (String to Target). */
+ private Hashtable targets = new Hashtable();
+ /** Set of global filters. */
+ private FilterSet globalFilterSet = new FilterSet();
+ {
+ // Initialize the globalFileSet's project
+ globalFilterSet.setProject(this);
+ }
+
+ /**
+ * Wrapper around globalFilterSet. This collection only ever contains one
+ * FilterSet, but the wrapper is needed in order to make it easier to use
+ * the FileUtils interface.
+ */
+ private FilterSetCollection globalFilters = new FilterSetCollection(
+ globalFilterSet);
+
+ /** Project base directory. */
+ private File baseDir;
+
+ /** lock object used when adding/removing listeners */
+ private final Object listenersLock = new Object();
+
+ /** List of listeners to notify of build events. */
+ private volatile BuildListener[] listeners = new BuildListener[0];
+
+ /**
+ * for each thread, record whether it is currently executing messageLogged
+ */
+ private final ThreadLocal isLoggingMessage = new ThreadLocal() {
+ protected Object initialValue() {
+ return Boolean.FALSE;
+ }
+ };
+
+ /**
+ * The Ant core classloader--may be <code>null</code> if using parent
+ * classloader.
+ */
+ private ClassLoader coreLoader = null;
+
+ /** Records the latest task to be executed on a thread. */
+ private final Map/* <Thread,Task> */threadTasks = Collections
+ .synchronizedMap(new WeakHashMap());
+
+ /** Records the latest task to be executed on a thread group. */
+ private final Map/* <ThreadGroup,Task> */threadGroupTasks = Collections
+ .synchronizedMap(new WeakHashMap());
+
+ /**
+ * Called to handle any input requests.
+ */
+ private InputHandler inputHandler = null;
+
+ /**
+ * The default input stream used to read any input.
+ */
+ private InputStream defaultInputStream = null;
+
+ /**
+ * Keep going flag.
+ */
+ private boolean keepGoingMode = false;
+
+ /**
+ * Set the input handler.
+ *
+ * @param handler
+ * the InputHandler instance to use for gathering input.
+ */
+ public void setInputHandler(InputHandler handler) {
+ inputHandler = handler;
+ }
+
+ /**
+ * Set the default System input stream. Normally this stream is set to
+ * System.in. This inputStream is used when no task input redirection is
+ * being performed.
+ *
+ * @param defaultInputStream
+ * the default input stream to use when input is requested.
+ * @since Ant 1.6
+ */
+ public void setDefaultInputStream(InputStream defaultInputStream) {
+ this.defaultInputStream = defaultInputStream;
+ }
+
+ /**
+ * Get this project's input stream.
+ *
+ * @return the InputStream instance in use by this Project instance to read
+ * input.
+ */
+ public InputStream getDefaultInputStream() {
+ return defaultInputStream;
+ }
+
+ /**
+ * Retrieve the current input handler.
+ *
+ * @return the InputHandler instance currently in place for the project
+ * instance.
+ */
+ public InputHandler getInputHandler() {
+ return inputHandler;
+ }
+
+ /**
+ * Create a new Ant project.
+ */
+ public Project() {
+ inputHandler = new DefaultInputHandler();
+ }
+
+ /**
+ * Create and initialize a subproject. By default the subproject will be of
+ * the same type as its parent. If a no-arg constructor is unavailable, the
+ * <code>Project</code> class will be used.
+ *
+ * @return a Project instance configured as a subproject of this Project.
+ * @since Ant 1.7
+ */
+ public Project createSubProject() {
+ Project subProject = null;
+ try {
+ subProject = (Project) (getClass().newInstance());
+ } catch (Exception e) {
+ subProject = new Project();
+ }
+ initSubProject(subProject);
+ return subProject;
+ }
+
+ /**
+ * Initialize a subproject.
+ *
+ * @param subProject
+ * the subproject to initialize.
+ */
+ public void initSubProject(Project subProject) {
+ ComponentHelper.getComponentHelper(subProject).initSubProject(
+ ComponentHelper.getComponentHelper(this));
+ subProject.setDefaultInputStream(getDefaultInputStream());
+ subProject.setKeepGoingMode(this.isKeepGoingMode());
+ subProject.setExecutor(getExecutor().getSubProjectExecutor());
+ }
+
+ /**
+ * Initialise the project.
+ *
+ * This involves setting the default task definitions and loading the system
+ * properties.
+ *
+ * @exception BuildException
+ * if the default task list cannot be loaded.
+ */
+ public void init() throws BuildException {
+ initProperties();
+
+ ComponentHelper.getComponentHelper(this).initDefaultDefinitions();
+ }
+
+ /**
+ * Initializes the properties.
+ *
+ * @exception BuildException
+ * if an vital property could not be set.
+ * @since Ant 1.7
+ */
+ public void initProperties() throws BuildException {
+ setJavaVersionProperty();
+ setSystemProperties();
+ setPropertyInternal(MagicNames.ANT_VERSION, Main.getAntVersion());
+ setAntLib();
+ }
+
+ /**
+ * Set a property to the location of ant.jar. Use the locator to find the
+ * location of the Project.class, and if this is not null, set the property
+ * {@link MagicNames#ANT_LIB} to the result
+ */
+ private void setAntLib() {
+ File antlib = org.apache.tools.ant.launch.Locator
+ .getClassSource(Project.class);
+ if (antlib != null) {
+ setPropertyInternal(MagicNames.ANT_LIB, antlib.getAbsolutePath());
+ }
+ }
+
+ /**
+ * Factory method to create a class loader for loading classes from a given
+ * path.
+ *
+ * @param path
+ * the path from which classes are to be loaded.
+ *
+ * @return an appropriate classloader.
+ */
+ public AntClassLoader createClassLoader(Path path) {
+ return AntClassLoader.newAntClassLoader(getClass().getClassLoader(),
+ this, path, true);
+ }
+
+ /**
+ * Factory method to create a class loader for loading classes from a given
+ * path.
+ *
+ * @param parent
+ * the parent classloader for the new loader.
+ * @param path
+ * the path from which classes are to be loaded.
+ *
+ * @return an appropriate classloader.
+ */
+ public AntClassLoader createClassLoader(ClassLoader parent, Path path) {
+ return AntClassLoader.newAntClassLoader(parent, this, path, true);
+ }
+
+ /**
+ * Set the core classloader for the project. If a <code>null</code>
+ * classloader is specified, the parent classloader should be used.
+ *
+ * @param coreLoader
+ * The classloader to use for the project. May be
+ * <code>null</code>.
+ */
+ public void setCoreLoader(ClassLoader coreLoader) {
+ this.coreLoader = coreLoader;
+ }
+
+ /**
+ * Return the core classloader to use for this project. This may be
+ * <code>null</code>, indicating that the parent classloader should be used.
+ *
+ * @return the core classloader to use for this project.
+ *
+ */
+ public ClassLoader getCoreLoader() {
+ return coreLoader;
+ }
+
+ /**
+ * Add a build listener to the list. This listener will be notified of build
+ * events for this project.
+ *
+ * @param listener
+ * The listener to add to the list. Must not be <code>null</code>
+ * .
+ */
+ public void addBuildListener(BuildListener listener) {
+ synchronized (listenersLock) {
+ // If the listeners already has this listener, do nothing
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] == listener) {
+ return;
+ }
+ }
+ // copy on write semantics
+ BuildListener[] newListeners = new BuildListener[listeners.length + 1];
+ System.arraycopy(listeners, 0, newListeners, 0, listeners.length);
+ newListeners[listeners.length] = listener;
+ listeners = newListeners;
+ }
+ }
+
+ /**
+ * Remove a build listener from the list. This listener will no longer be
+ * notified of build events for this project.
+ *
+ * @param listener
+ * The listener to remove from the list. Should not be
+ * <code>null</code>.
+ */
+ public void removeBuildListener(BuildListener listener) {
+ synchronized (listenersLock) {
+ // copy on write semantics
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] == listener) {
+ BuildListener[] newListeners = new BuildListener[listeners.length - 1];
+ System.arraycopy(listeners, 0, newListeners, 0, i);
+ System.arraycopy(listeners, i + 1, newListeners, i,
+ listeners.length - i - 1);
+ listeners = newListeners;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Return a copy of the list of build listeners for the project.
+ *
+ * @return a list of build listeners for the project
+ */
+ public Vector getBuildListeners() {
+ synchronized (listenersLock) {
+ Vector r = new Vector(listeners.length);
+ for (int i = 0; i < listeners.length; i++) {
+ r.add(listeners[i]);
+ }
+ return r;
+ }
+ }
+
+ /**
+ * Write a message to the log with the default log level of MSG_INFO .
+ *
+ * @param message
+ * The text to log. Should not be <code>null</code>.
+ */
+
+ public void log(String message) {
+ log(message, MSG_INFO);
+ }
+
+ /**
+ * Write a project level message to the log with the given log level.
+ *
+ * @param message
+ * The text to log. Should not be <code>null</code>.
+ * @param msgLevel
+ * The log priority level to use.
+ */
+ public void log(String message, int msgLevel) {
+ log(message, null, msgLevel);
+ }
+
+ /**
+ * Write a project level message to the log with the given log level.
+ *
+ * @param message
+ * The text to log. Should not be <code>null</code>.
+ * @param throwable
+ * The exception causing this log, may be <code>null</code>.
+ * @param msgLevel
+ * The log priority level to use.
+ * @since 1.7
+ */
+ public void log(String message, Throwable throwable, int msgLevel) {
+ fireMessageLogged(this, message, throwable, msgLevel);
+ }
+
+ /**
+ * Write a task level message to the log with the given log level.
+ *
+ * @param task
+ * The task to use in the log. Must not be <code>null</code>.
+ * @param message
+ * The text to log. Should not be <code>null</code>.
+ * @param msgLevel
+ * The log priority level to use.
+ */
+ public void log(Task task, String message, int msgLevel) {
+ fireMessageLogged(task, message, null, msgLevel);
+ }
+
+ /**
+ * Write a task level message to the log with the given log level.
+ *
+ * @param task
+ * The task to use in the log. Must not be <code>null</code>.
+ * @param message
+ * The text to log. Should not be <code>null</code>.
+ * @param throwable
+ * The exception causing this log, may be <code>null</code>.
+ * @param msgLevel
+ * The log priority level to use.
+ * @since 1.7
+ */
+ public void log(Task task, String message, Throwable throwable, int msgLevel) {
+ fireMessageLogged(task, message, throwable, msgLevel);
+ }
+
+ /**
+ * Write a target level message to the log with the given log level.
+ *
+ * @param target
+ * The target to use in the log. Must not be <code>null</code>.
+ * @param message
+ * The text to log. Should not be <code>null</code>.
+ * @param msgLevel
+ * The log priority level to use.
+ */
+ public void log(Target target, String message, int msgLevel) {
+ log(target, message, null, msgLevel);
+ }
+
+ /**
+ * Write a target level message to the log with the given log level.
+ *
+ * @param target
+ * The target to use in the log. Must not be <code>null</code>.
+ * @param message
+ * The text to log. Should not be <code>null</code>.
+ * @param throwable
+ * The exception causing this log, may be <code>null</code>.
+ * @param msgLevel
+ * The log priority level to use.
+ * @since 1.7
+ */
+ public void log(Target target, String message, Throwable throwable,
+ int msgLevel) {
+ fireMessageLogged(target, message, throwable, msgLevel);
+ }
+
+ /**
+ * Return the set of global filters.
+ *
+ * @return the set of global filters.
+ */
+ public FilterSet getGlobalFilterSet() {
+ return globalFilterSet;
+ }
+
+ /**
+ * Set a property. Any existing property of the same name is overwritten,
+ * unless it is a user property.
+ *
+ * @param name
+ * The name of property to set. Must not be <code>null</code>.
+ * @param value
+ * The new value of the property. Must not be <code>null</code>.
+ */
+ public void setProperty(String name, String value) {
+ PropertyHelper.getPropertyHelper(this).setProperty(name, value, true);
+ }
+
+ /**
+ * Set a property if no value currently exists. If the property exists
+ * already, a message is logged and the method returns with no other effect.
+ *
+ * @param name
+ * The name of property to set. Must not be <code>null</code>.
+ * @param value
+ * The new value of the property. Must not be <code>null</code>.
+ * @since 1.5
+ */
+ public void setNewProperty(String name, String value) {
+ PropertyHelper.getPropertyHelper(this).setNewProperty(name, value);
+ }
+
+ /**
+ * Set a user property, which cannot be overwritten by set/unset property
+ * calls. Any previous value is overwritten.
+ *
+ * @param name
+ * The name of property to set. Must not be <code>null</code>.
+ * @param value
+ * The new value of the property. Must not be <code>null</code>.
+ * @see #setProperty(String,String)
+ */
+ public void setUserProperty(String name, String value) {
+ PropertyHelper.getPropertyHelper(this).setUserProperty(name, value);
+ }
+
+ /**
+ * Set a user property, which cannot be overwritten by set/unset property
+ * calls. Any previous value is overwritten. Also marks these properties as
+ * properties that have not come from the command line.
+ *
+ * @param name
+ * The name of property to set. Must not be <code>null</code>.
+ * @param value
+ * The new value of the property. Must not be <code>null</code>.
+ * @see #setProperty(String,String)
+ */
+ public void setInheritedProperty(String name, String value) {
+ PropertyHelper.getPropertyHelper(this)
+ .setInheritedProperty(name, value);
+ }
+
+ /**
+ * Set a property unless it is already defined as a user property (in which
+ * case the method returns silently).
+ *
+ * @param name
+ * The name of the property. Must not be <code>null</code>.
+ * @param value
+ * The property value. Must not be <code>null</code>.
+ */
+ private void setPropertyInternal(String name, String value) {
+ PropertyHelper.getPropertyHelper(this).setProperty(name, value, false);
+ }
+
+ /**
+ * Return the value of a property, if it is set.
+ *
+ * @param propertyName
+ * The name of the property. May be <code>null</code>, in which
+ * case the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match or if a
+ * <code>null</code> name is provided.
+ */
+ public String getProperty(String propertyName) {
+ Object value = PropertyHelper.getPropertyHelper(this).getProperty(
+ propertyName);
+ return value == null ? null : String.valueOf(value);
+ }
+
+ /**
+ * Replace ${} style constructions in the given value with the string value
+ * of the corresponding data types.
+ *
+ * @param value
+ * The string to be scanned for property references. May be
+ * <code>null</code>.
+ *
+ * @return the given string with embedded property names replaced by values,
+ * or <code>null</code> if the given string is <code>null</code>.
+ *
+ * @exception BuildException
+ * if the given value has an unclosed property name, e.g.
+ * <code>${xxx</code>.
+ */
+ public String replaceProperties(String value) throws BuildException {
+ return PropertyHelper.getPropertyHelper(this).replaceProperties(null,
+ value, null);
+ }
+
+ /**
+ * Return the value of a user property, if it is set.
+ *
+ * @param propertyName
+ * The name of the property. May be <code>null</code>, in which
+ * case the return value is also <code>null</code>.
+ * @return the property value, or <code>null</code> for no match or if a
+ * <code>null</code> name is provided.
+ */
+ public String getUserProperty(String propertyName) {
+ return (String) PropertyHelper.getPropertyHelper(this).getUserProperty(
+ propertyName);
+ }
+
+ /**
+ * Return a copy of the properties table.
+ *
+ * @return a hashtable containing all properties (including user
+ * properties).
+ */
+ public Hashtable getProperties() {
+ return PropertyHelper.getPropertyHelper(this).getProperties();
+ }
+
+ /**
+ * Return a copy of the user property hashtable.
+ *
+ * @return a hashtable containing just the user properties.
+ */
+ public Hashtable getUserProperties() {
+ return PropertyHelper.getPropertyHelper(this).getUserProperties();
+ }
+
+ /**
+ * Return a copy of the inherited property hashtable.
+ *
+ * @return a hashtable containing just the inherited properties.
+ * @since Ant 1.8.0
+ */
+ public Hashtable getInheritedProperties() {
+ return PropertyHelper.getPropertyHelper(this).getInheritedProperties();
+ }
+
+ /**
+ * Copy all user properties that have been set on the command line or a GUI
+ * tool from this instance to the Project instance given as the argument.
+ *
+ * <p>
+ * To copy all "user" properties, you will also have to call
+ * {@link #copyInheritedProperties copyInheritedProperties}.
+ * </p>
+ *
+ * @param other
+ * the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.5
+ */
+ public void copyUserProperties(Project other) {
+ PropertyHelper.getPropertyHelper(this).copyUserProperties(other);
+ }
+
+ /**
+ * Copy all user properties that have not been set on the command line or a
+ * GUI tool from this instance to the Project instance given as the
+ * argument.
+ *
+ * <p>
+ * To copy all "user" properties, you will also have to call
+ * {@link #copyUserProperties copyUserProperties}.
+ * </p>
+ *
+ * @param other
+ * the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.5
+ */
+ public void copyInheritedProperties(Project other) {
+ PropertyHelper.getPropertyHelper(this).copyInheritedProperties(other);
+ }
+
+ /**
+ * Set the default target of the project.
+ *
+ * @param defaultTarget
+ * The name of the default target for this project. May be
+ * <code>null</code>, indicating that there is no default target.
+ *
+ * @deprecated since 1.5.x. Use setDefault.
+ * @see #setDefault(String)
+ */
+ public void setDefaultTarget(String defaultTarget) {
+ setDefault(defaultTarget);
+ }
+
+ /**
+ * Return the name of the default target of the project.
+ *
+ * @return name of the default target or <code>null</code> if no default has
+ * been set.
+ */
+ public String getDefaultTarget() {
+ return defaultTarget;
+ }
+
+ /**
+ * Set the default target of the project.
+ *
+ * @param defaultTarget
+ * The name of the default target for this project. May be
+ * <code>null</code>, indicating that there is no default target.
+ */
+ public void setDefault(String defaultTarget) {
+ if (defaultTarget != null) {
+ setUserProperty(MagicNames.PROJECT_DEFAULT_TARGET, defaultTarget);
+ }
+ this.defaultTarget = defaultTarget;
+ }
+
+ /**
+ * Set the name of the project, also setting the user property
+ * <code>ant.project.name</code>.
+ *
+ * @param name
+ * The name of the project. Must not be <code>null</code>.
+ */
+ public void setName(String name) {
+ setUserProperty(MagicNames.PROJECT_NAME, name);
+ this.name = name;
+ }
+
+ /**
+ * Return the project name, if one has been set.
+ *
+ * @return the project name, or <code>null</code> if it hasn't been set.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the project description.
+ *
+ * @param description
+ * The description of the project. May be <code>null</code>.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Return the project description, if one has been set.
+ *
+ * @return the project description, or <code>null</code> if it hasn't been
+ * set.
+ */
+ public String getDescription() {
+ if (description == null) {
+ description = Description.getDescription(this);
+ }
+ return description;
+ }
+
+ /**
+ * Add a filter to the set of global filters.
+ *
+ * @param token
+ * The token to filter. Must not be <code>null</code>.
+ * @param value
+ * The replacement value. Must not be <code>null</code>.
+ * @deprecated since 1.4.x. Use getGlobalFilterSet().addFilter(token,value)
+ *
+ * @see #getGlobalFilterSet()
+ * @see FilterSet#addFilter(String,String)
+ */
+ public void addFilter(String token, String value) {
+ if (token == null) {
+ return;
+ }
+ globalFilterSet.addFilter(new FilterSet.Filter(token, value));
+ }
+
+ /**
+ * Return a hashtable of global filters, mapping tokens to values.
+ *
+ * @return a hashtable of global filters, mapping tokens to values (String
+ * to String).
+ *
+ * @deprecated since 1.4.x Use getGlobalFilterSet().getFilterHash().
+ *
+ * @see #getGlobalFilterSet()
+ * @see FilterSet#getFilterHash()
+ */
+ public Hashtable getFilters() {
+ // we need to build the hashtable dynamically
+ return globalFilterSet.getFilterHash();
+ }
+
+ /**
+ * Set the base directory for the project, checking that the given filename
+ * exists and is a directory.
+ *
+ * @param baseD
+ * The project base directory. Must not be <code>null</code>.
+ *
+ * @exception BuildException
+ * if the directory if invalid.
+ */
+ public void setBasedir(String baseD) throws BuildException {
+ setBaseDir(new File(baseD));
+ }
+
+ /**
+ * Set the base directory for the project, checking that the given file
+ * exists and is a directory.
+ *
+ * @param baseDir
+ * The project base directory. Must not be <code>null</code>.
+ * @exception BuildException
+ * if the specified file doesn't exist or isn't a directory.
+ */
+ public void setBaseDir(File baseDir) throws BuildException {
+ baseDir = FILE_UTILS.normalize(baseDir.getAbsolutePath());
+ if (!baseDir.exists()) {
+ throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+ + " does not exist");
+ }
+ if (!baseDir.isDirectory()) {
+ throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+ + " is not a directory");
+ }
+ this.baseDir = baseDir;
+ setPropertyInternal(MagicNames.PROJECT_BASEDIR, this.baseDir.getPath());
+ String msg = "Project base dir set to: " + this.baseDir;
+ log(msg, MSG_VERBOSE);
+ }
+
+ /**
+ * Return the base directory of the project as a file object.
+ *
+ * @return the project base directory, or <code>null</code> if the base
+ * directory has not been successfully set to a valid value.
+ */
+ public File getBaseDir() {
+ if (baseDir == null) {
+ try {
+ setBasedir(".");
+ } catch (BuildException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return baseDir;
+ }
+
+ /**
+ * Set "keep-going" mode. In this mode Ant will try to execute as
+ * many targets as possible. All targets that do not depend on failed
+ * target(s) will be executed. If the keepGoing settor/getter methods are
+ * used in conjunction with the <code>ant.executor.class</code> property,
+ * they will have no effect.
+ *
+ * @param keepGoingMode
+ * "keep-going" mode
+ * @since Ant 1.6
+ */
+ public void setKeepGoingMode(boolean keepGoingMode) {
+ this.keepGoingMode = keepGoingMode;
+ }
+
+ /**
+ * Return the keep-going mode. If the keepGoing settor/getter methods are
+ * used in conjunction with the <code>ant.executor.class</code> property,
+ * they will have no effect.
+ *
+ * @return "keep-going" mode
+ * @since Ant 1.6
+ */
+ public boolean isKeepGoingMode() {
+ return this.keepGoingMode;
+ }
+
+ /**
+ * Return the version of Java this class is running under.
+ *
+ * @return the version of Java as a String, e.g. "1.1" .
+ * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
+ * @deprecated since 1.5.x. Use org.apache.tools.ant.util.JavaEnvUtils
+ * instead.
+ */
+ public static String getJavaVersion() {
+ return JavaEnvUtils.getJavaVersion();
+ }
+
+ /**
+ * Set the <code>ant.java.version</code> property and tests for unsupported
+ * JVM versions. If the version is supported, verbose log messages are
+ * generated to record the Java version and operating system name.
+ *
+ * @exception BuildException
+ * if this Java version is not supported.
+ *
+ * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
+ */
+ public void setJavaVersionProperty() throws BuildException {
+ String javaVersion = JavaEnvUtils.getJavaVersion();
+ setPropertyInternal(MagicNames.ANT_JAVA_VERSION, javaVersion);
+
+ // sanity check
+ if (!JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_1_4)) {
+ throw new BuildException("Ant cannot work on Java prior to 1.4");
+ }
+ log("Detected Java version: " + javaVersion + " in: "
+ + System.getProperty("java.home"), MSG_VERBOSE);
+
+ log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
+ }
+
+ /**
+ * Add all system properties which aren't already defined as user properties
+ * to the project properties.
+ */
+ public void setSystemProperties() {
+ Properties systemP = System.getProperties();
+ Enumeration e = systemP.propertyNames();
+ while (e.hasMoreElements()) {
+ String propertyName = (String) e.nextElement();
+ String value = systemP.getProperty(propertyName);
+ if (value != null) {
+ this.setPropertyInternal(propertyName, value);
+ }
+ }
+ }
+
+ /**
+ * Add a new task definition to the project. Attempting to override an
+ * existing definition with an equivalent one (i.e. with the same classname)
+ * results in a verbose log message. Attempting to override an existing
+ * definition with a different one results in a warning log message and
+ * invalidates any tasks which have already been created with the old
+ * definition.
+ *
+ * @param taskName
+ * The name of the task to add. Must not be <code>null</code>.
+ * @param taskClass
+ * The full name of the class implementing the task. Must not be
+ * <code>null</code>.
+ *
+ * @exception BuildException
+ * if the class is unsuitable for being an Ant task. An error
+ * level message is logged before this exception is thrown.
+ *
+ * @see #checkTaskClass(Class)
+ */
+ public void addTaskDefinition(String taskName, Class taskClass)
+ throws BuildException {
+ ComponentHelper.getComponentHelper(this).addTaskDefinition(taskName,
+ taskClass);
+ }
+
+ /**
+ * Check whether or not a class is suitable for serving as Ant task. Ant
+ * task implementation classes must be public, concrete, and have a no-arg
+ * constructor.
+ *
+ * @param taskClass
+ * The class to be checked. Must not be <code>null</code>.
+ *
+ * @exception BuildException
+ * if the class is unsuitable for being an Ant task. An error
+ * level message is logged before this exception is thrown.
+ */
+ public void checkTaskClass(final Class taskClass) throws BuildException {
+ ComponentHelper.getComponentHelper(this).checkTaskClass(taskClass);
+
+ if (!Modifier.isPublic(taskClass.getModifiers())) {
+ final String message = taskClass + " is not public";
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ }
+ if (Modifier.isAbstract(taskClass.getModifiers())) {
+ final String message = taskClass + " is abstract";
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ }
+ try {
+ taskClass.getConstructor((Class[]) null);
+ // don't have to check for public, since
+ // getConstructor finds public constructors only.
+ } catch (NoSuchMethodException e) {
+ final String message = "No public no-arg constructor in "
+ + taskClass;
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message);
+ } catch (LinkageError e) {
+ String message = "Could not load " + taskClass + ": " + e;
+ log(message, Project.MSG_ERR);
+ throw new BuildException(message, e);
+ }
+ if (!Task.class.isAssignableFrom(taskClass)) {
+ TaskAdapter.checkTaskClass(taskClass, this);
+ }
+ }
+
+ /**
+ * Return the current task definition hashtable. The returned hashtable is
+ * "live" and so should not be modified.
+ *
+ * @return a map of from task name to implementing class (String to Class).
+ */
+ public Hashtable getTaskDefinitions() {
+ return ComponentHelper.getComponentHelper(this).getTaskDefinitions();
+ }
+
+ /**
+ * Return the current task definition map. The returned map is a copy of the
+ * "live" definitions.
+ *
+ * @return a map of from task name to implementing class (String to Class).
+ *
+ * @since Ant 1.8.1
+ */
+ public Map getCopyOfTaskDefinitions() {
+ return new HashMap(getTaskDefinitions());
+ }
+
+ /**
+ * Add a new datatype definition. Attempting to override an existing
+ * definition with an equivalent one (i.e. with the same classname) results
+ * in a verbose log message. Attempting to override an existing definition
+ * with a different one results in a warning log message, but the definition
+ * is changed.
+ *
+ * @param typeName
+ * The name of the datatype. Must not be <code>null</code>.
+ * @param typeClass
+ * The full name of the class implementing the datatype. Must not
+ * be <code>null</code>.
+ */
+ public void addDataTypeDefinition(String typeName, Class typeClass) {
+ ComponentHelper.getComponentHelper(this).addDataTypeDefinition(
+ typeName, typeClass);
+ }
+
+ /**
+ * Return the current datatype definition hashtable. The returned hashtable
+ * is "live" and so should not be modified.
+ *
+ * @return a map of from datatype name to implementing class (String to
+ * Class).
+ */
+ public Hashtable getDataTypeDefinitions() {
+ return ComponentHelper.getComponentHelper(this)
+ .getDataTypeDefinitions();
+ }
+
+ /**
+ * Return the current datatype definition map. The returned map is a copy pf
+ * the "live" definitions.
+ *
+ * @return a map of from datatype name to implementing class (String to
+ * Class).
+ *
+ * @since Ant 1.8.1
+ */
+ public Map getCopyOfDataTypeDefinitions() {
+ return new HashMap(getDataTypeDefinitions());
+ }
+
+ /**
+ * Add a <em>new</em> target to the project.
+ *
+ * @param target
+ * The target to be added to the project. Must not be
+ * <code>null</code>.
+ *
+ * @exception BuildException
+ * if the target already exists in the project
+ *
+ * @see Project#addOrReplaceTarget(Target)
+ */
+ public void addTarget(Target target) throws BuildException {
+ addTarget(target.getName(), target);
+ }
+
+ /**
+ * Add a <em>new</em> target to the project.
+ *
+ * @param targetName
+ * The name to use for the target. Must not be <code>null</code>.
+ * @param target
+ * The target to be added to the project. Must not be
+ * <code>null</code>.
+ *
+ * @exception BuildException
+ * if the target already exists in the project.
+ *
+ * @see Project#addOrReplaceTarget(String, Target)
+ */
+ public void addTarget(String targetName, Target target)
+ throws BuildException {
+ if (targets.get(targetName) != null) {
+ throw new BuildException("Duplicate target: `" + targetName + "'");
+ }
+ addOrReplaceTarget(targetName, target);
+ }
+
+ /**
+ * Add a target to the project, or replaces one with the same name.
+ *
+ * @param target
+ * The target to be added or replaced in the project. Must not be
+ * <code>null</code>.
+ */
+ public void addOrReplaceTarget(Target target) {
+ addOrReplaceTarget(target.getName(), target);
+ }
+
+ /**
+ * Add a target to the project, or replaces one with the same name.
+ *
+ * @param targetName
+ * The name to use for the target. Must not be <code>null</code>.
+ * @param target
+ * The target to be added or replaced in the project. Must not be
+ * <code>null</code>.
+ */
+ public void addOrReplaceTarget(String targetName, Target target) {
+ String msg = " +Target: " + targetName;
+ log(msg, MSG_DEBUG);
+ target.setProject(this);
+ targets.put(targetName, target);
+ }
+
+ /**
+ * Return the hashtable of targets. The returned hashtable is
+ * "live" and so should not be modified.
+ *
+ * @return a map from name to target (String to Target).
+ */
+ public Hashtable getTargets() {
+ return targets;
+ }
+
+ /**
+ * Return the map of targets. The returned map is a copy of the
+ * "live" targets.
+ *
+ * @return a map from name to target (String to Target).
+ * @since Ant 1.8.1
+ */
+ public Map getCopyOfTargets() {
+ return new HashMap(targets);
+ }
+
+ /**
+ * Create a new instance of a task, adding it to a list of created tasks for
+ * later invalidation. This causes all tasks to be remembered until the
+ * containing project is removed
+ *
+ * @param taskType
+ * The name of the task to create an instance of. Must not be
+ * <code>null</code>.
+ *
+ * @return an instance of the specified task, or <code>null</code> if the
+ * task name is not recognised.
+ *
+ * @exception BuildException
+ * if the task name is recognised but task creation fails.
+ */
+ public Task createTask(String taskType) throws BuildException {
+ return ComponentHelper.getComponentHelper(this).createTask(taskType);
+ }
+
+ /**
+ * Create a new instance of a data type.
+ *
+ * @param typeName
+ * The name of the data type to create an instance of. Must not
+ * be <code>null</code>.
+ *
+ * @return an instance of the specified data type, or <code>null</code> if
+ * the data type name is not recognised.
+ *
+ * @exception BuildException
+ * if the data type name is recognised but instance creation
+ * fails.
+ */
+ public Object createDataType(String typeName) throws BuildException {
+ return ComponentHelper.getComponentHelper(this)
+ .createDataType(typeName);
+ }
+
+ /**
+ * Set the Executor instance for this Project.
+ *
+ * @param e
+ * the Executor to use.
+ */
+ public void setExecutor(Executor e) {
+ addReference(MagicNames.ANT_EXECUTOR_REFERENCE, e);
+ }
+
+ /**
+ * Get this Project's Executor (setting it if necessary).
+ *
+ * @return an Executor instance.
+ */
+ public Executor getExecutor() {
+ Object o = getReference(MagicNames.ANT_EXECUTOR_REFERENCE);
+ if (o == null) {
+ String classname = getProperty(MagicNames.ANT_EXECUTOR_CLASSNAME);
+ if (classname == null) {
+ classname = DefaultExecutor.class.getName();
+ }
+ log("Attempting to create object of type " + classname, MSG_DEBUG);
+ try {
+ o = Class.forName(classname, true, coreLoader).newInstance();
+ } catch (ClassNotFoundException seaEnEfEx) {
+ // try the current classloader
+ try {
+ o = Class.forName(classname).newInstance();
+ } catch (Exception ex) {
+ log(ex.toString(), MSG_ERR);
+ }
+ } catch (Exception ex) {
+ log(ex.toString(), MSG_ERR);
+ }
+ if (o == null) {
+ throw new BuildException(
+ "Unable to obtain a Target Executor instance.");
+ }
+ setExecutor((Executor) o);
+ }
+ return (Executor) o;
+ }
+
+ /**
+ * Execute the specified sequence of targets, and the targets they depend
+ * on.
+ *
+ * @param names
+ * A vector of target name strings to execute. Must not be
+ * <code>null</code>.
+ *
+ * @exception BuildException
+ * if the build failed.
+ */
+ public void executeTargets(Vector names) throws BuildException {
+ setUserProperty(MagicNames.PROJECT_INVOKED_TARGETS, CollectionUtils
+ .flattenToString(names));
+ getExecutor().executeTargets(this,
+ (String[]) (names.toArray(new String[names.size()])));
+ }
+
+ /**
+ * Demultiplex output so that each task receives the appropriate messages.
+ * If the current thread is not currently executing a task, the message is
+ * logged directly.
+ *
+ * @param output
+ * Message to handle. Should not be <code>null</code>.
+ * @param isWarning
+ * Whether the text represents an warning (<code>true</code>) or
+ * information (<code>false</code>).
+ */
+ public void demuxOutput(String output, boolean isWarning) {
+ Task task = getThreadTask(Thread.currentThread());
+ if (task == null) {
+ log(output, isWarning ? MSG_WARN : MSG_INFO);
+ } else {
+ if (isWarning) {
+ task.handleErrorOutput(output);
+ } else {
+ task.handleOutput(output);
+ }
+ }
+ }
+
+ /**
+ * Read data from the default input stream. If no default has been
+ * specified, System.in is used.
+ *
+ * @param buffer
+ * the buffer into which data is to be read.
+ * @param offset
+ * the offset into the buffer at which data is stored.
+ * @param length
+ * the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException
+ * if the data cannot be read.
+ * @since Ant 1.6
+ */
+ public int defaultInput(byte[] buffer, int offset, int length)
+ throws IOException {
+ if (defaultInputStream != null) {
+ System.out.flush();
+ return defaultInputStream.read(buffer, offset, length);
+ } else {
+ throw new EOFException("No input provided for project");
+ }
+ }
+
+ /**
+ * Demux an input request to the correct task.
+ *
+ * @param buffer
+ * the buffer into which data is to be read.
+ * @param offset
+ * the offset into the buffer at which data is stored.
+ * @param length
+ * the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException
+ * if the data cannot be read.
+ * @since Ant 1.6
+ */
+ public int demuxInput(byte[] buffer, int offset, int length)
+ throws IOException {
+ Task task = getThreadTask(Thread.currentThread());
+ if (task == null) {
+ return defaultInput(buffer, offset, length);
+ } else {
+ return task.handleInput(buffer, offset, length);
+ }
+ }
+
+ /**
+ * Demultiplex flush operations so that each task receives the appropriate
+ * messages. If the current thread is not currently executing a task, the
+ * message is logged directly.
+ *
+ * @since Ant 1.5.2
+ *
+ * @param output
+ * Message to handle. Should not be <code>null</code>.
+ * @param isError
+ * Whether the text represents an error (<code>true</code>) or
+ * information (<code>false</code>).
+ */
+ public void demuxFlush(String output, boolean isError) {
+ Task task = getThreadTask(Thread.currentThread());
+ if (task == null) {
+ fireMessageLogged(this, output, isError ? MSG_ERR : MSG_INFO);
+ } else {
+ if (isError) {
+ task.handleErrorFlush(output);
+ } else {
+ task.handleFlush(output);
+ }
+ }
+ }
+
+ /**
+ * Execute the specified target and any targets it depends on.
+ *
+ * @param targetName
+ * The name of the target to execute. Must not be
+ * <code>null</code>.
+ *
+ * @exception BuildException
+ * if the build failed.
+ */
+ public void executeTarget(String targetName) throws BuildException {
+
+ // sanity check ourselves, if we've been asked to build nothing
+ // then we should complain
+
+ if (targetName == null) {
+ String msg = "No target specified";
+ throw new BuildException(msg);
+ }
+
+ // Sort and run the dependency tree.
+ // Sorting checks if all the targets (and dependencies)
+ // exist, and if there is any cycle in the dependency
+ // graph.
+ executeSortedTargets(topoSort(targetName, targets, false));
+ }
+
+ protected String breakAt;
+
+ public void setBreakAt(String breakAt) {
+ this.breakAt = breakAt;
+ }
+
+ public String getBreakAt() {
+ return breakAt;
+ }
+
+ /**
+ * Execute a <code>Vector</code> of sorted targets.
+ *
+ * @param sortedTargets
+ * the aforementioned <code>Vector</code>.
+ * @throws BuildException
+ * on error.
+ */
+ public void executeSortedTargets(Vector sortedTargets)
+ throws BuildException {
+ Set succeededTargets = new HashSet();
+ BuildException buildException = null; // first build exception
+// int i = -1;
+// for (Enumeration iter = sortedTargets.elements(); iter
+// .hasMoreElements() && getBreakAt() != null && getBreakAt().trim().length() > 0;) {
+// i++;
+// Target curTarget = (Target) iter.nextElement();
+// if(curTarget.getName().equals(getBreakAt())) {
+// log("Adding default DebugTarget");
+// sortedTargets.add(i, DebugTask.createDebugTarget(this));
+// break;
+// }
+// }
+ for (Enumeration iter = sortedTargets.elements(); iter
+ .hasMoreElements();) {
+ Target curtarget = (Target) iter.nextElement();
+ boolean canExecute = true;
+ for (Enumeration depIter = curtarget.getDependencies(); depIter
+ .hasMoreElements();) {
+ String dependencyName = ((String) depIter.nextElement());
+ if (!succeededTargets.contains(dependencyName)) {
+ canExecute = false;
+ log(curtarget, "Cannot execute '" + curtarget.getName()
+ + "' - '" + dependencyName
+ + "' failed or was not executed.", MSG_ERR);
+ break;
+ }
+ }
+ if (canExecute) {
+ Throwable thrownException = null;
+ try {
+ curtarget.performTasks();
+ succeededTargets.add(curtarget.getName());
+ } catch (RuntimeException ex) {
+ if (!(keepGoingMode)) {
+ throw ex; // throw further
+ }
+ thrownException = ex;
+ } catch (Throwable ex) {
+ if (!(keepGoingMode)) {
+ throw new BuildException(ex);
+ }
+ thrownException = ex;
+ }
+ if (thrownException != null) {
+ if (thrownException instanceof BuildException) {
+ log(curtarget, "Target '" + curtarget.getName()
+ + "' failed with message '"
+ + thrownException.getMessage() + "'.", MSG_ERR);
+ // only the first build exception is reported
+ if (buildException == null) {
+ buildException = (BuildException) thrownException;
+ }
+ } else {
+ log(curtarget, "Target '" + curtarget.getName()
+ + "' failed with message '"
+ + thrownException.getMessage() + "'.", MSG_ERR);
+ thrownException.printStackTrace(System.err);
+ if (buildException == null) {
+ buildException = new BuildException(thrownException);
+ }
+ }
+ }
+ }
+ }
+ if (buildException != null) {
+ throw buildException;
+ }
+ }
+
+ /**
+ * Return the canonical form of a filename.
+ * <p>
+ * If the specified file name is relative it is resolved with respect to the
+ * given root directory.
+ *
+ * @param fileName
+ * The name of the file to resolve. Must not be <code>null</code>
+ * .
+ *
+ * @param rootDir
+ * The directory respective to which relative file names are
+ * resolved. May be <code>null</code>, in which case the current
+ * directory is used.
+ *
+ * @return the resolved File.
+ *
+ * @deprecated since 1.4.x
+ */
+ public File resolveFile(String fileName, File rootDir) {
+ return FILE_UTILS.resolveFile(rootDir, fileName);
+ }
+
+ /**
+ * Return the canonical form of a filename.
+ * <p>
+ * If the specified file name is relative it is resolved with respect to the
+ * project's base directory.
+ *
+ * @param fileName
+ * The name of the file to resolve. Must not be <code>null</code>
+ * .
+ *
+ * @return the resolved File.
+ *
+ */
+ public File resolveFile(String fileName) {
+ return FILE_UTILS.resolveFile(baseDir, fileName);
+ }
+
+ /**
+ * Translate a path into its native (platform specific) format.
+ * <p>
+ * This method uses PathTokenizer to separate the input path into its
+ * components. This handles DOS style paths in a relatively sensible way.
+ * The file separators are then converted to their platform specific
+ * versions.
+ *
+ * @param toProcess
+ * The path to be translated. May be <code>null</code>.
+ *
+ * @return the native version of the specified path or an empty string if
+ * the path is <code>null</code> or empty.
+ *
+ * @deprecated since 1.7 Use FileUtils.translatePath instead.
+ *
+ * @see PathTokenizer
+ */
+ public static String translatePath(String toProcess) {
+ return FileUtils.translatePath(toProcess);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination. No
+ * filtering is performed.
+ *
+ * @param sourceFile
+ * Name of file to copy from. Must not be <code>null</code>.
+ * @param destFile
+ * Name of file to copy to. Must not be <code>null</code>.
+ *
+ * @exception IOException
+ * if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ public void copyFile(String sourceFile, String destFile) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used.
+ *
+ * @param sourceFile
+ * Name of file to copy from. Must not be <code>null</code>.
+ * @param destFile
+ * Name of file to copy to. Must not be <code>null</code>.
+ * @param filtering
+ * Whether or not token filtering should be used during the copy.
+ *
+ * @exception IOException
+ * if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ public void copyFile(String sourceFile, String destFile, boolean filtering)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile, filtering ? globalFilters
+ : null);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used and if source files may
+ * overwrite newer destination files.
+ *
+ * @param sourceFile
+ * Name of file to copy from. Must not be <code>null</code>.
+ * @param destFile
+ * Name of file to copy to. Must not be <code>null</code>.
+ * @param filtering
+ * Whether or not token filtering should be used during the copy.
+ * @param overwrite
+ * Whether or not the destination file should be overwritten if
+ * it already exists.
+ *
+ * @exception IOException
+ * if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ public void copyFile(String sourceFile, String destFile, boolean filtering,
+ boolean overwrite) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile, filtering ? globalFilters
+ : null, overwrite);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used, if source files may
+ * overwrite newer destination files, and if the last modified time of the
+ * resulting file should be set to that of the source file.
+ *
+ * @param sourceFile
+ * Name of file to copy from. Must not be <code>null</code>.
+ * @param destFile
+ * Name of file to copy to. Must not be <code>null</code>.
+ * @param filtering
+ * Whether or not token filtering should be used during the copy.
+ * @param overwrite
+ * Whether or not the destination file should be overwritten if
+ * it already exists.
+ * @param preserveLastModified
+ * Whether or not the last modified time of the resulting file
+ * should be set to that of the source file.
+ *
+ * @exception IOException
+ * if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ public void copyFile(String sourceFile, String destFile, boolean filtering,
+ boolean overwrite, boolean preserveLastModified) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile, filtering ? globalFilters
+ : null, overwrite, preserveLastModified);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination. No
+ * filtering is performed.
+ *
+ * @param sourceFile
+ * File to copy from. Must not be <code>null</code>.
+ * @param destFile
+ * File to copy to. Must not be <code>null</code>.
+ *
+ * @exception IOException
+ * if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ public void copyFile(File sourceFile, File destFile) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used.
+ *
+ * @param sourceFile
+ * File to copy from. Must not be <code>null</code>.
+ * @param destFile
+ * File to copy to. Must not be <code>null</code>.
+ * @param filtering
+ * Whether or not token filtering should be used during the copy.
+ *
+ * @exception IOException
+ * if the copying fails.
+ *
+ * @deprecated since 1.4.x
+ */
+ public void copyFile(File sourceFile, File destFile, boolean filtering)
+ throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile, filtering ? globalFilters
+ : null);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used and if source files may
+ * overwrite newer destination files.
+ *
+ * @param sourceFile
+ * File to copy from. Must not be <code>null</code>.
+ * @param destFile
+ * File to copy to. Must not be <code>null</code>.
+ * @param filtering
+ * Whether or not token filtering should be used during the copy.
+ * @param overwrite
+ * Whether or not the destination file should be overwritten if
+ * it already exists.
+ *
+ * @exception IOException
+ * if the file cannot be copied.
+ *
+ * @deprecated since 1.4.x
+ */
+ public void copyFile(File sourceFile, File destFile, boolean filtering,
+ boolean overwrite) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile, filtering ? globalFilters
+ : null, overwrite);
+ }
+
+ /**
+ * Convenience method to copy a file from a source to a destination
+ * specifying if token filtering should be used, if source files may
+ * overwrite newer destination files, and if the last modified time of the
+ * resulting file should be set to that of the source file.
+ *
+ * @param sourceFile
+ * File to copy from. Must not be <code>null</code>.
+ * @param destFile
+ * File to copy to. Must not be <code>null</code>.
+ * @param filtering
+ * Whether or not token filtering should be used during the copy.
+ * @param overwrite
+ * Whether or not the destination file should be overwritten if
+ * it already exists.
+ * @param preserveLastModified
+ * Whether or not the last modified time of the resulting file
+ * should be set to that of the source file.
+ *
+ * @exception IOException
+ * if the file cannot be copied.
+ *
+ * @deprecated since 1.4.x
+ */
+ public void copyFile(File sourceFile, File destFile, boolean filtering,
+ boolean overwrite, boolean preserveLastModified) throws IOException {
+ FILE_UTILS.copyFile(sourceFile, destFile, filtering ? globalFilters
+ : null, overwrite, preserveLastModified);
+ }
+
+ /**
+ * Call File.setLastModified(long time) on Java above 1.1, and logs a
+ * warning on Java 1.1.
+ *
+ * @param file
+ * The file to set the last modified time on. Must not be
+ * <code>null</code>.
+ *
+ * @param time
+ * the required modification time.
+ *
+ * @deprecated since 1.4.x
+ *
+ * @exception BuildException
+ * if the last modified time cannot be set despite running on
+ * a platform with a version above 1.1.
+ */
+ public void setFileLastModified(File file, long time) throws BuildException {
+ FILE_UTILS.setFileLastModified(file, time);
+ log("Setting modification time for " + file, MSG_VERBOSE);
+ }
+
+ /**
+ * Return the boolean equivalent of a string, which is considered
+ * <code>true</code> if either <code>"on"</code>, <code>"true"</code>, or
+ * <code>"yes"</code> is found, ignoring case.
+ *
+ * @param s
+ * The string to convert to a boolean value.
+ *
+ * @return <code>true</code> if the given string is <code>"on"</code>,
+ * <code>"true"</code> or <code>"yes"</code>, or <code>false</code>
+ * otherwise.
+ */
+ public static boolean toBoolean(String s) {
+ return ("on".equalsIgnoreCase(s) || "true".equalsIgnoreCase(s) || "yes"
+ .equalsIgnoreCase(s));
+ }
+
+ /**
+ * Get the Project instance associated with the specified object.
+ *
+ * @param o
+ * the object to query.
+ * @return Project instance, if any.
+ * @since Ant 1.7.1
+ */
+ public static Project getProject(Object o) {
+ if (o instanceof ProjectComponent) {
+ return ((ProjectComponent) o).getProject();
+ }
+ try {
+ Method m = o.getClass().getMethod("getProject", (Class[]) null);
+ if (Project.class == m.getReturnType()) {
+ return (Project) m.invoke(o, (Object[]) null);
+ }
+ } catch (Exception e) {
+ // too bad
+ }
+ return null;
+ }
+
+ /**
+ * Topologically sort a set of targets. Equivalent to calling
+ * <code>topoSort(new String[] {root}, targets, true)</code>.
+ *
+ * @param root
+ * The name of the root target. The sort is created in such a way
+ * that the sequence of Targets up to the root target is the
+ * minimum possible such sequence. Must not be <code>null</code>.
+ * @param targetTable
+ * A Hashtable mapping names to Targets. Must not be
+ * <code>null</code>.
+ * @return a Vector of ALL Target objects in sorted order.
+ * @exception BuildException
+ * if there is a cyclic dependency among the targets, or if a
+ * named target does not exist.
+ */
+ public final Vector topoSort(String root, Hashtable targetTable)
+ throws BuildException {
+ return topoSort(new String[] { root }, targetTable, true);
+ }
+
+ /**
+ * Topologically sort a set of targets. Equivalent to calling
+ * <code>topoSort(new String[] {root}, targets, returnAll)</code>.
+ *
+ * @param root
+ * The name of the root target. The sort is created in such a way
+ * that the sequence of Targets up to the root target is the
+ * minimum possible such sequence. Must not be <code>null</code>.
+ * @param targetTable
+ * A Hashtable mapping names to Targets. Must not be
+ * <code>null</code>.
+ * @param returnAll
+ * <code>boolean</code> indicating whether to return all targets,
+ * or the execution sequence only.
+ * @return a Vector of Target objects in sorted order.
+ * @exception BuildException
+ * if there is a cyclic dependency among the targets, or if a
+ * named target does not exist.
+ * @since Ant 1.6.3
+ */
+ public final Vector topoSort(String root, Hashtable targetTable,
+ boolean returnAll) throws BuildException {
+ return topoSort(new String[] { root }, targetTable, returnAll);
+ }
+
+ /**
+ * Topologically sort a set of targets.
+ *
+ * @param root
+ * <code>String[]</code> containing the names of the root
+ * targets. The sort is created in such a way that the ordered
+ * sequence of Targets is the minimum possible such sequence to
+ * the specified root targets. Must not be <code>null</code>.
+ * @param targetTable
+ * A map of names to targets (String to Target). Must not be
+ * <code>null</code>.
+ * @param returnAll
+ * <code>boolean</code> indicating whether to return all targets,
+ * or the execution sequence only.
+ * @return a Vector of Target objects in sorted order.
+ * @exception BuildException
+ * if there is a cyclic dependency among the targets, or if a
+ * named target does not exist.
+ * @since Ant 1.6.3
+ */
+ public final Vector topoSort(String[] root, Hashtable targetTable,
+ boolean returnAll) throws BuildException {
+ Vector ret = new VectorSet();
+ Hashtable state = new Hashtable();
+ Stack visiting = new Stack();
+
+ // We first run a DFS based sort using each root as a starting node.
+ // This creates the minimum sequence of Targets to the root node(s).
+ // We then do a sort on any remaining unVISITED targets.
+ // This is unnecessary for doing our build, but it catches
+ // circular dependencies or missing Targets on the entire
+ // dependency tree, not just on the Targets that depend on the
+ // build Target.
+
+ for (int i = 0; i < root.length; i++) {
+ String st = (String) (state.get(root[i]));
+ if (st == null) {
+ tsort(root[i], targetTable, state, visiting, ret);
+ } else if (st == VISITING) {
+ throw new RuntimeException(
+ "Unexpected node in visiting state: " + root[i]);
+ }
+ }
+ StringBuffer buf = new StringBuffer("Build sequence for target(s)");
+
+ for (int j = 0; j < root.length; j++) {
+ buf.append((j == 0) ? " `" : ", `").append(root[j]).append('\'');
+ }
+ buf.append(" is " + ret);
+ log(buf.toString(), MSG_VERBOSE);
+
+ Vector complete = (returnAll) ? ret : new Vector(ret);
+ for (Enumeration en = targetTable.keys(); en.hasMoreElements();) {
+ String curTarget = (String) en.nextElement();
+ String st = (String) state.get(curTarget);
+ if (st == null) {
+ tsort(curTarget, targetTable, state, visiting, complete);
+ } else if (st == VISITING) {
+ throw new RuntimeException(
+ "Unexpected node in visiting state: " + curTarget);
+ }
+ }
+ log("Complete build sequence is " + complete, MSG_VERBOSE);
+ return ret;
+ }
+
+ /**
+ * Perform a single step in a recursive depth-first-search traversal of the
+ * target dependency tree.
+ * <p>
+ * The current target is first set to the "visiting" state, and
+ * pushed onto the "visiting" stack.
+ * <p>
+ * An exception is then thrown if any child of the current node is in the
+ * visiting state, as that implies a circular dependency. The exception
+ * contains details of the cycle, using elements of the "visiting"
+ * stack.
+ * <p>
+ * If any child has not already been "visited", this method is
+ * called recursively on it.
+ * <p>
+ * The current target is then added to the ordered list of targets. Note
+ * that this is performed after the children have been visited in order to
+ * get the correct order. The current target is set to the
+ * "visited" state.
+ * <p>
+ * By the time this method returns, the ordered list contains the sequence
+ * of targets up to and including the current target.
+ *
+ * @param root
+ * The current target to inspect. Must not be <code>null</code>.
+ * @param targetTable
+ * A mapping from names to targets (String to Target). Must not
+ * be <code>null</code>.
+ * @param state
+ * A mapping from target names to states (String to String). The
+ * states in question are "VISITING" and
+ * "VISITED". Must not be <code>null</code>.
+ * @param visiting
+ * A stack of targets which are currently being visited. Must not
+ * be <code>null</code>.
+ * @param ret
+ * The list to add target names to. This will end up containing
+ * the complete list of dependencies in dependency order. Must
+ * not be <code>null</code>.
+ *
+ * @exception BuildException
+ * if a non-existent target is specified or if a circular
+ * dependency is detected.
+ */
+ private void tsort(String root, Hashtable targetTable, Hashtable state,
+ Stack visiting, Vector ret) throws BuildException {
+ state.put(root, VISITING);
+ visiting.push(root);
+
+ Target target = (Target) targetTable.get(root);
+
+ // Make sure we exist
+ if (target == null) {
+ StringBuffer sb = new StringBuffer("Target \"");
+ sb.append(root);
+ sb.append("\" does not exist in the project \"");
+ sb.append(name);
+ sb.append("\". ");
+ visiting.pop();
+ if (!visiting.empty()) {
+ String parent = (String) visiting.peek();
+ sb.append("It is used from target \"");
+ sb.append(parent);
+ sb.append("\".");
+ }
+ throw new BuildException(new String(sb));
+ }
+ for (Enumeration en = target.getDependencies(); en.hasMoreElements();) {
+ String cur = (String) en.nextElement();
+ String m = (String) state.get(cur);
+ if (m == null) {
+ // Not been visited
+ tsort(cur, targetTable, state, visiting, ret);
+ } else if (m == VISITING) {
+ // Currently visiting this node, so have a cycle
+ throw makeCircularException(cur, visiting);
+ }
+ }
+ String p = (String) visiting.pop();
+ if (root != p) {
+ throw new RuntimeException(
+ "Unexpected internal error: expected to " + "pop " + root
+ + " but got " + p);
+ }
+ state.put(root, VISITED);
+ ret.addElement(target);
+ }
+
+ /**
+ * Build an appropriate exception detailing a specified circular dependency.
+ *
+ * @param end
+ * The dependency to stop at. Must not be <code>null</code>.
+ * @param stk
+ * A stack of dependencies. Must not be <code>null</code>.
+ *
+ * @return a BuildException detailing the specified circular dependency.
+ */
+ private static BuildException makeCircularException(String end, Stack stk) {
+ StringBuffer sb = new StringBuffer("Circular dependency: ");
+ sb.append(end);
+ String c;
+ do {
+ c = (String) stk.pop();
+ sb.append(" <- ");
+ sb.append(c);
+ } while (!c.equals(end));
+ return new BuildException(new String(sb));
+ }
+
+ /**
+ * Inherit the id references.
+ *
+ * @param parent
+ * the parent project of this project.
+ */
+ public void inheritIDReferences(Project parent) {
+ parentIdProject = parent;
+ }
+
+ /**
+ * Add an id reference. Used for broken build files.
+ *
+ * @param id
+ * the id to set.
+ * @param value
+ * the value to set it to (Unknown element in this case.
+ */
+ public void addIdReference(String id, Object value) {
+ idReferences.put(id, value);
+ }
+
+ /**
+ * Add a reference to the project.
+ *
+ * @param referenceName
+ * The name of the reference. Must not be <code>null</code>.
+ * @param value
+ * The value of the reference.
+ */
+ public void addReference(String referenceName, Object value) {
+ Object old = ((AntRefTable) references).getReal(referenceName);
+ if (old == value) {
+ // no warning, this is not changing anything
+ return;
+ }
+ if (old != null && !(old instanceof UnknownElement)) {
+ log("Overriding previous definition of reference to "
+ + referenceName, MSG_VERBOSE);
+ }
+ log("Adding reference: " + referenceName, MSG_DEBUG);
+ references.put(referenceName, value);
+ }
+
+ /**
+ * Return a map of the references in the project (String to Object). The
+ * returned hashtable is "live" and so must not be modified.
+ *
+ * @return a map of the references in the project (String to Object).
+ */
+ public Hashtable getReferences() {
+ return references;
+ }
+
+ /**
+ * Does the project know this reference?
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean hasReference(String key) {
+ return references.containsKey(key);
+ }
+
+ /**
+ * Return a map of the references in the project (String to Object). The
+ * returned hashtable is a copy of the "live" references.
+ *
+ * @return a map of the references in the project (String to Object).
+ *
+ * @since Ant 1.8.1
+ */
+ public Map getCopyOfReferences() {
+ return new HashMap(references);
+ }
+
+ /**
+ * Look up a reference by its key (ID).
+ *
+ * @param key
+ * The key for the desired reference. Must not be
+ * <code>null</code>.
+ *
+ * @return the reference with the specified ID, or <code>null</code> if
+ * there is no such reference in the project.
+ */
+ public Object getReference(String key) {
+ Object ret = references.get(key);
+ if (ret != null) {
+ return ret;
+ }
+ if (!key.equals(MagicNames.REFID_PROPERTY_HELPER)) {
+ try {
+ if (PropertyHelper.getPropertyHelper(this).containsProperties(
+ key)) {
+ log(
+ "Unresolvable reference "
+ + key
+ + " might be a misuse of property expansion syntax.",
+ MSG_WARN);
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Return a description of the type of the given element, with special
+ * handling for instances of tasks and data types.
+ * <p>
+ * This is useful for logging purposes.
+ *
+ * @param element
+ * The element to describe. Must not be <code>null</code>.
+ *
+ * @return a description of the element type.
+ *
+ * @since 1.95, Ant 1.5
+ */
+ public String getElementName(Object element) {
+ return ComponentHelper.getComponentHelper(this).getElementName(element);
+ }
+
+ /**
+ * Send a "build started" event to the build listeners for this
+ * project.
+ */
+ public void fireBuildStarted() {
+ BuildEvent event = new BuildEvent(this);
+ BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].buildStarted(event);
+ }
+ }
+
+ /**
+ * Send a "build finished" event to the build listeners for this
+ * project.
+ *
+ * @param exception
+ * an exception indicating a reason for a build failure. May be
+ * <code>null</code>, indicating a successful build.
+ */
+ public void fireBuildFinished(Throwable exception) {
+ BuildEvent event = new BuildEvent(this);
+ event.setException(exception);
+ BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].buildFinished(event);
+ }
+ // Inform IH to clear the cache
+ IntrospectionHelper.clearCache();
+ }
+
+ /**
+ * Send a "subbuild started" event to the build listeners for this
+ * project.
+ *
+ * @since Ant 1.6.2
+ */
+ public void fireSubBuildStarted() {
+ BuildEvent event = new BuildEvent(this);
+ BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ if (currListeners[i] instanceof SubBuildListener) {
+ ((SubBuildListener) currListeners[i]).subBuildStarted(event);
+ }
+ }
+ }
+
+ /**
+ * Send a "subbuild finished" event to the build listeners for
+ * this project.
+ *
+ * @param exception
+ * an exception indicating a reason for a build failure. May be
+ * <code>null</code>, indicating a successful build.
+ *
+ * @since Ant 1.6.2
+ */
+ public void fireSubBuildFinished(Throwable exception) {
+ BuildEvent event = new BuildEvent(this);
+ event.setException(exception);
+ BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ if (currListeners[i] instanceof SubBuildListener) {
+ ((SubBuildListener) currListeners[i]).subBuildFinished(event);
+ }
+ }
+ }
+
+ /**
+ * Send a "target started" event to the build listeners for this
+ * project.
+ *
+ * @param target
+ * The target which is starting to build. Must not be
+ * <code>null</code>.
+ */
+ protected void fireTargetStarted(Target target) {
+ BuildEvent event = new BuildEvent(target);
+ BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].targetStarted(event);
+ }
+
+ }
+
+ /**
+ * Send a "target finished" event to the build listeners for this
+ * project.
+ *
+ * @param target
+ * The target which has finished building. Must not be
+ * <code>null</code>.
+ * @param exception
+ * an exception indicating a reason for a build failure. May be
+ * <code>null</code>, indicating a successful build.
+ */
+ protected void fireTargetFinished(Target target, Throwable exception) {
+ BuildEvent event = new BuildEvent(target);
+ event.setException(exception);
+ BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].targetFinished(event);
+ }
+
+ }
+
+ /**
+ * Send a "task started" event to the build listeners for this
+ * project.
+ *
+ * @param task
+ * The target which is starting to execute. Must not be
+ * <code>null</code>.
+ */
+ protected void fireTaskStarted(Task task) {
+ // register this as the current task on the current thread.
+ registerThreadTask(Thread.currentThread(), task);
+ BuildEvent event = new BuildEvent(task);
+ BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].taskStarted(event);
+ }
+ }
+
+ /**
+ * Send a "task finished" event to the build listeners for this
+ * project.
+ *
+ * @param task
+ * The task which has finished executing. Must not be
+ * <code>null</code>.
+ * @param exception
+ * an exception indicating a reason for a build failure. May be
+ * <code>null</code>, indicating a successful build.
+ */
+ protected void fireTaskFinished(Task task, Throwable exception) {
+ registerThreadTask(Thread.currentThread(), null);
+ System.out.flush();
+ System.err.flush();
+ BuildEvent event = new BuildEvent(task);
+ event.setException(exception);
+ BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].taskFinished(event);
+ }
+
+ }
+
+ /**
+ * Send a "message logged" event to the build listeners for this
+ * project.
+ *
+ * @param event
+ * The event to send. This should be built up with the
+ * appropriate task/target/project by the caller, so that this
+ * method can set the message and priority, then send the event.
+ * Must not be <code>null</code>.
+ * @param message
+ * The message to send. Should not be <code>null</code>.
+ * @param priority
+ * The priority of the message.
+ */
+ private void fireMessageLoggedEvent(BuildEvent event, String message,
+ int priority) {
+
+ if (message == null) {
+ message = String.valueOf(message);
+ }
+ if (message.endsWith(StringUtils.LINE_SEP)) {
+ int endIndex = message.length() - StringUtils.LINE_SEP.length();
+ event.setMessage(message.substring(0, endIndex), priority);
+ } else {
+ event.setMessage(message, priority);
+ }
+ if (isLoggingMessage.get() != Boolean.FALSE) {
+ /*
+ * One of the Listeners has attempted to access System.err or
+ * System.out.
+ *
+ * We used to throw an exception in this case, but sometimes
+ * Listeners can't prevent it(like our own Log4jListener which
+ * invokes getLogger() which in turn wants to write to the console).
+ *
+ * @see http://marc.theaimsgroup.com/?t=110538624200006&r=1&w=2
+ *
+ * We now (Ant 1.6.3 and later) simply swallow the message.
+ */
+ return;
+ }
+ try {
+ isLoggingMessage.set(Boolean.TRUE);
+ BuildListener[] currListeners = listeners;
+ for (int i = 0; i < currListeners.length; i++) {
+ currListeners[i].messageLogged(event);
+ }
+ } finally {
+ isLoggingMessage.set(Boolean.FALSE);
+ }
+ }
+
+ /**
+ * Send a "message logged" project level event to the build
+ * listeners for this project.
+ *
+ * @param project
+ * The project generating the event. Should not be
+ * <code>null</code>.
+ * @param message
+ * The message to send. Should not be <code>null</code>.
+ * @param priority
+ * The priority of the message.
+ */
+ protected void fireMessageLogged(Project project, String message,
+ int priority) {
+ fireMessageLogged(project, message, null, priority);
+ }
+
+ /**
+ * Send a "message logged" project level event to the build
+ * listeners for this project.
+ *
+ * @param project
+ * The project generating the event. Should not be
+ * <code>null</code>.
+ * @param message
+ * The message to send. Should not be <code>null</code>.
+ * @param throwable
+ * The exception that caused this message. May be
+ * <code>null</code>.
+ * @param priority
+ * The priority of the message.
+ * @since 1.7
+ */
+ protected void fireMessageLogged(Project project, String message,
+ Throwable throwable, int priority) {
+ BuildEvent event = new BuildEvent(project);
+ event.setException(throwable);
+ fireMessageLoggedEvent(event, message, priority);
+ }
+
+ /**
+ * Send a "message logged" target level event to the build
+ * listeners for this project.
+ *
+ * @param target
+ * The target generating the event. Must not be <code>null</code>
+ * .
+ * @param message
+ * The message to send. Should not be <code>null</code>.
+ * @param priority
+ * The priority of the message.
+ */
+ protected void fireMessageLogged(Target target, String message, int priority) {
+ fireMessageLogged(target, message, null, priority);
+ }
+
+ /**
+ * Send a "message logged" target level event to the build
+ * listeners for this project.
+ *
+ * @param target
+ * The target generating the event. Must not be <code>null</code>
+ * .
+ * @param message
+ * The message to send. Should not be <code>null</code>.
+ * @param throwable
+ * The exception that caused this message. May be
+ * <code>null</code>.
+ * @param priority
+ * The priority of the message.
+ * @since 1.7
+ */
+ protected void fireMessageLogged(Target target, String message,
+ Throwable throwable, int priority) {
+ BuildEvent event = new BuildEvent(target);
+ event.setException(throwable);
+ fireMessageLoggedEvent(event, message, priority);
+ }
+
+ /**
+ * Send a "message logged" task level event to the build listeners
+ * for this project.
+ *
+ * @param task
+ * The task generating the event. Must not be <code>null</code>.
+ * @param message
+ * The message to send. Should not be <code>null</code>.
+ * @param priority
+ * The priority of the message.
+ */
+ protected void fireMessageLogged(Task task, String message, int priority) {
+ fireMessageLogged(task, message, null, priority);
+ }
+
+ /**
+ * Send a "message logged" task level event to the build listeners
+ * for this project.
+ *
+ * @param task
+ * The task generating the event. Must not be <code>null</code>.
+ * @param message
+ * The message to send. Should not be <code>null</code>.
+ * @param throwable
+ * The exception that caused this message. May be
+ * <code>null</code>.
+ * @param priority
+ * The priority of the message.
+ * @since 1.7
+ */
+ protected void fireMessageLogged(Task task, String message,
+ Throwable throwable, int priority) {
+ BuildEvent event = new BuildEvent(task);
+ event.setException(throwable);
+ fireMessageLoggedEvent(event, message, priority);
+ }
+
+ /**
+ * Register a task as the current task for a thread. If the task is null,
+ * the thread's entry is removed.
+ *
+ * @param thread
+ * the thread on which the task is registered.
+ * @param task
+ * the task to be registered.
+ * @since Ant 1.5
+ */
+ public void registerThreadTask(Thread thread, Task task) {
+ synchronized (threadTasks) {
+ if (task != null) {
+ threadTasks.put(thread, task);
+ threadGroupTasks.put(thread.getThreadGroup(), task);
+ } else {
+ threadTasks.remove(thread);
+ threadGroupTasks.remove(thread.getThreadGroup());
+ }
+ }
+ }
+
+ /**
+ * Get the current task associated with a thread, if any.
+ *
+ * @param thread
+ * the thread for which the task is required.
+ * @return the task which is currently registered for the given thread or
+ * null if no task is registered.
+ */
+ public Task getThreadTask(Thread thread) {
+ synchronized (threadTasks) {
+ Task task = (Task) threadTasks.get(thread);
+ if (task == null) {
+ ThreadGroup group = thread.getThreadGroup();
+ while (task == null && group != null) {
+ task = (Task) threadGroupTasks.get(group);
+ group = group.getParent();
+ }
+ }
+ return task;
+ }
+ }
+
+ // Should move to a separate public class - and have API to add
+ // listeners, etc.
+ private static class AntRefTable extends Hashtable {
+
+ AntRefTable() {
+ super();
+ }
+
+ /**
+ * Returns the unmodified original object. This method should be called
+ * internally to get the "real" object. The normal get method
+ * will do the replacement of UnknownElement (this is similar with the
+ * JDNI refs behavior).
+ */
+ private Object getReal(Object key) {
+ return super.get(key);
+ }
+
+ /**
+ * Get method for the reference table. It can be used to hook dynamic
+ * references and to modify some references on the fly--for example for
+ * delayed evaluation.
+ *
+ * It is important to make sure that the processing that is done inside
+ * is not calling get indirectly.
+ *
+ * @param key
+ * lookup key.
+ * @return mapped value.
+ */
+ public Object get(Object key) {
+ // System.out.println("AntRefTable.get " + key);
+ Object o = getReal(key);
+ if (o instanceof UnknownElement) {
+ // Make sure that
+ UnknownElement ue = (UnknownElement) o;
+ ue.maybeConfigure();
+ o = ue.getRealThing();
+ }
+ return o;
+ }
+ }
+
+ /**
+ * Set a reference to this Project on the parameterized object. Need to set
+ * the project before other set/add elements are called.
+ *
+ * @param obj
+ * the object to invoke setProject(this) on.
+ */
+ public final void setProjectReference(final Object obj) {
+ if (obj instanceof ProjectComponent) {
+ ((ProjectComponent) obj).setProject(this);
+ return;
+ }
+ try {
+ Method method = obj.getClass().getMethod("setProject",
+ new Class[] { Project.class });
+ if (method != null) {
+ method.invoke(obj, new Object[] { this });
+ }
+ } catch (Throwable e) {
+ // ignore this if the object does not have
+ // a set project method or the method
+ // is private/protected.
+ }
+ }
+
+ /**
+ * Resolve the file relative to the project's basedir and return it as a
+ * FileResource.
+ *
+ * @param name
+ * the name of the file to resolve.
+ * @return the file resource.
+ * @since Ant 1.7
+ */
+ public Resource getResource(String name) {
+ return new FileResource(getBaseDir(), name);
+ }
+}
diff --git a/command-line-debugger/src/main/org/apache/tools/ant/helper/ProjectHelper2.java b/command-line-debugger/src/main/org/apache/tools/ant/helper/ProjectHelper2.java
new file mode 100644
index 0000000..2876598
--- /dev/null
+++ b/command-line-debugger/src/main/org/apache/tools/ant/helper/ProjectHelper2.java
@@ -0,0 +1,1279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.helper;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ExtensionPoint;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.UnknownElement;
+import org.apache.tools.ant.DebugTask;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.URLProvider;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.apache.tools.zip.ZipFile;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * Sax2 based project reader
+ *
+ */
+public class ProjectHelper2 extends ProjectHelper {
+
+ /** Reference holding the (ordered) target Vector */
+ public static final String REFID_TARGETS = "ant.targets";
+
+ /* Stateless */
+
+ // singletons - since all state is in the context
+ private static AntHandler elementHandler = new ElementHandler();
+ private static AntHandler targetHandler = new TargetHandler();
+ private static AntHandler mainHandler = new MainHandler();
+ private static AntHandler projectHandler = new ProjectHandler();
+
+ /** Specific to ProjectHelper2 so not a true Ant "magic name:" */
+ private static final String REFID_CONTEXT = "ant.parsing.context";
+
+ /**
+ * helper for path -> URI and URI -> path conversions.
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Whether this instance of ProjectHelper can parse an Antlib
+ * descriptor given by the URL and return its content as an
+ * UnknownElement ready to be turned into an Antlib task.
+ *
+ * <p>This implementation returns true.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public boolean canParseAntlibDescriptor(Resource resource) {
+ return true;
+ }
+
+ /**
+ * Parse the given URL as an antlib descriptor and return the
+ * content as something that can be turned into an Antlib task.
+ *
+ * <p>simply delegates to {@link #parseUnknownElement
+ * parseUnknownElement} if the resource provides an URL and throws
+ * an exception otherwise.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public UnknownElement parseAntlibDescriptor(Project containingProject,
+ Resource resource) {
+ URLProvider up = (URLProvider) resource.as(URLProvider.class);
+ if (up == null) {
+ throw new BuildException("Unsupported resource type: " + resource);
+ }
+ return parseUnknownElement(containingProject, up.getURL());
+ }
+
+ /**
+ * Parse an unknown element from a url
+ *
+ * @param project the current project
+ * @param source the url containing the task
+ * @return a configured task
+ * @exception BuildException if an error occurs
+ */
+ public UnknownElement parseUnknownElement(Project project, URL source)
+ throws BuildException {
+ Target dummyTarget = new Target();
+ dummyTarget.setProject(project);
+
+ AntXMLContext context = new AntXMLContext(project);
+ context.addTarget(dummyTarget);
+ context.setImplicitTarget(dummyTarget);
+
+ parse(context.getProject(), source, new RootHandler(context, elementHandler));
+ Task[] tasks = dummyTarget.getTasks();
+ if (tasks.length != 1) {
+ throw new BuildException("No tasks defined");
+ }
+ return (UnknownElement) tasks[0];
+ }
+
+ /**
+ * Parse a source xml input.
+ *
+ * @param project the current project
+ * @param source the xml source
+ * @exception BuildException if an error occurs
+ */
+ public void parse(Project project, Object source) throws BuildException {
+ getImportStack().addElement(source);
+ AntXMLContext context = null;
+ context = (AntXMLContext) project.getReference(REFID_CONTEXT);
+ if (context == null) {
+ context = new AntXMLContext(project);
+ project.addReference(REFID_CONTEXT, context);
+ project.addReference(REFID_TARGETS, context.getTargets());
+ }
+ if (getImportStack().size() > 1) {
+ // we are in an imported file.
+ context.setIgnoreProjectTag(true);
+ Target currentTarget = context.getCurrentTarget();
+ Target currentImplicit = context.getImplicitTarget();
+ Map currentTargets = context.getCurrentTargets();
+ try {
+ Target newCurrent = new Target();
+ newCurrent.setProject(project);
+ newCurrent.setName("");
+ context.setCurrentTarget(newCurrent);
+ context.setCurrentTargets(new HashMap());
+ context.setImplicitTarget(newCurrent);
+ parse(project, source, new RootHandler(context, mainHandler));
+ newCurrent.execute();
+ } finally {
+ context.setCurrentTarget(currentTarget);
+ context.setImplicitTarget(currentImplicit);
+ context.setCurrentTargets(currentTargets);
+ }
+ } else {
+ // top level file
+ context.setCurrentTargets(new HashMap());
+ parse(project, source, new RootHandler(context, mainHandler));
+ // Execute the top-level target
+ context.getImplicitTarget().execute();
+
+ // resolve extensionOf attributes
+ for (Iterator i = getExtensionStack().iterator(); i.hasNext(); ) {
+ String[] extensionInfo = (String[]) i.next();
+ String tgName = extensionInfo[0];
+ String name = extensionInfo[1];
+ OnMissingExtensionPoint missingBehaviour = OnMissingExtensionPoint
+ .valueOf(extensionInfo[2]);
+ Hashtable projectTargets = project.getTargets();
+ if (!projectTargets.containsKey(tgName)) {
+ String message = "can't add target " + name
+ + " to extension-point " + tgName
+ + " because the extension-point is unknown.";
+ if (missingBehaviour == OnMissingExtensionPoint.FAIL) {
+ throw new BuildException(message);
+ } else if (missingBehaviour == OnMissingExtensionPoint.WARN) {
+ Target target = (Target) projectTargets.get(name);
+ context.getProject().log(target,
+ "Warning: " + message,
+ Project.MSG_WARN);
+ }
+ } else {
+ Target t = (Target) projectTargets.get(tgName);
+ if (!(t instanceof ExtensionPoint)) {
+ throw new BuildException("referenced target "
+ + tgName
+ + " is not an extension-point");
+ }
+ t.addDependency(name);
+ }
+ }
+ }
+ }
+
+ /**
+ * Parses the project file, configuring the project as it goes.
+ *
+ * @param project the current project
+ * @param source the xml source
+ * @param handler the root handler to use (contains the current context)
+ * @exception BuildException if the configuration is invalid or cannot
+ * be read
+ */
+ public void parse(Project project, Object source, RootHandler handler) throws BuildException {
+
+ AntXMLContext context = handler.context;
+
+ File buildFile = null;
+ URL url = null;
+ String buildFileName = null;
+
+ if (source instanceof File) {
+ buildFile = (File) source;
+ } else if (source instanceof URL) {
+ url = (URL) source;
+ } else if (source instanceof Resource) {
+ FileProvider fp =
+ (FileProvider) ((Resource) source).as(FileProvider.class);
+ if (fp != null) {
+ buildFile = fp.getFile();
+ } else {
+ URLProvider up =
+ (URLProvider) ((Resource) source).as(URLProvider.class);
+ if (up != null) {
+ url = up.getURL();
+ }
+ }
+ }
+ if (buildFile != null) {
+ buildFile = FILE_UTILS.normalize(buildFile.getAbsolutePath());
+ context.setBuildFile(buildFile);
+ buildFileName = buildFile.toString();
+ } else if (url != null) {
+ try {
+ context.setBuildFile((File) null);
+ context.setBuildFile(url);
+ } catch (java.net.MalformedURLException ex) {
+ throw new BuildException(ex);
+ }
+ buildFileName = url.toString();
+ } else {
+ throw new BuildException("Source " + source.getClass().getName()
+ + " not supported by this plugin");
+ }
+ InputStream inputStream = null;
+ InputSource inputSource = null;
+ ZipFile zf = null;
+
+ try {
+ /**
+ * SAX 2 style parser used to parse the given file.
+ */
+ XMLReader parser = JAXPUtils.getNamespaceXMLReader();
+
+ String uri = null;
+ if (buildFile != null) {
+ uri = FILE_UTILS.toURI(buildFile.getAbsolutePath());
+ inputStream = new FileInputStream(buildFile);
+ } else {
+ uri = url.toString();
+ int pling = -1;
+ if (uri.startsWith("jar:file")
+ && (pling = uri.indexOf("!/")) > -1) {
+ zf = new ZipFile(org.apache.tools.ant.launch.Locator
+ .fromJarURI(uri), "UTF-8");
+ inputStream =
+ zf.getInputStream(zf.getEntry(uri.substring(pling + 1)));
+ } else {
+ inputStream = url.openStream();
+ }
+ }
+
+ inputSource = new InputSource(inputStream);
+ if (uri != null) {
+ inputSource.setSystemId(uri);
+ }
+ project.log("parsing buildfile " + buildFileName + " with URI = "
+ + uri + (zf != null ? " from a zip file" : ""),
+ Project.MSG_VERBOSE);
+
+ DefaultHandler hb = handler;
+
+ parser.setContentHandler(hb);
+ parser.setEntityResolver(hb);
+ parser.setErrorHandler(hb);
+ parser.setDTDHandler(hb);
+ parser.parse(inputSource);
+ } catch (SAXParseException exc) {
+ Location location = new Location(exc.getSystemId(), exc.getLineNumber(), exc
+ .getColumnNumber());
+
+ Throwable t = exc.getException();
+ if (t instanceof BuildException) {
+ BuildException be = (BuildException) t;
+ if (be.getLocation() == Location.UNKNOWN_LOCATION) {
+ be.setLocation(location);
+ }
+ throw be;
+ }
+ throw new BuildException(exc.getMessage(), t == null ? exc : t, location);
+ } catch (SAXException exc) {
+ Throwable t = exc.getException();
+ if (t instanceof BuildException) {
+ throw (BuildException) t;
+ }
+ throw new BuildException(exc.getMessage(), t == null ? exc : t);
+ } catch (FileNotFoundException exc) {
+ throw new BuildException(exc);
+ } catch (UnsupportedEncodingException exc) {
+ throw new BuildException("Encoding of project file " + buildFileName + " is invalid.",
+ exc);
+ } catch (IOException exc) {
+ throw new BuildException("Error reading project file " + buildFileName + ": "
+ + exc.getMessage(), exc);
+ } finally {
+ FileUtils.close(inputStream);
+ ZipFile.closeQuietly(zf);
+ }
+ }
+
+ /**
+ * Returns main handler
+ * @return main handler
+ */
+ protected static AntHandler getMainHandler() {
+ return mainHandler;
+ }
+
+ /**
+ * Sets main handler
+ * @param handler new main handler
+ */
+ protected static void setMainHandler(AntHandler handler) {
+ mainHandler = handler;
+ }
+
+ /**
+ * Returns project handler
+ * @return project handler
+ */
+ protected static AntHandler getProjectHandler() {
+ return projectHandler;
+ }
+
+ /**
+ * Sets project handler
+ * @param handler new project handler
+ */
+ protected static void setProjectHandler(AntHandler handler) {
+ projectHandler = handler;
+ }
+
+ /**
+ * Returns target handler
+ * @return target handler
+ */
+ protected static AntHandler getTargetHandler() {
+ return targetHandler;
+ }
+
+ /**
+ * Sets target handler
+ * @param handler new target handler
+ */
+ protected static void setTargetHandler(AntHandler handler) {
+ targetHandler = handler;
+ }
+
+ /**
+ * Returns element handler
+ * @return element handler
+ */
+ protected static AntHandler getElementHandler() {
+ return elementHandler;
+ }
+
+ /**
+ * Sets element handler
+ * @param handler new element handler
+ */
+ protected static void setElementHandler(AntHandler handler) {
+ elementHandler = handler;
+ }
+
+ /**
+ * The common superclass for all SAX event handlers used to parse
+ * the configuration file.
+ *
+ * The context will hold all state information. At each time
+ * there is one active handler for the current element. It can
+ * use onStartChild() to set an alternate handler for the child.
+ */
+ public static class AntHandler {
+ /**
+ * Handles the start of an element. This base implementation does
+ * nothing.
+ *
+ * @param uri the namespace URI for the tag
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name of the element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The context that this element is in.
+ *
+ * @exception SAXParseException if this method is not overridden, or in
+ * case of error in an overridden version
+ */
+ public void onStartElement(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ }
+
+ /**
+ * Handles the start of an element. This base implementation just
+ * throws an exception - you must override this method if you expect
+ * child elements.
+ *
+ * @param uri The namespace uri for this element.
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The current context.
+ * @return a handler (in the derived classes)
+ *
+ * @exception SAXParseException if this method is not overridden, or in
+ * case of error in an overridden version
+ */
+ public AntHandler onStartChild(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ throw new SAXParseException("Unexpected element \"" + qname + " \"", context
+ .getLocator());
+ }
+
+ /**
+ * Handle the end of a element.
+ *
+ * @param uri the namespace uri of the element
+ * @param tag the tag of the element
+ * @param qname the qualified name of the element
+ * @param context the current context
+ * @exception SAXParseException if an error occurs
+ */
+ public void onEndChild(String uri, String tag, String qname, AntXMLContext context)
+ throws SAXParseException {
+ }
+
+ /**
+ * This method is called when this element and all elements nested into it have been
+ * handled. I.e., this happens at the </end_tag_of_the_element>.
+ * @param uri the namespace uri for this element
+ * @param tag the element name
+ * @param context the current context
+ */
+ public void onEndElement(String uri, String tag, AntXMLContext context) {
+ }
+
+ /**
+ * Handles text within an element. This base implementation just
+ * throws an exception, you must override it if you expect content.
+ *
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ * @param context The current context.
+ *
+ * @exception SAXParseException if this method is not overridden, or in
+ * case of error in an overridden version
+ */
+ public void characters(char[] buf, int start, int count, AntXMLContext context)
+ throws SAXParseException {
+ String s = new String(buf, start, count).trim();
+
+ if (s.length() > 0) {
+ throw new SAXParseException("Unexpected text \"" + s + "\"", context.getLocator());
+ }
+ }
+
+ /**
+ * Will be called every time a namespace is reached.
+ * It'll verify if the ns was processed, and if not load the task definitions.
+ * @param uri The namespace uri.
+ */
+ protected void checkNamespace(String uri) {
+ }
+ }
+
+ /**
+ * Handler for ant processing. Uses a stack of AntHandlers to
+ * implement each element ( the original parser used a recursive behavior,
+ * with the implicit execution stack )
+ */
+ public static class RootHandler extends DefaultHandler {
+ private Stack antHandlers = new Stack();
+ private AntHandler currentHandler = null;
+ private AntXMLContext context;
+
+ /**
+ * Creates a new RootHandler instance.
+ *
+ * @param context The context for the handler.
+ * @param rootHandler The handler for the root element.
+ */
+ public RootHandler(AntXMLContext context, AntHandler rootHandler) {
+ currentHandler = rootHandler;
+ antHandlers.push(currentHandler);
+ this.context = context;
+ }
+
+ /**
+ * Returns the current ant handler object.
+ * @return the current ant handler.
+ */
+ public AntHandler getCurrentAntHandler() {
+ return currentHandler;
+ }
+
+ /**
+ * Resolves file: URIs relative to the build file.
+ *
+ * @param publicId The public identifier, or <code>null</code>
+ * if none is available. Ignored in this
+ * implementation.
+ * @param systemId The system identifier provided in the XML
+ * document. Will not be <code>null</code>.
+ * @return an inputsource for this identifier
+ */
+ public InputSource resolveEntity(String publicId, String systemId) {
+
+ context.getProject().log("resolving systemId: " + systemId, Project.MSG_VERBOSE);
+
+ if (systemId.startsWith("file:")) {
+ String path = FILE_UTILS.fromURI(systemId);
+
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ file = FILE_UTILS.resolveFile(context.getBuildFileParent(), path);
+ context.getProject().log(
+ "Warning: '" + systemId + "' in " + context.getBuildFile()
+ + " should be expressed simply as '" + path.replace('\\', '/')
+ + "' for compliance with other XML tools", Project.MSG_WARN);
+ }
+ context.getProject().log("file=" + file, Project.MSG_DEBUG);
+ try {
+ InputSource inputSource = new InputSource(new FileInputStream(file));
+ inputSource.setSystemId(FILE_UTILS.toURI(file.getAbsolutePath()));
+ return inputSource;
+ } catch (FileNotFoundException fne) {
+ context.getProject().log(file.getAbsolutePath() + " could not be found",
+ Project.MSG_WARN);
+ }
+
+ }
+ // use default if not file or file not found
+ context.getProject().log("could not resolve systemId", Project.MSG_DEBUG);
+ return null;
+ }
+
+ /**
+ * Handles the start of a project element. A project handler is created
+ * and initialised with the element name and attributes.
+ *
+ * @param uri The namespace uri for this element.
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ *
+ * @exception org.xml.sax.SAXParseException if the tag given is not
+ * <code>"project"</code>
+ */
+ public void startElement(String uri, String tag, String qname, Attributes attrs)
+ throws SAXParseException {
+ AntHandler next = currentHandler.onStartChild(uri, tag, qname, attrs, context);
+ antHandlers.push(currentHandler);
+ currentHandler = next;
+ currentHandler.onStartElement(uri, tag, qname, attrs, context);
+ }
+
+ /**
+ * Sets the locator in the project helper for future reference.
+ *
+ * @param locator The locator used by the parser.
+ * Will not be <code>null</code>.
+ */
+ public void setDocumentLocator(Locator locator) {
+ context.setLocator(locator);
+ }
+
+ /**
+ * Handles the end of an element. Any required clean-up is performed
+ * by the onEndElement() method and then the original handler is restored to the parser.
+ *
+ * @param uri The namespace URI for this element.
+ * @param name The name of the element which is ending.
+ * Will not be <code>null</code>.
+ * @param qName The qualified name for this element.
+ *
+ * @exception SAXException in case of error (not thrown in this implementation)
+ */
+ public void endElement(String uri, String name, String qName) throws SAXException {
+ currentHandler.onEndElement(uri, name, context);
+ AntHandler prev = (AntHandler) antHandlers.pop();
+ currentHandler = prev;
+ if (currentHandler != null) {
+ currentHandler.onEndChild(uri, name, qName, context);
+ }
+ }
+
+ /**
+ * Handle text within an element, calls currentHandler.characters.
+ *
+ * @param buf A character array of the test.
+ * @param start The start offset in the array.
+ * @param count The number of characters to read.
+ * @exception SAXParseException if an error occurs
+ */
+ public void characters(char[] buf, int start, int count) throws SAXParseException {
+ currentHandler.characters(buf, start, count, context);
+ }
+
+ /**
+ * Start a namespace prefix to uri mapping
+ *
+ * @param prefix the namespace prefix
+ * @param uri the namespace uri
+ */
+ public void startPrefixMapping(String prefix, String uri) {
+ context.startPrefixMapping(prefix, uri);
+ }
+
+ /**
+ * End a namepace prefix to uri mapping
+ *
+ * @param prefix the prefix that is not mapped anymore
+ */
+ public void endPrefixMapping(String prefix) {
+ context.endPrefixMapping(prefix);
+ }
+ }
+
+ /**
+ * The main handler - it handles the <project> tag.
+ *
+ * @see org.apache.tools.ant.helper.ProjectHelper2.AntHandler
+ */
+ public static class MainHandler extends AntHandler {
+
+ /**
+ * Handle the project tag
+ *
+ * @param uri The namespace uri.
+ * @param name The element tag.
+ * @param qname The element qualified name.
+ * @param attrs The attributes of the element.
+ * @param context The current context.
+ * @return The project handler that handles subelements of project
+ * @exception SAXParseException if the qualified name is not "project".
+ */
+ public AntHandler onStartChild(String uri, String name, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ if (name.equals("project")
+ && (uri.equals("") || uri.equals(ANT_CORE_URI))) {
+ return ProjectHelper2.projectHandler;
+ }
+ if (name.equals(qname)) {
+ throw new SAXParseException("Unexpected element \"{" + uri
+ + "}" + name + "\" {" + ANT_CORE_URI + "}" + name, context.getLocator());
+ }
+ throw new SAXParseException("Unexpected element \"" + qname
+ + "\" " + name, context.getLocator());
+ }
+ }
+
+ /**
+ * Handler for the top level "project" element.
+ */
+ public static class ProjectHandler extends AntHandler {
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. The attributes which
+ * this handler can deal with are: <code>"default"</code>,
+ * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
+ *
+ * @param uri The namespace URI for this element.
+ * @param tag Name of the element which caused this handler
+ * to be created. Should not be <code>null</code>.
+ * Ignored in this implementation.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ * @param context The current context.
+ *
+ * @exception SAXParseException if an unexpected attribute is
+ * encountered or if the <code>"default"</code> attribute
+ * is missing.
+ */
+ public void onStartElement(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ String baseDir = null;
+ boolean nameAttributeSet = false;
+
+ Project project = context.getProject();
+ // Set the location of the implicit target associated with the project tag
+ context.getImplicitTarget().setLocation(new Location(context.getLocator()));
+
+ /** XXX I really don't like this - the XML processor is still
+ * too 'involved' in the processing. A better solution (IMO)
+ * would be to create UE for Project and Target too, and
+ * then process the tree and have Project/Target deal with
+ * its attributes ( similar with Description ).
+ *
+ * If we eventually switch to ( or add support for ) DOM,
+ * things will work smoothly - UE can be avoided almost completely
+ * ( it could still be created on demand, for backward compatibility )
+ */
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String attrUri = attrs.getURI(i);
+ if (attrUri != null && !attrUri.equals("") && !attrUri.equals(uri)) {
+ continue; // Ignore attributes from unknown uris
+ }
+ String key = attrs.getLocalName(i);
+ String value = attrs.getValue(i);
+
+ if (key.equals("default")) {
+ if (value != null && !value.equals("")) {
+ if (!context.isIgnoringProjectTag()) {
+ project.setDefault(value);
+ }
+ }
+ } else if (key.equals("name")) {
+ if (value != null) {
+ context.setCurrentProjectName(value);
+ nameAttributeSet = true;
+ if (!context.isIgnoringProjectTag()) {
+ project.setName(value);
+ project.addReference(value, project);
+ } else if (isInIncludeMode()) {
+ if (!"".equals(value)
+ && (getCurrentTargetPrefix() == null
+ || getCurrentTargetPrefix().length() == 0)
+ ) {
+ // help nested include tasks
+ setCurrentTargetPrefix(value);
+ }
+ }
+ }
+ } else if (key.equals("id")) {
+ if (value != null) {
+ // What's the difference between id and name ?
+ if (!context.isIgnoringProjectTag()) {
+ project.addReference(value, project);
+ }
+ }
+ } else if (key.equals("basedir")) {
+ if (!context.isIgnoringProjectTag()) {
+ baseDir = value;
+ }
+ } else {
+ // XXX ignore attributes in a different NS ( maybe store them ? )
+ throw new SAXParseException("Unexpected attribute \"" + attrs.getQName(i)
+ + "\"", context.getLocator());
+ }
+ }
+
+ // XXX Move to Project ( so it is shared by all helpers )
+ String antFileProp =
+ MagicNames.ANT_FILE + "." + context.getCurrentProjectName();
+ String dup = project.getProperty(antFileProp);
+ String typeProp =
+ MagicNames.ANT_FILE_TYPE + "." + context.getCurrentProjectName();
+ String dupType = project.getProperty(typeProp);
+ if (dup != null && nameAttributeSet) {
+ Object dupFile = null;
+ Object contextFile = null;
+ if (MagicNames.ANT_FILE_TYPE_URL.equals(dupType)) {
+ try {
+ dupFile = new URL(dup);
+ } catch (java.net.MalformedURLException mue) {
+ throw new BuildException("failed to parse "
+ + dup + " as URL while looking"
+ + " at a duplicate project"
+ + " name.", mue);
+ }
+ contextFile = context.getBuildFileURL();
+ } else {
+ dupFile = new File(dup);
+ contextFile = context.getBuildFile();
+ }
+
+ if (context.isIgnoringProjectTag() && !dupFile.equals(contextFile)) {
+ project.log("Duplicated project name in import. Project "
+ + context.getCurrentProjectName() + " defined first in " + dup
+ + " and again in " + contextFile, Project.MSG_WARN);
+ }
+ }
+ if (nameAttributeSet) {
+ if (context.getBuildFile() != null) {
+ project.setUserProperty(antFileProp,
+ context.getBuildFile().toString());
+ project.setUserProperty(typeProp,
+ MagicNames.ANT_FILE_TYPE_FILE);
+ } else if (context.getBuildFileURL() != null) {
+ project.setUserProperty(antFileProp,
+ context.getBuildFileURL().toString());
+ project.setUserProperty(typeProp,
+ MagicNames.ANT_FILE_TYPE_URL);
+ }
+ }
+ if (context.isIgnoringProjectTag()) {
+ // no further processing
+ return;
+ }
+ // set explicitly before starting ?
+ if (project.getProperty("basedir") != null) {
+ project.setBasedir(project.getProperty("basedir"));
+ } else {
+ // Default for baseDir is the location of the build file.
+ if (baseDir == null) {
+ project.setBasedir(context.getBuildFileParent().getAbsolutePath());
+ } else {
+ // check whether the user has specified an absolute path
+ if ((new File(baseDir)).isAbsolute()) {
+ project.setBasedir(baseDir);
+ } else {
+ project.setBaseDir(FILE_UTILS.resolveFile(context.getBuildFileParent(),
+ baseDir));
+ }
+ }
+ }
+ project.addTarget("", context.getImplicitTarget());
+ context.setCurrentTarget(context.getImplicitTarget());
+ }
+
+ /**
+ * Handles the start of a top-level element within the project. An
+ * appropriate handler is created and initialised with the details
+ * of the element.
+ *
+ * @param uri The namespace URI for this element.
+ * @param name The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The context for this element.
+ * @return a target or an element handler.
+ *
+ * @exception org.xml.sax.SAXParseException if the tag given is not
+ * <code>"taskdef"</code>, <code>"typedef"</code>,
+ * <code>"property"</code>, <code>"target"</code>,
+ * <code>"extension-point"</code>
+ * or a data type definition
+ */
+ public AntHandler onStartChild(String uri, String name, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ return (name.equals("target") || name.equals("extension-point"))
+ && (uri.equals("") || uri.equals(ANT_CORE_URI))
+ ? ProjectHelper2.targetHandler : ProjectHelper2.elementHandler;
+ }
+ }
+
+ /**
+ * Handler for "target" and "extension-point" elements.
+ */
+ public static class TargetHandler extends AntHandler {
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. The attributes which
+ * this handler can deal with are: <code>"name"</code>,
+ * <code>"depends"</code>, <code>"if"</code>,
+ * <code>"unless"</code>, <code>"id"</code> and
+ * <code>"description"</code>.
+ *
+ * @param uri The namespace URI for this element.
+ * @param tag Name of the element which caused this handler
+ * to be created. Should not be <code>null</code>.
+ * Ignored in this implementation.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ * @param context The current context.
+ *
+ * @exception SAXParseException if an unexpected attribute is encountered
+ * or if the <code>"name"</code> attribute is missing.
+ */
+ public void onStartElement(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ String name = null;
+ String depends = "";
+ String extensionPoint = null;
+ OnMissingExtensionPoint extensionPointMissing = null;
+
+ Project project = context.getProject();
+ Target target = "target".equals(tag)
+ ? new Target() : new ExtensionPoint();
+ target.setProject(project);
+ target.setLocation(new Location(context.getLocator()));
+ context.addTarget(target);
+ boolean isTarget = false;
+ if(!(target instanceof ExtensionPoint)) {
+ isTarget = true;
+ }
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String attrUri = attrs.getURI(i);
+ if (attrUri != null && !attrUri.equals("") && !attrUri.equals(uri)) {
+ continue; // Ignore attributes from unknown uris
+ }
+ String key = attrs.getLocalName(i);
+ String value = attrs.getValue(i);
+
+ if (key.equals("name")) {
+ name = value;
+ if ("".equals(name)) {
+ throw new BuildException("name attribute must " + "not be empty");
+ }
+ } else if (key.equals("depends")) {
+ depends = value;
+ } else if (key.equals("if")) {
+ target.setIf(value);
+ } else if (key.equals("unless")) {
+ target.setUnless(value);
+ } else if (key.equals("id")) {
+ if (value != null && !value.equals("")) {
+ context.getProject().addReference(value, target);
+ }
+ } else if (key.equals("description")) {
+ target.setDescription(value);
+ } else if (key.equals("extensionOf")) {
+ extensionPoint = value;
+ } else if (key.equals("onMissingExtensionPoint")) {
+ try {
+ extensionPointMissing = OnMissingExtensionPoint.valueOf(value);
+ } catch (IllegalArgumentException e) {
+ throw new BuildException("Invalid onMissingExtensionPoint " + value);
+ }
+ } else {
+ throw new SAXParseException("Unexpected attribute \"" + key + "\"", context
+ .getLocator());
+ }
+ }
+
+ if (name == null) {
+ throw new SAXParseException("target element appears without a name attribute",
+ context.getLocator());
+ }
+
+ String prefix = null;
+ boolean isInIncludeMode =
+ context.isIgnoringProjectTag() && isInIncludeMode();
+ String sep = getCurrentPrefixSeparator();
+
+ if (isInIncludeMode) {
+ prefix = getTargetPrefix(context);
+ if (prefix == null) {
+ throw new BuildException("can't include build file "
+ + context.getBuildFileURL()
+ + ", no as attribute has been given"
+ + " and the project tag doesn't"
+ + " specify a name attribute");
+ }
+ name = prefix + sep + name;
+ }
+
+ // Check if this target is in the current build file
+ if (context.getCurrentTargets().get(name) != null) {
+ throw new BuildException("Duplicate target '" + name + "'",
+ target.getLocation());
+ }
+ Hashtable projectTargets = project.getTargets();
+ boolean usedTarget = false;
+ // If the name has not already been defined define it
+ if (projectTargets.containsKey(name)) {
+ project.log("Already defined in main or a previous import, ignore " + name,
+ Project.MSG_VERBOSE);
+ } else {
+ target.setName(name);
+ context.getCurrentTargets().put(name, target);
+ project.addOrReplaceTarget(name, target);
+ usedTarget = true;
+ }
+
+ if (depends.length() > 0) {
+ if (!isInIncludeMode) {
+ target.setDepends(depends);
+ } else {
+ for (Iterator iter =
+ Target.parseDepends(depends, name, "depends")
+ .iterator();
+ iter.hasNext(); ) {
+ target.addDependency(prefix + sep + iter.next());
+ }
+ }
+ }
+
+ if(isTarget && name.equals(project.getBreakAt())) {
+ // If the current element is a target and it is the same one as specified
+ // for Ant to suspend execution at, then add an internal dependency on
+ // the Debug Target
+ DebugTask.createDebugTarget(project);
+
+ // add this dependency to the end of the dependency list so that this target
+ // executes immediately ahead of the -breakAt target
+ target.addDependency(DebugTask.DEBUG_TARGET_NAME);
+ }
+
+ if (!isInIncludeMode && context.isIgnoringProjectTag()
+ && (prefix = getTargetPrefix(context)) != null) {
+ // In an imported file (and not completely
+ // ignoring the project tag or having a preconfigured prefix)
+ String newName = prefix + sep + name;
+ Target newTarget = usedTarget ? new Target(target) : target;
+ newTarget.setName(newName);
+ context.getCurrentTargets().put(newName, newTarget);
+ project.addOrReplaceTarget(newName, newTarget);
+ }
+ if (extensionPointMissing != null && extensionPoint == null) {
+ throw new BuildException("onMissingExtensionPoint attribute cannot " +
+ "be specified unless extensionOf is specified",
+ target.getLocation());
+
+ }
+ if (extensionPoint != null) {
+ ProjectHelper helper =
+ (ProjectHelper) context.getProject().
+ getReference(ProjectHelper.PROJECTHELPER_REFERENCE);
+ for (Iterator iter =
+ Target.parseDepends(extensionPoint, name, "extensionOf")
+ .iterator();
+ iter.hasNext(); ) {
+ String tgName = (String) iter.next();
+ if (isInIncludeMode()) {
+ tgName = prefix + sep + tgName;
+ }
+ if (extensionPointMissing == null) {
+ extensionPointMissing = OnMissingExtensionPoint.FAIL;
+ }
+ // defer extensionpoint resolution until the full
+ // import stack has been processed
+ helper.getExtensionStack().add(new String[] {
+ tgName, name, extensionPointMissing.name() });
+ }
+ }
+ }
+
+ private String getTargetPrefix(AntXMLContext context) {
+ String configuredValue = getCurrentTargetPrefix();
+ if (configuredValue != null && configuredValue.length() == 0) {
+ configuredValue = null;
+ }
+ if (configuredValue != null) {
+ return configuredValue;
+ }
+
+ String projectName = context.getCurrentProjectName();
+ if ("".equals(projectName)) {
+ projectName = null;
+ }
+
+ return projectName;
+ }
+
+ /**
+ * Handles the start of an element within a target.
+ *
+ * @param uri The namespace URI for this element.
+ * @param name The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The current context.
+ * @return an element handler.
+ *
+ * @exception SAXParseException if an error occurs when initialising
+ * the appropriate child handler
+ */
+ public AntHandler onStartChild(String uri, String name, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ return ProjectHelper2.elementHandler;
+ }
+
+ /**
+ * Handle the end of the project, sets the current target of the
+ * context to be the implicit target.
+ *
+ * @param uri The namespace URI of the element.
+ * @param tag The name of the element.
+ * @param context The current context.
+ */
+ public void onEndElement(String uri, String tag, AntXMLContext context) {
+ context.setCurrentTarget(context.getImplicitTarget());
+ }
+ }
+
+ /**
+ * Handler for all project elements ( tasks, data types )
+ */
+ public static class ElementHandler extends AntHandler {
+
+ /**
+ * Constructor.
+ */
+ public ElementHandler() {
+ }
+
+ /**
+ * Initialisation routine called after handler creation
+ * with the element name and attributes. This configures
+ * the element with its attributes and sets it up with
+ * its parent container (if any). Nested elements are then
+ * added later as the parser encounters them.
+ *
+ * @param uri The namespace URI for this element.
+ * @param tag Name of the element which caused this handler
+ * to be created. Must not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element which caused this
+ * handler to be created. Must not be <code>null</code>.
+ * @param context The current context.
+ *
+ * @exception SAXParseException in case of error (not thrown in
+ * this implementation)
+ */
+ public void onStartElement(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ RuntimeConfigurable parentWrapper = context.currentWrapper();
+ Object parent = null;
+
+ if (parentWrapper != null) {
+ parent = parentWrapper.getProxy();
+ }
+
+ /* UnknownElement is used for tasks and data types - with
+ delayed eval */
+ UnknownElement task = new UnknownElement(tag);
+ task.setProject(context.getProject());
+ task.setNamespace(uri);
+ task.setQName(qname);
+ task.setTaskType(ProjectHelper.genComponentName(task.getNamespace(), tag));
+ task.setTaskName(qname);
+
+ Location location = new Location(context.getLocator().getSystemId(), context
+ .getLocator().getLineNumber(), context.getLocator().getColumnNumber());
+ task.setLocation(location);
+ task.setOwningTarget(context.getCurrentTarget());
+
+ if (parent != null) {
+ // Nested element
+ ((UnknownElement) parent).addChild(task);
+ } else {
+ // Task included in a target ( including the default one ).
+ context.getCurrentTarget().addTask(task);
+ }
+
+ context.configureId(task, attrs);
+
+ // container.addTask(task);
+ // This is a nop in UE: task.init();
+
+ RuntimeConfigurable wrapper = new RuntimeConfigurable(task, task.getTaskName());
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String name = attrs.getLocalName(i);
+ String attrUri = attrs.getURI(i);
+ if (attrUri != null && !attrUri.equals("") && !attrUri.equals(uri)) {
+ name = attrUri + ":" + attrs.getQName(i);
+ }
+ String value = attrs.getValue(i);
+ // PR: Hack for ant-type value
+ // an ant-type is a component name which can
+ // be namespaced, need to extract the name
+ // and convert from qualified name to uri/name
+ if (ANT_TYPE.equals(name)
+ || (ANT_CORE_URI.equals(attrUri)
+ && ANT_TYPE.equals(attrs.getLocalName(i)))) {
+ name = ANT_TYPE;
+ int index = value.indexOf(":");
+ if (index >= 0) {
+ String prefix = value.substring(0, index);
+ String mappedUri = context.getPrefixMapping(prefix);
+ if (mappedUri == null) {
+ throw new BuildException("Unable to find XML NS prefix \"" + prefix
+ + "\"");
+ }
+ value = ProjectHelper.genComponentName(mappedUri, value
+ .substring(index + 1));
+ }
+ }
+ wrapper.setAttribute(name, value);
+ }
+ if (parentWrapper != null) {
+ parentWrapper.addChild(wrapper);
+ }
+ context.pushWrapper(wrapper);
+ }
+
+ /**
+ * Adds text to the task, using the wrapper
+ *
+ * @param buf A character array of the text within the element.
+ * Will not be <code>null</code>.
+ * @param start The start element in the array.
+ * @param count The number of characters to read from the array.
+ * @param context The current context.
+ *
+ * @exception SAXParseException if the element doesn't support text
+ *
+ * @see ProjectHelper#addText(Project,java.lang.Object,char[],int,int)
+ */
+ public void characters(char[] buf, int start, int count,
+ AntXMLContext context) throws SAXParseException {
+ RuntimeConfigurable wrapper = context.currentWrapper();
+ wrapper.addText(buf, start, count);
+ }
+
+ /**
+ * Handles the start of an element within a target. Task containers
+ * will always use another task handler, and all other tasks
+ * will always use a nested element handler.
+ *
+ * @param uri The namespace URI for this element.
+ * @param tag The name of the element being started.
+ * Will not be <code>null</code>.
+ * @param qname The qualified name for this element.
+ * @param attrs Attributes of the element being started.
+ * Will not be <code>null</code>.
+ * @param context The current context.
+ * @return The handler for elements.
+ *
+ * @exception SAXParseException if an error occurs when initialising
+ * the appropriate child handler
+ */
+ public AntHandler onStartChild(String uri, String tag, String qname, Attributes attrs,
+ AntXMLContext context) throws SAXParseException {
+ return ProjectHelper2.elementHandler;
+ }
+
+ /**
+ * Handles the end of the element. This pops the wrapper from
+ * the context.
+ *
+ * @param uri The namespace URI for the element.
+ * @param tag The name of the element.
+ * @param context The current context.
+ */
+ public void onEndElement(String uri, String tag, AntXMLContext context) {
+ context.popWrapper();
+ }
+ }
+}
diff --git a/command-line-debugger/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/command-line-debugger/src/main/org/apache/tools/ant/taskdefs/defaults.properties
new file mode 100644
index 0000000..435a1a2
--- /dev/null
+++ b/command-line-debugger/src/main/org/apache/tools/ant/taskdefs/defaults.properties
@@ -0,0 +1,233 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# standard ant tasks
+ant=org.apache.tools.ant.taskdefs.Ant
+antcall=org.apache.tools.ant.taskdefs.CallTarget
+antstructure=org.apache.tools.ant.taskdefs.AntStructure
+antversion=org.apache.tools.ant.taskdefs.condition.AntVersion
+apply=org.apache.tools.ant.taskdefs.Transform
+apt=org.apache.tools.ant.taskdefs.Apt
+augment=org.apache.tools.ant.taskdefs.AugmentReference
+available=org.apache.tools.ant.taskdefs.Available
+basename=org.apache.tools.ant.taskdefs.Basename
+bindtargets=org.apache.tools.ant.taskdefs.BindTargets
+buildnumber=org.apache.tools.ant.taskdefs.BuildNumber
+bunzip2=org.apache.tools.ant.taskdefs.BUnzip2
+bzip2=org.apache.tools.ant.taskdefs.BZip2
+checksum=org.apache.tools.ant.taskdefs.Checksum
+chmod=org.apache.tools.ant.taskdefs.Chmod
+classloader=org.apache.tools.ant.taskdefs.Classloader
+componentdef=org.apache.tools.ant.taskdefs.Componentdef
+concat=org.apache.tools.ant.taskdefs.Concat
+condition=org.apache.tools.ant.taskdefs.ConditionTask
+copy=org.apache.tools.ant.taskdefs.Copy
+cvs=org.apache.tools.ant.taskdefs.Cvs
+cvschangelog=org.apache.tools.ant.taskdefs.cvslib.ChangeLogTask
+cvspass=org.apache.tools.ant.taskdefs.CVSPass
+cvstagdiff=org.apache.tools.ant.taskdefs.cvslib.CvsTagDiff
+cvsversion=org.apache.tools.ant.taskdefs.cvslib.CvsVersion
+defaultexcludes=org.apache.tools.ant.taskdefs.DefaultExcludes
+delete=org.apache.tools.ant.taskdefs.Delete
+dependset=org.apache.tools.ant.taskdefs.DependSet
+diagnostics=org.apache.tools.ant.taskdefs.DiagnosticsTask
+dirname=org.apache.tools.ant.taskdefs.Dirname
+ear=org.apache.tools.ant.taskdefs.Ear
+echo=org.apache.tools.ant.taskdefs.Echo
+echoproperties=org.apache.tools.ant.taskdefs.optional.EchoProperties
+echoxml=org.apache.tools.ant.taskdefs.EchoXML
+exec=org.apache.tools.ant.taskdefs.ExecTask
+fail=org.apache.tools.ant.taskdefs.Exit
+filter=org.apache.tools.ant.taskdefs.Filter
+fixcrlf=org.apache.tools.ant.taskdefs.FixCRLF
+#funtest=org.apache.tools.ant.taskdefs.optional.testing.Funtest
+genkey=org.apache.tools.ant.taskdefs.GenerateKey
+get=org.apache.tools.ant.taskdefs.Get
+gunzip=org.apache.tools.ant.taskdefs.GUnzip
+gzip=org.apache.tools.ant.taskdefs.GZip
+hostinfo=org.apache.tools.ant.taskdefs.HostInfo
+import=org.apache.tools.ant.taskdefs.ImportTask
+include=org.apache.tools.ant.taskdefs.ImportTask
+input=org.apache.tools.ant.taskdefs.Input
+jar=org.apache.tools.ant.taskdefs.Jar
+java=org.apache.tools.ant.taskdefs.Java
+javac=org.apache.tools.ant.taskdefs.Javac
+javadoc=org.apache.tools.ant.taskdefs.Javadoc
+length=org.apache.tools.ant.taskdefs.Length
+loadfile=org.apache.tools.ant.taskdefs.LoadFile
+loadproperties=org.apache.tools.ant.taskdefs.LoadProperties
+loadresource=org.apache.tools.ant.taskdefs.LoadResource
+local=org.apache.tools.ant.taskdefs.Local
+macrodef=org.apache.tools.ant.taskdefs.MacroDef
+mail=org.apache.tools.ant.taskdefs.email.EmailTask
+makeurl=org.apache.tools.ant.taskdefs.MakeUrl
+manifest=org.apache.tools.ant.taskdefs.ManifestTask
+manifestclasspath=org.apache.tools.ant.taskdefs.ManifestClassPath
+mkdir=org.apache.tools.ant.taskdefs.Mkdir
+move=org.apache.tools.ant.taskdefs.Move
+nice=org.apache.tools.ant.taskdefs.Nice
+parallel=org.apache.tools.ant.taskdefs.Parallel
+patch=org.apache.tools.ant.taskdefs.Patch
+pathconvert=org.apache.tools.ant.taskdefs.PathConvert
+presetdef=org.apache.tools.ant.taskdefs.PreSetDef
+projecthelper=org.apache.tools.ant.taskdefs.ProjectHelperTask
+property=org.apache.tools.ant.taskdefs.Property
+propertyhelper=org.apache.tools.ant.taskdefs.PropertyHelperTask
+record=org.apache.tools.ant.taskdefs.Recorder
+replace=org.apache.tools.ant.taskdefs.Replace
+resourcecount=org.apache.tools.ant.taskdefs.ResourceCount
+retry=org.apache.tools.ant.taskdefs.Retry
+rmic=org.apache.tools.ant.taskdefs.Rmic
+sequential=org.apache.tools.ant.taskdefs.Sequential
+signjar=org.apache.tools.ant.taskdefs.SignJar
+sleep=org.apache.tools.ant.taskdefs.Sleep
+sql=org.apache.tools.ant.taskdefs.SQLExec
+subant=org.apache.tools.ant.taskdefs.SubAnt
+sync=org.apache.tools.ant.taskdefs.Sync
+tar=org.apache.tools.ant.taskdefs.Tar
+taskdef=org.apache.tools.ant.taskdefs.Taskdef
+tempfile=org.apache.tools.ant.taskdefs.TempFile
+touch=org.apache.tools.ant.taskdefs.Touch
+tstamp=org.apache.tools.ant.taskdefs.Tstamp
+truncate=org.apache.tools.ant.taskdefs.Truncate
+typedef=org.apache.tools.ant.taskdefs.Typedef
+unjar=org.apache.tools.ant.taskdefs.Expand
+untar=org.apache.tools.ant.taskdefs.Untar
+unwar=org.apache.tools.ant.taskdefs.Expand
+unzip=org.apache.tools.ant.taskdefs.Expand
+uptodate=org.apache.tools.ant.taskdefs.UpToDate
+waitfor=org.apache.tools.ant.taskdefs.WaitFor
+war=org.apache.tools.ant.taskdefs.War
+whichresource=org.apache.tools.ant.taskdefs.WhichResource
+xmlproperty=org.apache.tools.ant.taskdefs.XmlProperty
+xslt=org.apache.tools.ant.taskdefs.XSLTProcess
+zip=org.apache.tools.ant.taskdefs.Zip
+
+# optional tasks
+antlr=org.apache.tools.ant.taskdefs.optional.ANTLR
+attrib=org.apache.tools.ant.taskdefs.optional.windows.Attrib
+blgenclient=org.apache.tools.ant.taskdefs.optional.ejb.BorlandGenerateClient
+cab=org.apache.tools.ant.taskdefs.optional.Cab
+cccheckin=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckin
+cccheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckout
+cclock=org.apache.tools.ant.taskdefs.optional.clearcase.CCLock
+ccmcheckin=org.apache.tools.ant.taskdefs.optional.ccm.CCMCheckin
+ccmcheckintask=org.apache.tools.ant.taskdefs.optional.ccm.CCMCheckinDefault
+ccmcheckout=org.apache.tools.ant.taskdefs.optional.ccm.CCMCheckout
+ccmcreatetask=org.apache.tools.ant.taskdefs.optional.ccm.CCMCreateTask
+ccmkattr=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkattr
+ccmkbl=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkbl
+ccmkdir=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkdir
+ccmkelem=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkelem
+ccmklabel=org.apache.tools.ant.taskdefs.optional.clearcase.CCMklabel
+ccmklbtype=org.apache.tools.ant.taskdefs.optional.clearcase.CCMklbtype
+ccmreconfigure=org.apache.tools.ant.taskdefs.optional.ccm.CCMReconfigure
+ccrmtype=org.apache.tools.ant.taskdefs.optional.clearcase.CCRmtype
+ccuncheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCUnCheckout
+ccunlock=org.apache.tools.ant.taskdefs.optional.clearcase.CCUnlock
+ccupdate=org.apache.tools.ant.taskdefs.optional.clearcase.CCUpdate
+chgrp=org.apache.tools.ant.taskdefs.optional.unix.Chgrp
+chown=org.apache.tools.ant.taskdefs.optional.unix.Chown
+ddcreator=org.apache.tools.ant.taskdefs.optional.ejb.DDCreator
+depend=org.apache.tools.ant.taskdefs.optional.depend.Depend
+ejbc=org.apache.tools.ant.taskdefs.optional.ejb.Ejbc
+ejbjar=org.apache.tools.ant.taskdefs.optional.ejb.EjbJar
+ftp=org.apache.tools.ant.taskdefs.optional.net.FTP
+image=org.apache.tools.ant.taskdefs.optional.image.Image
+iplanet-ejbc=org.apache.tools.ant.taskdefs.optional.ejb.IPlanetEjbcTask
+jarlib-available=org.apache.tools.ant.taskdefs.optional.extension.JarLibAvailableTask
+jarlib-display=org.apache.tools.ant.taskdefs.optional.extension.JarLibDisplayTask
+jarlib-manifest=org.apache.tools.ant.taskdefs.optional.extension.JarLibManifestTask
+jarlib-resolve=org.apache.tools.ant.taskdefs.optional.extension.JarLibResolveTask
+javacc=org.apache.tools.ant.taskdefs.optional.javacc.JavaCC
+javah=org.apache.tools.ant.taskdefs.optional.Javah
+jdepend=org.apache.tools.ant.taskdefs.optional.jdepend.JDependTask
+jjdoc=org.apache.tools.ant.taskdefs.optional.javacc.JJDoc
+jjtree=org.apache.tools.ant.taskdefs.optional.javacc.JJTree
+junit=org.apache.tools.ant.taskdefs.optional.junit.JUnitTask
+junitreport=org.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator
+native2ascii=org.apache.tools.ant.taskdefs.optional.Native2Ascii
+netrexxc=org.apache.tools.ant.taskdefs.optional.NetRexxC
+p4add=org.apache.tools.ant.taskdefs.optional.perforce.P4Add
+p4change=org.apache.tools.ant.taskdefs.optional.perforce.P4Change
+p4counter=org.apache.tools.ant.taskdefs.optional.perforce.P4Counter
+p4delete=org.apache.tools.ant.taskdefs.optional.perforce.P4Delete
+p4edit=org.apache.tools.ant.taskdefs.optional.perforce.P4Edit
+p4fstat=org.apache.tools.ant.taskdefs.optional.perforce.P4Fstat
+p4have=org.apache.tools.ant.taskdefs.optional.perforce.P4Have
+p4integrate=org.apache.tools.ant.taskdefs.optional.perforce.P4Integrate
+p4label=org.apache.tools.ant.taskdefs.optional.perforce.P4Label
+p4labelsync=org.apache.tools.ant.taskdefs.optional.perforce.P4Labelsync
+p4reopen=org.apache.tools.ant.taskdefs.optional.perforce.P4Reopen
+p4resolve=org.apache.tools.ant.taskdefs.optional.perforce.P4Resolve
+p4revert=org.apache.tools.ant.taskdefs.optional.perforce.P4Revert
+p4submit=org.apache.tools.ant.taskdefs.optional.perforce.P4Submit
+p4sync=org.apache.tools.ant.taskdefs.optional.perforce.P4Sync
+propertyfile=org.apache.tools.ant.taskdefs.optional.PropertyFile
+pvcs=org.apache.tools.ant.taskdefs.optional.pvcs.Pvcs
+replaceregexp=org.apache.tools.ant.taskdefs.optional.ReplaceRegExp
+rexec=org.apache.tools.ant.taskdefs.optional.net.RExecTask
+rpm=org.apache.tools.ant.taskdefs.optional.Rpm
+schemavalidate=org.apache.tools.ant.taskdefs.optional.SchemaValidate
+scp=org.apache.tools.ant.taskdefs.optional.ssh.Scp
+script=org.apache.tools.ant.taskdefs.optional.Script
+scriptdef=org.apache.tools.ant.taskdefs.optional.script.ScriptDef
+serverdeploy=org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+setproxy=org.apache.tools.ant.taskdefs.optional.net.SetProxy
+soscheckin=org.apache.tools.ant.taskdefs.optional.sos.SOSCheckin
+soscheckout=org.apache.tools.ant.taskdefs.optional.sos.SOSCheckout
+sosget=org.apache.tools.ant.taskdefs.optional.sos.SOSGet
+soslabel=org.apache.tools.ant.taskdefs.optional.sos.SOSLabel
+sound=org.apache.tools.ant.taskdefs.optional.sound.SoundTask
+splash=org.apache.tools.ant.taskdefs.optional.splash.SplashTask
+sshexec=org.apache.tools.ant.taskdefs.optional.ssh.SSHExec
+sshsession=org.apache.tools.ant.taskdefs.optional.ssh.SSHSession
+stcheckin=org.apache.tools.ant.taskdefs.optional.starteam.StarTeamCheckin
+stcheckout=org.apache.tools.ant.taskdefs.optional.starteam.StarTeamCheckout
+stlabel=org.apache.tools.ant.taskdefs.optional.starteam.StarTeamLabel
+stlist=org.apache.tools.ant.taskdefs.optional.starteam.StarTeamList
+symlink=org.apache.tools.ant.taskdefs.optional.unix.Symlink
+telnet=org.apache.tools.ant.taskdefs.optional.net.TelnetTask
+translate=org.apache.tools.ant.taskdefs.optional.i18n.Translate
+verifyjar=org.apache.tools.ant.taskdefs.VerifyJar
+vssadd=org.apache.tools.ant.taskdefs.optional.vss.MSVSSADD
+vsscheckin=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCHECKIN
+vsscheckout=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCHECKOUT
+vsscp=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCP
+vsscreate=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCREATE
+vssget=org.apache.tools.ant.taskdefs.optional.vss.MSVSSGET
+vsshistory=org.apache.tools.ant.taskdefs.optional.vss.MSVSSHISTORY
+vsslabel=org.apache.tools.ant.taskdefs.optional.vss.MSVSSLABEL
+wljspc=org.apache.tools.ant.taskdefs.optional.jsp.WLJspc
+wlrun=org.apache.tools.ant.taskdefs.optional.ejb.WLRun
+wlstop=org.apache.tools.ant.taskdefs.optional.ejb.WLStop
+xmlvalidate=org.apache.tools.ant.taskdefs.optional.XMLValidateTask
+
+
+# deprecated ant tasks (kept for back compatibility)
+copydir=org.apache.tools.ant.taskdefs.Copydir
+copyfile=org.apache.tools.ant.taskdefs.Copyfile
+copypath=org.apache.tools.ant.taskdefs.CopyPath
+deltree=org.apache.tools.ant.taskdefs.Deltree
+execon=org.apache.tools.ant.taskdefs.ExecuteOn
+javadoc2=org.apache.tools.ant.taskdefs.Javadoc
+jlink=org.apache.tools.ant.taskdefs.optional.jlink.JlinkTask
+jspc=org.apache.tools.ant.taskdefs.optional.jsp.JspC
+mimemail=org.apache.tools.ant.taskdefs.optional.net.MimeMail
+rename=org.apache.tools.ant.taskdefs.Rename
+renameext=org.apache.tools.ant.taskdefs.optional.RenameExtensions
+starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut
+style=org.apache.tools.ant.taskdefs.XSLTProcess
+debug=org.apache.tools.ant.DebugTask
\ No newline at end of file