KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > types > Commandline


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant.types;
20
21 import java.io.File JavaDoc;
22 import java.util.StringTokenizer JavaDoc;
23 import java.util.Vector JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.ListIterator JavaDoc;
27 import java.util.LinkedList JavaDoc;
28 import java.util.Iterator JavaDoc;
29
30 import org.apache.tools.ant.BuildException;
31 import org.apache.tools.ant.ProjectComponent;
32 import org.apache.tools.ant.util.StringUtils;
33 import org.apache.tools.ant.taskdefs.condition.Os;
34
35 /**
36  * Commandline objects help handling command lines specifying processes to
37  * execute.
38  *
39  * The class can be used to define a command line as nested elements or as a
40  * helper to define a command line by an application.
41  * <p>
42  * <code>
43  * &lt;someelement&gt;<br>
44  * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
45  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
46  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
47  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
48  * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
49  * &lt;/someelement&gt;<br>
50  * </code>
51  * The element <code>someelement</code> must provide a method
52  * <code>createAcommandline</code> which returns an instance of this class.
53  *
54  */

55 public class Commandline implements Cloneable JavaDoc {
56     /** win9x uses a (shudder) bat file (antRun.bat) for executing commands */
57     private static final boolean IS_WIN_9X = Os.isFamily("win9x");
58
59     /**
60      * The arguments of the command
61      */

62     private Vector JavaDoc arguments = new Vector JavaDoc();
63
64     /**
65      * the program to execute
66      */

67     private String JavaDoc executable = null;
68
69     protected static final String JavaDoc DISCLAIMER =
70         StringUtils.LINE_SEP
71         + "The \' characters around the executable and arguments are"
72         + StringUtils.LINE_SEP
73         + "not part of the command."
74         + StringUtils.LINE_SEP;
75
76     /**
77      * Create a command line from a string.
78      * @param toProcess the line: the first element becomes the executable, the rest
79      * the arguments.
80      */

81     public Commandline(String JavaDoc toProcess) {
82         super();
83         String JavaDoc[] tmp = translateCommandline(toProcess);
84         if (tmp != null && tmp.length > 0) {
85             setExecutable(tmp[0]);
86             for (int i = 1; i < tmp.length; i++) {
87                 createArgument().setValue(tmp[i]);
88             }
89         }
90     }
91
92     /**
93      * Create an empty command line.
94      */

95     public Commandline() {
96         super();
97     }
98
99     /**
100      * Used for nested xml command line definitions.
101      */

102     public static class Argument extends ProjectComponent {
103
104         private String JavaDoc[] parts;
105
106         /**
107          * Set a single commandline argument.
108          *
109          * @param value a single commandline argument.
110          */

111         public void setValue(String JavaDoc value) {
112             parts = new String JavaDoc[] {value};
113         }
114
115         /**
116          * Set the line to split into several commandline arguments.
117          *
118          * @param line line to split into several commandline arguments.
119          */

120         public void setLine(String JavaDoc line) {
121             if (line == null) {
122                 return;
123             }
124             parts = translateCommandline(line);
125         }
126
127         /**
128          * Set a single commandline argument and treats it like a
129          * PATH--ensuring the right separator for the local platform
130          * is used.
131          *
132          * @param value a single commandline argument.
133          */

134         public void setPath(Path value) {
135             parts = new String JavaDoc[] {value.toString()};
136         }
137
138         /**
139          * Set a single commandline argument from a reference to a
140          * path--ensuring the right separator for the local platform
141          * is used.
142          *
143          * @param value a single commandline argument.
144          */

145         public void setPathref(Reference value) {
146             Path p = new Path(getProject());
147             p.setRefid(value);
148             parts = new String JavaDoc[] {p.toString()};
149         }
150
151         /**
152          * Set a single commandline argument to the absolute filename
153          * of the given file.
154          *
155          * @param value a single commandline argument.
156          */

157         public void setFile(File JavaDoc value) {
158             parts = new String JavaDoc[] {value.getAbsolutePath()};
159         }
160
161         /**
162          * Return the constituent parts of this Argument.
163          * @return an array of strings.
164          */

165         public String JavaDoc[] getParts() {
166             return parts;
167         }
168     }
169
170     /**
171      * Class to keep track of the position of an Argument.
172      <p>This class is there to support the srcfile and targetfile
173      elements of &lt;execon&gt; and &lt;transform&gt; - don't know
174      whether there might be additional use cases.</p> --SB
175      */

176     public class Marker {
177
178         private int position;
179         private int realPos = -1;
180
181         /**
182          * Construct a marker for the specified position.
183          * @param position the position to mark.
184          */

185         Marker(int position) {
186             this.position = position;
187         }
188
189         /**
190          * Return the number of arguments that preceded this marker.
191          *
192          * <p>The name of the executable -- if set -- is counted as the
193          * first argument.</p>
194          * @return the position of this marker.
195          */

196         public int getPosition() {
197             if (realPos == -1) {
198                 realPos = (executable == null ? 0 : 1);
199                 for (int i = 0; i < position; i++) {
200                     Argument arg = (Argument) arguments.elementAt(i);
201                     realPos += arg.getParts().length;
202                 }
203             }
204             return realPos;
205         }
206     }
207
208     /**
209      * Create an argument object.
210      *
211      * <p>Each commandline object has at most one instance of the
212      * argument class. This method calls
213      * <code>this.createArgument(false)</code>.</p>
214      *
215      * @see #createArgument(boolean)
216      * @return the argument object.
217      */

218     public Argument createArgument() {
219         return this.createArgument(false);
220     }
221
222     /**
223      * Create an argument object and add it to our list of args.
224      *
225      * <p>Each commandline object has at most one instance of the
226      * argument class.</p>
227      *
228      * @param insertAtStart if true, the argument is inserted at the
229      * beginning of the list of args, otherwise it is appended.
230      * @return an argument to be configured
231      */

232     public Argument createArgument(boolean insertAtStart) {
233         Argument argument = new Argument();
234         if (insertAtStart) {
235             arguments.insertElementAt(argument, 0);
236         } else {
237             arguments.addElement(argument);
238         }
239         return argument;
240     }
241
242     /**
243      * Set the executable to run. All file separators in the string
244      * are converted to the platform specific value.
245      * @param executable the String executable name.
246      */

247     public void setExecutable(String JavaDoc executable) {
248         if (executable == null || executable.length() == 0) {
249             return;
250         }
251         this.executable = executable.replace('/', File.separatorChar)
252             .replace('\\', File.separatorChar);
253     }
254
255     /**
256      * Get the executable.
257      * @return the program to run--null if not yet set.
258      */

259     public String JavaDoc getExecutable() {
260         return executable;
261     }
262
263     /**
264      * Append the arguments to the existing command.
265      * @param line an array of arguments to append.
266      */

267     public void addArguments(String JavaDoc[] line) {
268         for (int i = 0; i < line.length; i++) {
269             createArgument().setValue(line[i]);
270         }
271     }
272
273     /**
274      * Return the executable and all defined arguments.
275      * @return the commandline as an array of strings.
276      */

277     public String JavaDoc[] getCommandline() {
278         List JavaDoc commands = new LinkedList JavaDoc();
279         ListIterator JavaDoc list = commands.listIterator();
280         addCommandToList(list);
281         final String JavaDoc[] result = new String JavaDoc[commands.size()];
282         return (String JavaDoc[]) commands.toArray(result);
283     }
284
285     /**
286      * Add the entire command, including (optional) executable to a list.
287      * @param list the list to add to.
288      * @since Ant 1.6
289      */

290     public void addCommandToList(ListIterator JavaDoc list) {
291         if (executable != null) {
292             list.add(executable);
293         }
294         addArgumentsToList(list);
295     }
296
297     /**
298      * Returns all arguments defined by <code>addLine</code>,
299      * <code>addValue</code> or the argument object.
300      * @return the arguments as an array of strings.
301      */

302     public String JavaDoc[] getArguments() {
303         List JavaDoc result = new ArrayList JavaDoc(arguments.size() * 2);
304         addArgumentsToList(result.listIterator());
305         String JavaDoc [] res = new String JavaDoc[result.size()];
306         return (String JavaDoc[]) result.toArray(res);
307     }
308
309     /**
310      * Append all the arguments to the tail of a supplied list.
311      * @param list the list of arguments.
312      * @since Ant 1.6
313      */

314     public void addArgumentsToList(ListIterator JavaDoc list) {
315         for (int i = 0; i < arguments.size(); i++) {
316             Argument arg = (Argument) arguments.elementAt(i);
317             String JavaDoc[] s = arg.getParts();
318             if (s != null) {
319                 for (int j = 0; j < s.length; j++) {
320                     list.add(s[j]);
321                 }
322             }
323         }
324     }
325
326     /**
327      * Return the command line as a string.
328      * @return the command line.
329      */

330     public String JavaDoc toString() {
331         return toString(getCommandline());
332     }
333
334     /**
335      * Put quotes around the given String if necessary.
336      *
337      * <p>If the argument doesn't include spaces or quotes, return it
338      * as is. If it contains double quotes, use single quotes - else
339      * surround the argument by double quotes.</p>
340      * @param argument the argument to quote if necessary.
341      * @return the quoted argument.
342      * @exception BuildException if the argument contains both, single
343      * and double quotes.
344      */

345     public static String JavaDoc quoteArgument(String JavaDoc argument) {
346         if (argument.indexOf("\"") > -1) {
347             if (argument.indexOf("\'") > -1) {
348                 throw new BuildException("Can\'t handle single and double"
349                         + " quotes in same argument");
350             } else {
351                 return '\'' + argument + '\'';
352             }
353         } else if (argument.indexOf("\'") > -1
354                    || argument.indexOf(" ") > -1
355                    // WIN9x uses a bat file for executing commands
356
|| (IS_WIN_9X && argument.indexOf(';') != -1)) {
357             return '\"' + argument + '\"';
358         } else {
359             return argument;
360         }
361     }
362
363     /**
364      * Quote the parts of the given array in way that makes them
365      * usable as command line arguments.
366      * @param line the list of arguments to quote.
367      * @return empty string for null or no command, else every argument split
368      * by spaces and quoted by quoting rules.
369      */

370     public static String JavaDoc toString(String JavaDoc[] line) {
371         // empty path return empty string
372
if (line == null || line.length == 0) {
373             return "";
374         }
375         // path containing one or more elements
376
final StringBuffer JavaDoc result = new StringBuffer JavaDoc();
377         for (int i = 0; i < line.length; i++) {
378             if (i > 0) {
379                 result.append(' ');
380             }
381             result.append(quoteArgument(line[i]));
382         }
383         return result.toString();
384     }
385
386     /**
387      * Crack a command line.
388      * @param toProcess the command line to process.
389      * @return the command line broken into strings.
390      * An empty or null toProcess parameter results in a zero sized array.
391      */

392     public static String JavaDoc[] translateCommandline(String JavaDoc toProcess) {
393         if (toProcess == null || toProcess.length() == 0) {
394             //no command? no string
395
return new String JavaDoc[0];
396         }
397         // parse with a simple finite state machine
398

399         final int normal = 0;
400         final int inQuote = 1;
401         final int inDoubleQuote = 2;
402         int state = normal;
403         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(toProcess, "\"\' ", true);
404         Vector JavaDoc v = new Vector JavaDoc();
405         StringBuffer JavaDoc current = new StringBuffer JavaDoc();
406         boolean lastTokenHasBeenQuoted = false;
407
408         while (tok.hasMoreTokens()) {
409             String JavaDoc nextTok = tok.nextToken();
410             switch (state) {
411             case inQuote:
412                 if ("\'".equals(nextTok)) {
413                     lastTokenHasBeenQuoted = true;
414                     state = normal;
415                 } else {
416                     current.append(nextTok);
417                 }
418                 break;
419             case inDoubleQuote:
420                 if ("\"".equals(nextTok)) {
421                     lastTokenHasBeenQuoted = true;
422                     state = normal;
423                 } else {
424                     current.append(nextTok);
425                 }
426                 break;
427             default:
428                 if ("\'".equals(nextTok)) {
429                     state = inQuote;
430                 } else if ("\"".equals(nextTok)) {
431                     state = inDoubleQuote;
432                 } else if (" ".equals(nextTok)) {
433                     if (lastTokenHasBeenQuoted || current.length() != 0) {
434                         v.addElement(current.toString());
435                         current = new StringBuffer JavaDoc();
436                     }
437                 } else {
438                     current.append(nextTok);
439                 }
440                 lastTokenHasBeenQuoted = false;
441                 break;
442             }
443         }
444         if (lastTokenHasBeenQuoted || current.length() != 0) {
445             v.addElement(current.toString());
446         }
447         if (state == inQuote || state == inDoubleQuote) {
448             throw new BuildException("unbalanced quotes in " + toProcess);
449         }
450         String JavaDoc[] args = new String JavaDoc[v.size()];
451         v.copyInto(args);
452         return args;
453     }
454
455     /**
456      * Size operator. This actually creates the command line, so it is not
457      * a zero cost operation.
458      * @return number of elements in the command, including the executable.
459      */

460     public int size() {
461         return getCommandline().length;
462     }
463
464     /**
465      * Generate a deep clone of the contained object.
466      * @return a clone of the contained object
467      */

468     public Object JavaDoc clone() {
469         try {
470             Commandline c = (Commandline) super.clone();
471             c.arguments = (Vector JavaDoc) arguments.clone();
472             return c;
473         } catch (CloneNotSupportedException JavaDoc e) {
474             throw new BuildException(e);
475         }
476     }
477
478     /**
479      * Clear out the whole command line.
480      */

481     public void clear() {
482         executable = null;
483         arguments.removeAllElements();
484     }
485
486     /**
487      * Clear out the arguments but leave the executable in place for
488      * another operation.
489      */

490     public void clearArgs() {
491         arguments.removeAllElements();
492     }
493
494     /**
495      * Return a marker.
496      *
497      * <p>This marker can be used to locate a position on the
498      * commandline--to insert something for example--when all
499      * parameters have been set.</p>
500      * @return a marker
501      */

502     public Marker createMarker() {
503         return new Marker(arguments.size());
504     }
505
506     /**
507      * Return a String that describes the command and arguments suitable for
508      * verbose output before a call to <code>Runtime.exec(String[])<code>.
509      * @return a string that describes the command and arguments.
510      * @since Ant 1.5
511      */

512     public String JavaDoc describeCommand() {
513         return describeCommand(this);
514     }
515
516     /**
517      * Return a String that describes the arguments suitable for
518      * verbose output before a call to <code>Runtime.exec(String[])<code>.
519      * @return a string that describes the arguments.
520      * @since Ant 1.5
521      */

522     public String JavaDoc describeArguments() {
523         return describeArguments(this);
524     }
525
526     /**
527      * Return a String that describes the command and arguments suitable for
528      * verbose output before a call to <code>Runtime.exec(String[])<code>.
529      * @param line the Commandline to describe.
530      * @return a string that describes the command and arguments.
531      * @since Ant 1.5
532      */

533     public static String JavaDoc describeCommand(Commandline line) {
534         return describeCommand(line.getCommandline());
535     }
536
537     /**
538      * Return a String that describes the arguments suitable for
539      * verbose output before a call to <code>Runtime.exec(String[])<code>.
540      * @param line the Commandline whose arguments to describe.
541      * @return a string that describes the arguments.
542      * @since Ant 1.5
543      */

544     public static String JavaDoc describeArguments(Commandline line) {
545         return describeArguments(line.getArguments());
546     }
547
548     /**
549      * Return a String that describes the command and arguments suitable for
550      * verbose output before a call to <code>Runtime.exec(String[])<code>.
551      *
552      * <p>This method assumes that the first entry in the array is the
553      * executable to run.</p>
554      * @param args the command line to describe as an array of strings
555      * @return a string that describes the command and arguments.
556      * @since Ant 1.5
557      */

558     public static String JavaDoc describeCommand(String JavaDoc[] args) {
559         if (args == null || args.length == 0) {
560             return "";
561         }
562         StringBuffer JavaDoc buf = new StringBuffer JavaDoc("Executing \'");
563         buf.append(args[0]);
564         buf.append("\'");
565         if (args.length > 1) {
566             buf.append(" with ");
567             buf.append(describeArguments(args, 1));
568         } else {
569             buf.append(DISCLAIMER);
570         }
571         return buf.toString();
572     }
573
574     /**
575      * Return a String that describes the arguments suitable for
576      * verbose output before a call to <code>Runtime.exec(String[])<code>.
577      * @param args the command line to describe as an array of strings.
578      * @return a string that describes the arguments.
579      * @since Ant 1.5
580      */

581     public static String JavaDoc describeArguments(String JavaDoc[] args) {
582         return describeArguments(args, 0);
583     }
584
585     /**
586      * Return a String that describes the arguments suitable for
587      * verbose output before a call to <code>Runtime.exec(String[])<code>.
588      *
589      * @param args the command line to describe as an array of strings.
590      * @param offset ignore entries before this index.
591      * @return a string that describes the arguments
592      *
593      * @since Ant 1.5
594      */

595     protected static String JavaDoc describeArguments(String JavaDoc[] args, int offset) {
596         if (args == null || args.length <= offset) {
597             return "";
598         }
599         StringBuffer JavaDoc buf = new StringBuffer JavaDoc("argument");
600         if (args.length > offset) {
601             buf.append("s");
602         }
603         buf.append(":").append(StringUtils.LINE_SEP);
604         for (int i = offset; i < args.length; i++) {
605             buf.append("\'").append(args[i]).append("\'")
606                 .append(StringUtils.LINE_SEP);
607         }
608         buf.append(DISCLAIMER);
609         return buf.toString();
610     }
611
612     /**
613      * Get an iterator to the arguments list.
614      * @since Ant 1.7
615      * @return an Iterator.
616      */

617     public Iterator JavaDoc iterator() {
618         return arguments.iterator();
619     }
620 }
621
Popular Tags