You are viewing a plain text version of this content. The canonical link for it is here.
Posted to easyant-commits@incubator.apache.org by kp...@apache.org on 2011/06/07 06:30:57 UTC

svn commit: r1132876 [1/3] - in /incubator/easyant/tasks/trunk/command-line-debugger/src/main/org: ./ apache/ apache/tools/ apache/tools/ant/ apache/tools/ant/helper/ apache/tools/ant/taskdefs/

Author: kpsiddharth
Date: Tue Jun  7 06:30:57 2011
New Revision: 1132876

URL: http://svn.apache.org/viewvc?rev=1132876&view=rev
Log:
Sources for implementing PoC for debugger

Added:
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/DebugTask.java   (with props)
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/Main.java   (with props)
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/Project.java   (with props)
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/helper/
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/helper/ProjectHelper2.java   (with props)
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/taskdefs/
    incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/taskdefs/defaults.properties   (with props)

Added: incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/DebugTask.java
URL: http://svn.apache.org/viewvc/incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/DebugTask.java?rev=1132876&view=auto
==============================================================================
--- incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/DebugTask.java (added)
+++ incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/DebugTask.java Tue Jun  7 06:30:57 2011
@@ -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");
+		}
+	}
+}

Propchange: incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/DebugTask.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/Main.java
URL: http://svn.apache.org/viewvc/incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/Main.java?rev=1132876&view=auto
==============================================================================
--- incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/Main.java (added)
+++ incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/Main.java Tue Jun  7 06:30:57 2011
@@ -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);
+    }
+}

Propchange: incubator/easyant/tasks/trunk/command-line-debugger/src/main/org/apache/tools/ant/Main.java
------------------------------------------------------------------------------
    svn:eol-style = native