Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/ant/internal/ui/antsupport/InternalAntRunner.java


1   /*******************************************************************************
2    * Copyright (c) 2000, 2005 IBM Corporation and others.
3    * Portions Copyright  2000-2005 The Apache Software Foundation
4    * All rights reserved.  This program and the accompanying materials are made 
5    * available under the terms of the Apache Software License v2.0 which 
6    * accompanies this distribution and is available at 
7    * http://www.apache.org/licenses/LICENSE-2.0.
8    * 
9    * Contributors:
10   *     IBM Corporation - derived implementation
11   *******************************************************************************/
12  
13  package org.eclipse.ant.internal.ui.antsupport;
14  
15  import java.io.File;
16  import java.io.FileInputStream;
17  import java.io.FileNotFoundException;
18  import java.io.FileOutputStream;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.PrintStream;
22  import java.text.MessageFormat;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Enumeration;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Properties;
31  import java.util.Vector;
32  
33  import org.apache.tools.ant.AntTypeDefinition;
34  import org.apache.tools.ant.BuildEvent;
35  import org.apache.tools.ant.BuildException;
36  import org.apache.tools.ant.BuildListener;
37  import org.apache.tools.ant.BuildLogger;
38  import org.apache.tools.ant.ComponentHelper;
39  import org.apache.tools.ant.DefaultLogger;
40  import org.apache.tools.ant.DemuxOutputStream;
41  import org.apache.tools.ant.Diagnostics;
42  import org.apache.tools.ant.Main;
43  import org.apache.tools.ant.Project;
44  import org.apache.tools.ant.ProjectHelper;
45  import org.apache.tools.ant.Target;
46  import org.apache.tools.ant.Task;
47  import org.apache.tools.ant.TaskAdapter;
48  import org.apache.tools.ant.util.FileUtils;
49  
50  /**
51   * Eclipse application entry point into Ant in a separate VM. Derived from the original Ant Main class
52   * to ensure that the functionality is equivalent when running in the platform.
53   */
54  public class InternalAntRunner {
55  
56    /**
57     *  Message priority for project help messages. 
58     */
59    public static final int MSG_PROJECT_HELP= Project.MSG_DEBUG + 1;
60    
61    private List buildListeners;
62  
63    private String buildFileLocation;
64  
65    /** 
66     * Targets we want to run.
67     */
68    private Vector targets;
69  
70    private Map userProperties;
71    
72    private Project currentProject;
73    
74    private String defaultTarget;
75    
76    private BuildLogger buildLogger= null;
77    
78    private Map eclipseSpecifiedTasks;
79    private Map eclipseSpecifiedTypes;
80    
81    /**
82     * Cache of the Ant version number when it has been loaded
83     */
84    private String antVersionNumber= null;
85  
86    /** Our current message output status. Follows Project.MSG_XXX */
87    private int messageOutputLevel = Project.MSG_INFO;
88  
89    /** Indicates whether output to the log is to be unadorned. */
90    private boolean emacsMode = false;
91  
92    /** Indicates we should only parse and display the project help information */
93    private boolean projectHelp = false;
94  
95    /** Stream that we are using for logging */
96    private PrintStream out = System.out;
97  
98    /** Stream that we are using for logging error messages */
99    private PrintStream err = System.err;
100 
101   /**
102    * The Ant logger class. There may be only one logger. It will have the
103    * right to use the 'out' PrintStream. The class must implement the BuildLogger
104    * interface.  An empty String indicates that no logger is to be used.  A <code>null</code>
105    * name indicates that the org.apache.tools.ant.DefaultLogger will be used.
106    */
107   private String loggerClassname = null;
108 
109   /** Extra arguments to be parsed as command line arguments. */
110   private String[] extraArguments = null;
111   
112   private boolean scriptExecuted= false;
113   
114   private List propertyFiles= new ArrayList();
115   
116   /**
117      * The Ant InputHandler class. There may be only one input handler.
118      */
119     private String inputHandlerClassname = null;
120     
121     /** 
122      * Indicates whether to execute all targets that 
123      * do not depend on failed targets
124      * @since Ant 1.6.0
125      */
126     private boolean keepGoing= false;
127 
128     /** 
129      * Indicates whether this build is to support interactive input 
130      * @since Ant 1.6.0
131      */
132     private boolean allowInput = true;
133     
134     public static void main(String[] args) {
135       try {
136         new InternalAntRunner().run(getArrayList(args));
137       } catch (Throwable t) {
138           t.printStackTrace();
139         System.exit(1);
140       }
141     System.exit(0);
142   }
143 
144   private void addBuildListeners(Project project) {
145     String className= null;
146     try {
147       BuildLogger logger= createLogger();
148       if (logger != null) {
149         project.addBuildListener(logger);
150       }
151       if (buildListeners != null) {
152         for (Iterator iterator = buildListeners.iterator(); iterator.hasNext();) {
153           className = (String) iterator.next();
154           Class listener = Class.forName(className);
155           project.addBuildListener((BuildListener) listener.newInstance());
156         }
157       }
158     } catch (ClassCastException e) {
159       String message = MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.{0}_which_was_specified_to_be_a_build_listener_is_not_an_instance_of_org.apache.tools.ant.BuildListener._1"), new String[]{className}); //$NON-NLS-1$
160       logMessage(null, message, Project.MSG_ERR);
161       throw new BuildException(message, e);
162     } catch (BuildException e) {
163       throw e;
164     } catch (Exception e) {
165       throw new BuildException(e);
166     }
167   }
168 
169   /**
170    * Parses the build file and adds necessary information into
171    * the given project.
172    * @param project The project to configure
173    */
174   private void parseBuildFile(Project project) {
175     File buildFile = new File(getBuildFileLocation());
176     if (!buildFile.exists()) {
177       throw new BuildException(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Buildfile__{0}_does_not_exist_!_1"), //$NON-NLS-1$
178              new String[]{buildFile.getAbsolutePath()}));
179     }
180     if (!buildFile.isFile()) {
181       throw new BuildException(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Buildfile__{0}_is_not_a_file_1"), //$NON-NLS-1$
182               new String[]{buildFile.getAbsolutePath()}));
183     }
184     
185     ProjectHelper helper = ProjectHelper.getProjectHelper();
186     project.addReference("ant.projectHelper", helper); //$NON-NLS-1$
187     helper.parse(project, buildFile);
188   }
189 
190   private void printArguments(Project project) {
191     if ((messageOutputLevel != Project.MSG_DEBUG) && (messageOutputLevel != Project.MSG_VERBOSE)) {
192       return;
193     }
194     StringBuffer sb = new StringBuffer();
195     for (int i = 0; i < extraArguments.length; i++) {
196       sb.append(extraArguments[i]);
197       sb.append(' ');
198     }
199     project.log(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Arguments__{0}_2"), new String[]{sb.toString().trim()})); //$NON-NLS-1$
200   }
201 
202 
203   /**
204    * Logs a message with the client that lists the targets
205    * in a project
206    * 
207    * @param project the project to list targets from
208    */
209   private void printTargets(Project project) {
210     //notify the logger that project help message are coming
211     //since there is no buildstarted or targetstarted to 
212     //to be used to establish the connection
213     logMessage(project, "", MSG_PROJECT_HELP); //$NON-NLS-1$
214     // find the target with the longest name
215     int maxLength = 0;
216     Enumeration ptargets = project.getTargets().elements();
217     String targetName;
218     String targetDescription;
219     Target currentTarget;
220     // split the targets in top-level and sub-targets depending
221     // on the presence of a description
222     List topNames = new ArrayList();
223     List topDescriptions = new ArrayList();
224     List subNames = new ArrayList();
225 
226     while (ptargets.hasMoreElements()) {
227       currentTarget = (Target) ptargets.nextElement();
228       targetName = currentTarget.getName();
229       targetDescription = currentTarget.getDescription();
230       if (targetDescription == null) {
231         subNames.add(targetName);
232       } else {
233         topNames.add(targetName);
234         topDescriptions.add(targetDescription);
235         if (targetName.length() > maxLength) {
236           maxLength = targetName.length();
237         }
238       }
239     }
240 
241     Collections.sort(subNames);
242     Collections.sort(topNames);
243     Collections.sort(topDescriptions);
244     
245     String defaultTargetName = project.getDefaultTarget();
246     if (defaultTargetName != null && !"".equals(defaultTargetName)) { // shouldn't need to check but... //$NON-NLS-1$
247       List defaultName = new ArrayList(1);
248       List defaultDesc = null;
249       defaultName.add(defaultTargetName);
250 
251       int indexOfDefDesc = topNames.indexOf(defaultTargetName);
252       if (indexOfDefDesc >= 0) {
253         defaultDesc = new ArrayList(1);
254         defaultDesc.add(topDescriptions.get(indexOfDefDesc));
255       }
256       printTargets(project, defaultName, defaultDesc, InternalAntMessages.getString("InternalAntRunner.Default_target__3"), maxLength); //$NON-NLS-1$
257 
258     }
259 
260     printTargets(project, topNames, topDescriptions, InternalAntMessages.getString("InternalAntRunner.Main_targets__4"), maxLength); //$NON-NLS-1$
261     printTargets(project, subNames, null, InternalAntMessages.getString("InternalAntRunner.Subtargets__5"), 0); //$NON-NLS-1$
262   }
263 
264   /**
265    * Logs a message with the client that lists the target names and optional descriptions
266    * 
267    * @param project the enclosing target
268    * @param names the targets names
269    * @param descriptions the corresponding descriptions
270    * @param heading the message heading
271    * @param maxlen maximum length that can be allocated for a name
272    */
273   private void printTargets(Project project, List names, List descriptions, String heading, int maxlen) {
274     // now, start printing the targets and their descriptions
275     String lSep = System.getProperty("line.separator"); //$NON-NLS-1$
276     
277     String spaces = "    "; //$NON-NLS-1$
278     while (spaces.length() < maxlen) {
279       spaces += spaces;
280     }
281     StringBuffer msg = new StringBuffer();
282     msg.append(heading + lSep + lSep);
283     for (int i = 0; i < names.size(); i++) {
284       msg.append(' ');
285       msg.append(names.get(i));
286       if (descriptions != null) {
287         msg.append(spaces.substring(0, maxlen - ((String) names.get(i)).length() + 2));
288         msg.append(descriptions.get(i));
289       }
290       msg.append(lSep);
291     }
292     logMessage(project, msg.toString(), Project.MSG_INFO);
293   }
294 
295   /*
296    * Note that the list passed to this method must support
297    * List#remove(Object)
298    */
299   private void run(List argList) {
300     setCurrentProject(new Project());
301     Throwable error = null;
302     PrintStream originalErr = System.err;
303     PrintStream originalOut = System.out;
304     InputStream originalIn= System.in;
305     
306     SecurityManager originalSM= System.getSecurityManager();
307     scriptExecuted= true;
308     try {
309       if (argList != null && (argList.remove("-projecthelp") || argList.remove("-p"))) { //$NON-NLS-1$ //$NON-NLS-2$
310         projectHelp = true;
311       }
312       getCurrentProject().init();
313       if (argList != null) {
314         scriptExecuted= preprocessCommandLine(argList);
315       
316         if (!scriptExecuted) {
317           return;
318         }
319       }
320       
321       addBuildListeners(getCurrentProject());
322     
323       processProperties(argList);
324       
325       setProperties(getCurrentProject());
326       
327       addInputHandler(getCurrentProject());
328       
329       remapSystemIn();
330       System.setOut(new PrintStream(new DemuxOutputStream(getCurrentProject(), false)));
331       System.setErr(new PrintStream(new DemuxOutputStream(getCurrentProject(), true)));
332       
333       if (!projectHelp) {
334         fireBuildStarted(getCurrentProject());
335       }
336       
337       if (argList != null && !argList.isEmpty()) {
338         try {
339           scriptExecuted= processCommandLine(argList);
340         } catch (BuildException e) {
341           scriptExecuted= false;
342           throw e;
343         }
344       }
345       if (!scriptExecuted) {
346         return;
347       }
348       
349       //we do not want to set the default input stream for the default input handler
350       //as we currently have no means for querying from System.in for input.
351       //see bug 45484
352       if (allowInput && inputHandlerClassname != null) {
353         if (isVersionCompatible("1.6")) { //$NON-NLS-1$
354           getCurrentProject().setDefaultInputStream(originalIn);
355         }
356       }
357       
358       getCurrentProject().log(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Build_file__{0}_1"), new String[]{getBuildFileLocation()})); //$NON-NLS-1$
359       
360       setTasks();
361       setTypes();
362       
363       if (isVersionCompatible("1.6")) { //$NON-NLS-1$
364         getCurrentProject().setKeepGoingMode(keepGoing);
365       }
366       
367       parseBuildFile(getCurrentProject());
368       validateDefaultTarget();
369       
370       if (projectHelp) {
371         printHelp(getCurrentProject());
372         scriptExecuted= false;
373         return;
374       }
375       
376       if (extraArguments != null) {
377         printArguments(getCurrentProject());
378       }
379       
380       System.setSecurityManager(new AntSecurityManager(originalSM));
381       
382       if (targets != null && !targets.isEmpty()) {
383         getCurrentProject().executeTargets(targets);
384       } else {
385         getCurrentProject().executeTarget(getCurrentProject().getDefaultTarget());
386       }
387     } catch (AntSecurityException e) {
388       //expected
389     } catch (Throwable e) {
390       error = e;
391     } finally {
392       System.setErr(originalErr);
393       System.setOut(originalOut);
394       System.setIn(originalIn);
395       if (System.getSecurityManager() instanceof AntSecurityManager) {
396         System.setSecurityManager(originalSM);
397       }
398       
399       if (!projectHelp) {        
400         fireBuildFinished(getCurrentProject(), error);
401       }
402             
403       //close any user specified build log
404       if (err != originalErr) {
405         err.close();
406       }
407       if (out != originalOut) {
408         out.close();
409       }
410     }
411   }
412   
413   private void setTasks() {
414     if (eclipseSpecifiedTasks != null) {
415       Iterator itr= eclipseSpecifiedTasks.keySet().iterator();
416       String taskName;
417       String taskClassName;
418       while (itr.hasNext()) {
419         taskName= (String) itr.next();
420         taskClassName= (String) eclipseSpecifiedTasks.get(taskName);
421         
422         if (isVersionCompatible("1.6")) { //$NON-NLS-1$
423           AntTypeDefinition def= new AntTypeDefinition();
424           def.setName(taskName);
425                 def.setClassName(taskClassName);
426                 def.setClassLoader(this.getClass().getClassLoader());
427                 def.setAdaptToClass(Task.class);
428                 def.setAdapterClass(TaskAdapter.class);
429                 ComponentHelper.getComponentHelper(getCurrentProject()).addDataTypeDefinition(def);
430         } else {
431           try {
432             Class taskClass = Class.forName(taskClassName);
433             getCurrentProject().addTaskDefinition(taskName, taskClass);
434           } catch (ClassNotFoundException e) {
435             String message= MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.161"), new String[]{taskClassName, taskName}); //$NON-NLS-1$
436             getCurrentProject().log(message, Project.MSG_WARN);
437           }  
438         }
439       }
440     }
441   }
442 
443   private void setTypes() {
444     if (eclipseSpecifiedTypes != null) {
445       Iterator itr= eclipseSpecifiedTypes.keySet().iterator();
446       String typeName;
447       String typeClassName;
448       while (itr.hasNext()) {
449         typeName = (String) itr.next();
450         typeClassName= (String) eclipseSpecifiedTypes.get(typeName);
451         if (isVersionCompatible("1.6")) { //$NON-NLS-1$
452           AntTypeDefinition def = new AntTypeDefinition();
453                   def.setName(typeName);
454                   def.setClassName(typeClassName);
455                   def.setClassLoader(this.getClass().getClassLoader());
456                   ComponentHelper.getComponentHelper(getCurrentProject()).addDataTypeDefinition(def);
457         } else {
458           try {
459             Class typeClass = Class.forName(typeClassName);
460             getCurrentProject().addDataTypeDefinition(typeName, typeClass);
461           } catch (ClassNotFoundException e) {
462             String message= MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.162"), new String[]{typeClassName, typeName}); //$NON-NLS-1$
463             getCurrentProject().log(message, Project.MSG_WARN);
464           }  
465         }
466       }
467     }
468   }
469 
470   private void remapSystemIn() {
471     if (!isVersionCompatible("1.6")) { //$NON-NLS-1$
472       return;
473     }
474     DemuxInputStreamSetter setter= new DemuxInputStreamSetter();
475     setter.remapSystemIn(getCurrentProject());
476   }
477   
478   private void validateDefaultTarget() {
479     defaultTarget = getCurrentProject().getDefaultTarget();
480     
481     Enumeration currentTargets = getCurrentProject().getTargets().elements();
482     boolean defaultFound= false;
483     while (currentTargets.hasMoreElements()) {
484       Target target = (Target) currentTargets.nextElement();
485       if (target.getName().equals(defaultTarget)) {
486         defaultFound= true;
487         break;
488       }
489     }
490     
491     if (!defaultFound) {
492       //default target must exist
493       throw new BuildException(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Default_target_{0}{1}{2}_does_not_exist_in_this_project_1"), new String[]{"'", defaultTarget, "'"})); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
494     }
495   }
496 
497   /**
498    * Creates and returns the default build logger for logging build events to the ant log.
499    * 
500    * @return the default build logger for logging build events to the ant log
501    *       can return <code>null</code> if no logging is to occur
502    */
503   private BuildLogger createLogger() {
504     if (loggerClassname == null) {
505       buildLogger= new DefaultLogger();
506     } else if (!"".equals(loggerClassname)) { //$NON-NLS-1$
507       try {
508         buildLogger = (BuildLogger) (Class.forName(loggerClassname).newInstance());
509       } catch (ClassCastException e) {
510         String message = MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.{0}_which_was_specified_to_perform_logging_is_not_an_instance_of_org.apache.tools.ant.BuildLogger._2"), new String[]{loggerClassname}); //$NON-NLS-1$
511         logMessage(null, message, Project.MSG_ERR);
512         throw new BuildException(message, e);
513       } catch (Exception e) {
514         String message = MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Unable_to_instantiate_logger__{0}_6"), new String[]{loggerClassname}); //$NON-NLS-1$
515         logMessage(null, message, Project.MSG_ERR);
516         throw new BuildException(message, e);
517       }
518     } 
519     
520     if (buildLogger != null) {
521       buildLogger.setMessageOutputLevel(messageOutputLevel);
522       buildLogger.setOutputPrintStream(out);
523       buildLogger.setErrorPrintStream(err);
524       buildLogger.setEmacsMode(emacsMode);
525     }
526 
527     return buildLogger;
528   }
529 
530   /*
531    * We only have to do this because Project.fireBuildStarted is protected. If it becomes
532    * public we should remove this method and call the appropriate one.
533    */
534   private void fireBuildStarted(Project project) {
535     BuildEvent event = new BuildEvent(project);
536     for (Iterator iterator = project.getBuildListeners().iterator(); iterator.hasNext();) {
537       BuildListener listener = (BuildListener) iterator.next();
538       listener.buildStarted(event);
539     }
540   }
541 
542   private void fireBuildFinished(Project project, Throwable error) {
543     if (error == null && scriptExecuted) {
544       logMessage(project, InternalAntMessages.getString("InternalAntRunner.BUILD_SUCCESSFUL_1"), messageOutputLevel); //$NON-NLS-1$
545     }
546     project.fireBuildFinished(error);
547   }
548 
549   private void logMessage(Project project, String message, int priority) {
550     if (project != null) {
551       project.log(message, priority);  
552     } else {
553       if (buildListeners != null) {
554         project = new Project();
555         BuildEvent event = new BuildEvent(project);
556         event.setMessage(message, priority);
557         //notify the build listeners that are not registered as
558         //no project existed
559         for (Iterator iterator = buildListeners.iterator(); iterator.hasNext();) {
560           try {
561             BuildListener listener = (BuildListener) iterator.next();
562             listener.messageLogged(event);
563           } catch (ClassCastException e) {
564             //ignore we could be trying to log that a build listener is the
565             //wrong type of class
566           }
567         }
568       }
569     }
570   }
571 
572   /**
573    * Sets the buildFileLocation.
574    * 
575    * @param buildFileLocation the file system location of the build file
576    */
577   private void setBuildFileLocation(String buildFileLocation) {
578     this.buildFileLocation = buildFileLocation;
579     if (getCurrentProject() != null) {
580       getCurrentProject().setUserProperty("ant.file", buildFileLocation); //$NON-NLS-1$
581     }
582   }
583 
584   private String getBuildFileLocation() {
585     if (buildFileLocation == null) {
586       buildFileLocation = new File("build.xml").getAbsolutePath(); //$NON-NLS-1$
587     }
588     return buildFileLocation;
589   }
590 
591   /**
592    * Sets the message output level. Use -1 for none.
593    * @param level The message output level
594    */
595   private void setMessageOutputLevel(int level) {
596     messageOutputLevel = level;
597     if (buildLogger != null) {
598       buildLogger.setMessageOutputLevel(level);
599     }
600   }
601   
602   /*
603    * Returns a String representation of the Ant version number as specified
604    * in the version.txt file.
605    */
606   private String getAntVersionNumber() throws BuildException {
607     if (antVersionNumber == null) {
608       try {
609         Properties props = new Properties();
610         InputStream in = Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt"); //$NON-NLS-1$
611         props.load(in);
612         in.close();
613         String versionNumber= props.getProperty("VERSION");  //$NON-NLS-1$
614         antVersionNumber= versionNumber;
615       } catch (IOException ioe) {
616         throw new BuildException(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Could_not_load_the_version_information._{0}_9"), new String[]{ioe.getMessage()})); //$NON-NLS-1$
617       } catch (NullPointerException npe) {
618         throw new BuildException(InternalAntMessages.getString("InternalAntRunner.Could_not_load_the_version_information._10")); //$NON-NLS-1$
619       }
620     }
621     return antVersionNumber;
622   }
623   
624   /*
625    * Returns whether the given version is compatible with the
626    * current Ant version. A version is compatible if it is less
627    * than or equal to the current version. 
628    */
629   private boolean isVersionCompatible(String comparison) {
630     String version= getAntVersionNumber();
631     return version.compareTo(comparison) >= 0;
632   }
633   
634   private boolean preprocessCommandLine(List commands) {
635     
636     String arg = getArgument(commands, "-listener"); //$NON-NLS-1$
637     while (arg != null) {
638       if (arg.length() == 0) {
639         throw new BuildException(InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_classname_when_using_the_-listener_argument_1")); //$NON-NLS-1$
640       } 
641       if (buildListeners == null) {
642         buildListeners= new ArrayList(1);
643       }
644       buildListeners.add(arg);
645       arg = getArgument(commands, "-listener"); //$NON-NLS-1$
646     }
647 
648     arg = getArgument(commands, "-logger"); //$NON-NLS-1$
649     if (arg != null) {
650       if (arg.length() == 0) {
651         throw new BuildException(InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_classname_when_using_the_-logger_argument_2")); //$NON-NLS-1$
652       } 
653       loggerClassname = arg;
654     }
655     arg = getArgument(commands, "-logger"); //$NON-NLS-1$
656     if (arg != null) {
657       throw new BuildException(InternalAntMessages.getString("InternalAntRunner.Only_one_logger_class_may_be_specified_1")); //$NON-NLS-1$
658     }
659     
660     arg = getArgument(commands, "-inputhandler"); //$NON-NLS-1$
661     if (arg != null) {
662       if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
663         throw new BuildException(InternalAntMessages.getString("InternalAntRunner.Specifying_an_InputHandler_is_an_Ant_1.5.*_feature._Please_update_your_Ant_classpath_to_include_an_Ant_version_greater_than_this._2")); //$NON-NLS-1$
664       }
665       if (arg.length() == 0) {
666         throw new BuildException(InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_classname_when_using_the_-inputhandler_argument_1")); //$NON-NLS-1$
667       } 
668       inputHandlerClassname = arg;
669     }
670     arg = getArgument(commands, "-inputhandler"); //$NON-NLS-1$
671     if (arg != null) {
672       throw new BuildException(InternalAntMessages.getString("InternalAntRunner.Only_one_input_handler_class_may_be_specified._2")); //$NON-NLS-1$
673     }
674     return true;
675   }
676   
677   /*
678    * Looks for interesting command line arguments. 
679    * Returns whether it is OK to run the script.
680    */
681   private boolean processCommandLine(List commands) {
682     
683     if (commands.remove("-help") || commands.remove("-h")) { //$NON-NLS-1$ //$NON-NLS-2$
684       printUsage();
685       return false;
686     }
687     
688     if (commands.remove("-version")) { //$NON-NLS-1$
689       printVersion();
690       return false;
691     }
692     
693     if (commands.remove("-verbose") || commands.remove("-v")) { //$NON-NLS-1$ //$NON-NLS-2$
694       printVersion();
695       setMessageOutputLevel(Project.MSG_VERBOSE);
696     }
697     
698     if (commands.remove("-debug") || commands.remove("-d")) { //$NON-NLS-1$ //$NON-NLS-2$
699       printVersion();
700       setMessageOutputLevel(Project.MSG_DEBUG);
701     }
702     
703     if (commands.remove("-quiet") || commands.remove("-q")) { //$NON-NLS-1$ //$NON-NLS-2$
704       setMessageOutputLevel(Project.MSG_WARN);
705     }
706 
707     if (commands.remove("-emacs") || commands.remove("-e")) { //$NON-NLS-1$ //$NON-NLS-2$
708       emacsMode = true;
709       if (buildLogger != null) {
710         buildLogger.setEmacsMode(true);
711       }
712     }
713     
714     if (commands.remove("-diagnostics")) { //$NON-NLS-1$
715       if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
716         throw new BuildException(InternalAntMessages.getString("InternalAntRunner.The_diagnositics_options_is_an_Ant_1.5.*_feature._Please_update_your_Ant_classpath_to_include_an_Ant_version_greater_than_this._4")); //$NON-NLS-1$
717       }
718       try {
719         Diagnostics.doReport(System.out);
720       } catch (NullPointerException e) {
721         logMessage(getCurrentProject(), InternalAntMessages.getString("InternalAntRunner.ANT_HOME_must_be_set_to_use_Ant_diagnostics_2"), Project.MSG_ERR); //$NON-NLS-1$
722       }
723       return false;
724     }
725     
726     String arg = getArgument(commands, "-logfile"); //$NON-NLS-1$
727     if (arg == null) {
728       arg = getArgument(commands, "-l"); //$NON-NLS-1$
729     }
730     if (arg != null) {
731       if (arg.length() == 0) {
732         String message= InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_log_file_when_using_the_-log_argument_3"); //$NON-NLS-1$
733         logMessage(currentProject, message, Project.MSG_ERR); 
734         throw new BuildException(message);
735       } 
736       try {
737         createLogFile(arg);
738       } catch (IOException e) {
739         // just log message and ignore exception
740         logMessage(getCurrentProject(), MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Could_not_write_to_the_specified_log_file__{0}._Make_sure_the_path_exists_and_you_have_write_permissions._2"), new String[]{arg}), Project.MSG_ERR); //$NON-NLS-1$
741         return false;
742       }
743     
744     }
745     
746     arg = getArgument(commands, "-buildfile"); //$NON-NLS-1$
747     if (arg == null) {
748       arg = getArgument(commands, "-file"); //$NON-NLS-1$
749       if (arg == null) {
750         arg = getArgument(commands, "-f"); //$NON-NLS-1$
751       }
752     }
753     
754     if (arg != null) {
755       if (arg.length() == 0) {
756         String message= InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_buildfile_when_using_the_-buildfile_argument_4"); //$NON-NLS-1$
757         logMessage(currentProject, message, Project.MSG_ERR); 
758         throw new BuildException(message);
759       } 
760       setBuildFileLocation(arg);
761     }
762     
763     if (isVersionCompatible("1.6")) { //$NON-NLS-1$
764       if (commands.remove("-k") || commands.remove("-keep-going")) { //$NON-NLS-1$ //$NON-NLS-2$
765         keepGoing= true;
766       }
767       if (commands.remove("-noinput")) { //$NON-NLS-1$
768         allowInput= false;
769       }
770       arg= getArgument(commands, "-lib"); //$NON-NLS-1$
771       if (arg != null) {
772         logMessage(currentProject, InternalAntMessages.getString("InternalAntRunner.157"), Project.MSG_ERR); //$NON-NLS-1$
773         return false;
774       }
775     }
776     
777     arg= getArgument(commands, "-find"); //$NON-NLS-1$
778     if (arg == null) {
779       arg= getArgument(commands, "-s"); //$NON-NLS-1$
780     }
781     if (arg != null) {
782       logMessage(currentProject, InternalAntMessages.getString("InternalAntRunner.-find_not_supported"), Project.MSG_ERR); //$NON-NLS-1$
783       return false;
784     }
785     
786     processTasksAndTypes(commands);
787     
788     if ((commands != null) && (!commands.isEmpty())) {
789       processUnrecognizedCommands(commands);
790     }
791 
792     if ((commands != null) && (!commands.isEmpty())) {
793       processTargets(commands);
794     }
795     
796     return true;
797   }
798   
799   private void processTasksAndTypes(List commands) {
800     String arg = getArgument(commands, "-eclipseTask"); //$NON-NLS-1$
801     while (arg != null) {
802       if (eclipseSpecifiedTasks == null) {
803         eclipseSpecifiedTasks= new HashMap();
804       }
805       int index= arg.indexOf(',');
806       if (index != -1) {
807         String name= arg.substring(0, index);
808         String className= arg.substring(index + 1);
809         eclipseSpecifiedTasks.put(name, className);
810       }
811       arg = getArgument(commands, "-eclipseTask"); //$NON-NLS-1$
812     }
813     
814     arg = getArgument(commands, "-eclipseType"); //$NON-NLS-1$
815     while (arg != null) {
816       if (eclipseSpecifiedTypes == null) {
817         eclipseSpecifiedTypes= new HashMap();
818       }
819       int index= arg.indexOf(',');
820       if (index != -1) {  
821         String name= arg.substring(0, index);
822         String className= arg.substring(index + 1);
823         eclipseSpecifiedTypes.put(name, className);
824       }
825       arg = getArgument(commands, "-eclipseType"); //$NON-NLS-1$
826     }
827   }
828 
829   /*
830    * Checks for unrecognized arguments on the command line.
831    * Since there is no syntactic way to distingush between
832    * ant -foo target1 target2
833    * ant -foo fooarg target
834    * we remove everything up to the last argument that
835    * begins with a '-'.  In the latter case, above, that
836    * means that there will be an extra target, 'fooarg',
837    * left lying around.
838    */
839   private void processUnrecognizedCommands(List commands) {
840     int p = -1;
841 
842     // find the last arg that begins with '-'
843     for (int i = commands.size() - 1; i >= 0; i--) {
844       if (((String) commands.get(0)).startsWith("-")) { //$NON-NLS-1$
845         p = i;
846         break;
847       }
848     }
849     if (p < 0) { return; }
850 
851     // remove everything preceding that last '-arg'
852     String s = ""; //$NON-NLS-1$
853     for (int i = 0; i <= p; i++) {
854       s += " " + ((String) commands.get(0)); //$NON-NLS-1$
855       commands.remove(0);
856     }
857     
858     // warn of ignored commands
859     String message = MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Unknown_argument__{0}_2"), new Object[]{ s.substring(1) }); //$NON-NLS-1$
860     logMessage(currentProject, message, Project.MSG_WARN); 
861   }
862   
863 
864   /*
865    * Checks for targets specified at the command line.
866    */
867   private void processTargets(List commands) {
868     if (targets == null) {
869       targets = new Vector(commands.size());
870     }
871     for (Iterator iter = commands.iterator(); iter.hasNext();) {
872       targets.add(iter.next());
873     }
874   }
875 
876   /*
877    * Creates the log file with the name specified by the user.
878    * If the fileName is not absolute, the file will be created in the
879    * working directory if specified or in the same directory as the location
880    * of the build file.
881    */
882   private void createLogFile(String fileName) throws FileNotFoundException, IOException {
883     File logFile = getFileRelativeToBaseDir(fileName);
884     
885     //this stream is closed in the finally block of run(list)
886     out = new PrintStream(new FileOutputStream(logFile));
887     err = out;
888     logMessage(getCurrentProject(), MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Using_{0}_file_as_build_log._1"), new String[]{logFile.getCanonicalPath()}), Project.MSG_INFO); //$NON-NLS-1$
889     if (buildLogger != null) {
890       buildLogger.setErrorPrintStream(err);
891       buildLogger.setOutputPrintStream(out);
892     }
893   }
894 
895   private File getFileRelativeToBaseDir(String fileName) {
896     File parentFile= null;
897     
898     String base= getCurrentProject().getUserProperty("basedir"); //$NON-NLS-1$
899     if (base != null) {
900       parentFile= new File(base);
901     } else {
902       //relative to the build file location
903       parentFile= new File(getBuildFileLocation()).getParentFile();
904     }
905     
906     return FileUtils.newFileUtils().resolveFile(parentFile, fileName);
907   }
908 
909   /*
910    * Processes cmd line properties and adds the user properties
911    * Any user properties that have been explicitly set are set as well.
912    * Ensures that -D properties take precedence.
913    */
914   private void processProperties(List commands) {
915     //MULTIPLE property files are allowed
916     String arg= getArgument(commands, "-propertyfile"); //$NON-NLS-1$
917     while (arg != null) {
918       if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
919         logMessage(currentProject, InternalAntMessages.getString("InternalAntRunner.Specifying_property_files_is_a_Ant_1.5.*_feature._Please_update_your_Ant_classpath._6"), Project.MSG_ERR); //$NON-NLS-1$
920         break;
921       }
922       if (arg.length() == 0) {
923         String message= InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_property_filename_when_using_the_-propertyfile_argument_3"); //$NON-NLS-1$
924         logMessage(currentProject, message, Project.MSG_ERR); 
925         throw new BuildException(message);
926       } 
927       
928       propertyFiles.add(arg);
929       arg= getArgument(commands, "-propertyfile"); //$NON-NLS-1$
930     }
931     
932     if (propertyFiles != null && !propertyFiles.isEmpty()) {
933       loadPropertyFiles();
934     }
935     
936     if (commands == null) {
937       return;
938     }
939     processMinusDProperties(commands);
940   }
941 
942   private void processMinusDProperties(List commands) {
943     String[] args = (String[]) commands.toArray(new String[commands.size()]);
944     for (int i = 0; i < args.length; i++) {
945       String arg = args[i];
946       if (arg.startsWith("-D")) { //$NON-NLS-1$
947         String name = arg.substring(2, arg.length());
948         String value = null;
949         int posEq = name.indexOf("="); //$NON-NLS-1$
950         if (posEq == 0) {
951           value= name.substring(1);
952           name= ""; //$NON-NLS-1$
953         } else if (posEq > 0 && posEq != name.length() - 1) {
954           value = name.substring(posEq + 1).trim();
955           name = name.substring(0, posEq);
956         }
957         
958         if (value == null) {
959           //the user has specified something like "-Debug"
960           continue;
961         }
962         if (userProperties == null) {
963           userProperties= new HashMap();
964         }
965         userProperties.put(name, value);
966         commands.remove(args[i]);
967       }
968     }
969   }
970   
971   private void setProperties(Project project) {
972     setBuiltInProperties(project);
973     if (userProperties != null) {
974       for (Iterator iterator = userProperties.entrySet().iterator(); iterator.hasNext();) {
975         Map.Entry entry = (Map.Entry) iterator.next();
976         project.setUserProperty((String) entry.getKey(), (String) entry.getValue());
977       }
978     } 
979   }
980 
981   private void setBuiltInProperties(Project project) {
982     project.setUserProperty("ant.file", getBuildFileLocation()); //$NON-NLS-1$
983     project.setUserProperty("ant.version", Main.getAntVersion()); //$NON-NLS-1$
984   }
985 
986   /*
987    * Print the project description, if any
988    */
989   private void printHelp(Project project) {
990     if (project.getDescription() != null) {
991       logMessage(project, project.getDescription(), Project.MSG_INFO);
992     }
993     printTargets(project);
994   }
995 
996   /*
997    * Logs a message with the client indicating the version of <b>Ant</b> that this class
998    * fronts.
999    */
1000  private void printVersion() {
1001    logMessage(getCurrentProject(), Main.getAntVersion(), Project.MSG_INFO);
1002  }
1003
1004  /*
1005   * Logs a message with the client outlining the usage of <b>Ant</b>.
1006   */
1007  private void printUsage() {
1008    String lSep = System.getProperty("line.separator"); //$NON-NLS-1$
1009    StringBuffer msg = new StringBuffer();
1010    msg.append("ant ["); //$NON-NLS-1$
1011    msg.append(InternalAntMessages.getString("InternalAntRunner.options_13")); //$NON-NLS-1$
1012    msg.append("] ["); //$NON-NLS-1$
1013    msg.append(InternalAntMessages.getString("InternalAntRunner.target_15")); //$NON-NLS-1$
1014    msg.append(" ["); //$NON-NLS-1$
1015    msg.append(InternalAntMessages.getString("InternalAntRunner.target_15")); //$NON-NLS-1$
1016    msg.append("2 ["); //$NON-NLS-1$
1017    msg.append(InternalAntMessages.getString("InternalAntRunner.target_15")); //$NON-NLS-1$
1018    msg.append("3] ...]]"); //$NON-NLS-1$
1019    msg.append(lSep);
1020    msg.append(InternalAntMessages.getString("InternalAntRunner.Options___21")); //$NON-NLS-1$
1021    msg.append(lSep);
1022    msg.append("\t-help, -h\t\t\t\t"); //$NON-NLS-1$
1023    msg.append(InternalAntMessages.getString("InternalAntRunner.print_this_message_23")); //$NON-NLS-1$
1024    msg.append(lSep);
1025    msg.append("\t-projecthelp, -p\t\t"); //$NON-NLS-1$
1026    msg.append(InternalAntMessages.getString("InternalAntRunner.print_project_help_information_25")); //$NON-NLS-1$
1027    msg.append(lSep);
1028    msg.append("\t-version\t\t\t\t"); //$NON-NLS-1$
1029    msg.append(InternalAntMessages.getString("InternalAntRunner.print_the_version_information_and_exit_27")); //$NON-NLS-1$
1030    msg.append(lSep); 
1031    msg.append("\t-diagnostics\t\t\t"); //$NON-NLS-1$
1032    msg.append(InternalAntMessages.getString("InternalAntRunner.12")); //$NON-NLS-1$
1033    msg.append(lSep);
1034    msg.append(InternalAntMessages.getString("InternalAntRunner.13")); //$NON-NLS-1$
1035    msg.append(lSep);
1036    msg.append("\t-quiet, -q\t\t\t"); //$NON-NLS-1$
1037    msg.append(InternalAntMessages.getString("InternalAntRunner.be_extra_quiet_29")); //$NON-NLS-1$
1038    msg.append(lSep);
1039    msg.append("\t-verbose, -v\t\t\t"); //$NON-NLS-1$
1040    msg.append(InternalAntMessages.getString("InternalAntRunner.be_extra_verbose_31")); //$NON-NLS-1$
1041    msg.append(lSep);
1042    msg.append("\t-debug, -d\t\t\t"); //$NON-NLS-1$
1043    msg.append(InternalAntMessages.getString("InternalAntRunner.print_debugging_information_33")); //$NON-NLS-1$
1044    msg.append(lSep);
1045    msg.append("\t-emacs, -e\t\t\t"); //$NON-NLS-1$
1046    msg.append(InternalAntMessages.getString("InternalAntRunner.produce_logging_information_without_adornments_35")); //$NON-NLS-1$
1047    msg.append(lSep);
1048    msg.append("\t-logfile\t<file>\t\t"); //$NON-NLS-1$
1049    msg.append(InternalAntMessages.getString("InternalAntRunner.use_given_file_for_log_37")); //$NON-NLS-1$
1050    msg.append(lSep);
1051    msg.append("\t\t-l\t<file>"); //$NON-NLS-1$
1052    msg.append(InternalAntMessages.getString("InternalAntRunner.1")); //$NON-NLS-1$ //$NON-NLS-2$
1053    msg.append(lSep);  
1054    msg.append("\t-logger <classname>\t\t"); //$NON-NLS-1$
1055    msg.append(InternalAntMessages.getString("InternalAntRunner.the_class_which_is_to_perform_logging_39")); //$NON-NLS-1$
1056    msg.append(lSep);  
1057    msg.append("\t-listener <classname>\t"); //$NON-NLS-1$
1058    msg.append(InternalAntMessages.getString("InternalAntRunner.add_an_instance_of_class_as_a_project_listener_41")); //$NON-NLS-1$
1059    msg.append(lSep);
1060    msg.append("\t-noinput\t"); //$NON-NLS-1$
1061    msg.append(InternalAntMessages.getString("InternalAntRunner.158")); //$NON-NLS-1$
1062    msg.append(lSep); 
1063    msg.append("\t-buildfile\t<file>\t"); //$NON-NLS-1$
1064    msg.append(InternalAntMessages.getString("InternalAntRunner.use_given_buildfile_43")); //$NON-NLS-1$
1065    msg.append(lSep); 
1066    msg.append("\t\t-file\t<file>"); //$NON-NLS-1$
1067    msg.append(InternalAntMessages.getString("InternalAntRunner.1")); //$NON-NLS-1$
1068    msg.append(lSep);
1069    msg.append("\t\t-f\t\t<file>"); //$NON-NLS-1$
1070    msg.append(InternalAntMessages.getString("InternalAntRunner.1")); //$NON-NLS-1$
1071    msg.append(lSep);
1072    msg.append("\t-D<property>=<value>\t"); //$NON-NLS-1$
1073    msg.append(InternalAntMessages.getString("InternalAntRunner.use_value_for_given_property_45")); //$NON-NLS-1$
1074    msg.append(lSep);
1075    msg.append("\t-keep-going, -k"); //$NON-NLS-1$
1076    msg.append(InternalAntMessages.getString("InternalAntRunner.159")); //$NON-NLS-1$
1077    msg.append(lSep);
1078    msg.append(InternalAntMessages.getString("InternalAntRunner.160")); //$NON-NLS-1$
1079    msg.append(lSep); 
1080    msg.append("\t-propertyfile <name>\t"); //$NON-NLS-1$
1081    msg.append(InternalAntMessages.getString("InternalAntRunner.19")); //$NON-NLS-1$
1082    msg.append(lSep);
1083    msg.append(InternalAntMessages.getString("InternalAntRunner.20")); //$NON-NLS-1$
1084    msg.append(lSep);
1085    msg.append("\t-inputhandler <class>\t"); //$NON-NLS-1$
1086    msg.append(InternalAntMessages.getString("InternalAntRunner.22")); //$NON-NLS-1$
1087    msg.append(lSep);
1088
1089    logMessage(getCurrentProject(), msg.toString(), Project.MSG_INFO);
1090  }
1091
1092  /*
1093   * From a command line list, return the argument for the given parameter.
1094   * The parameter and its argument are removed from the list.
1095   * 
1096   * @return <code>null</code> if the parameter is not found 
1097   *       or an empty String if no arguments are found
1098   */
1099  private String getArgument(List commands, String param) {
1100    if (commands == null) {
1101      return null;
1102    }
1103    int index = commands.indexOf(param);
1104    if (index == -1) {
1105      return null;
1106    }
1107    commands.remove(index);
1108    if (index == commands.size()) {// if this is the last command
1109      return ""; //$NON-NLS-1$
1110    }
1111    
1112    String command = (String) commands.get(index);
1113    if (command.startsWith("-")) { //new parameter //$NON-NLS-1$
1114      return ""; //$NON-NLS-1$
1115    }
1116    
1117    commands.remove(index);
1118    return command;
1119  }
1120
1121  /*
1122   * Helper method to ensure an array is converted into an ArrayList.
1123   */
1124  private static ArrayList getArrayList(String[] args) {
1125    if (args == null) {
1126      return null;
1127    }
1128    // We could be using Arrays.asList() here, but it does not specify
1129    // what kind of list it will return. We need a list that
1130    // implements the method List.remove(Object) and ArrayList does.
1131    ArrayList result = new ArrayList(args.length);
1132    for (int i = 0; i < args.length; i++) {
1133      result.add(args[i]);
1134    }
1135    return result;
1136  }
1137
1138  private Project getCurrentProject() {
1139    return currentProject;
1140  }
1141
1142  private void setCurrentProject(Project currentProject) {
1143    this.currentProject = currentProject;
1144  }
1145  
1146  /**
1147   * Load all properties from the files 
1148   * specified by -propertyfile.
1149   */
1150  private void loadPropertyFiles() {
1151    Iterator itr= propertyFiles.iterator();
1152        while (itr.hasNext()) {
1153            String filename= (String) itr.next();
1154             File file= getFileRelativeToBaseDir(filename);
1155            Properties props = new Properties();
1156            FileInputStream fis = null;
1157            try {
1158                fis = new FileInputStream(file);
1159                props.load(fis);
1160            } catch (IOException e) {
1161              String msg= MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Could_not_load_property_file_{0}__{1}_4"), new String[]{filename, e.getMessage()}); //$NON-NLS-1$
1162              logMessage(getCurrentProject(), msg, Project.MSG_ERR);
1163            } finally {
1164                if (fis != null) {
1165                    try {
1166                        fis.close();
1167                    } catch (IOException e){
1168                    }
1169                }
1170            }
1171
1172            if (userProperties == null) {
1173              userProperties= new HashMap();
1174            }
1175            Enumeration propertyNames = props.propertyNames();
1176            while (propertyNames.hasMoreElements()) {
1177                String name = (String) propertyNames.nextElement();
1178                //most specific to global
1179                //do not overwrite specific with a global property
1180                if (userProperties.get(name) == null) {
1181                userProperties.put(name, props.getProperty(name));
1182                }
1183            }
1184        }
1185  }
1186  
1187  /*
1188     * Creates the InputHandler and adds it to the project.
1189     *
1190     * @exception BuildException if a specified InputHandler
1191     *                           implementation could not be loaded.
1192     */
1193    private void addInputHandler(Project project) {
1194      if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
1195      return;
1196    }
1197    InputHandlerSetter setter= new InputHandlerSetter();
1198    setter.setInputHandler(project, inputHandlerClassname);
1199    }
1200}