source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/types/Commandline.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 18.3 KB
Line 
1/*
2 * Copyright 2000-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.types;
19
20import java.io.File;
21import java.util.StringTokenizer;
22import java.util.Vector;
23import java.util.ArrayList;
24import java.util.List;
25import java.util.ListIterator;
26import java.util.LinkedList;
27
28import org.apache.tools.ant.BuildException;
29import org.apache.tools.ant.ProjectComponent;
30import org.apache.tools.ant.util.StringUtils;
31
32
33/**
34 * Commandline objects help handling command lines specifying processes to
35 * execute.
36 *
37 * The class can be used to define a command line as nested elements or as a
38 * helper to define a command line by an application.
39 * <p>
40 * <code>
41 * &lt;someelement&gt;<br>
42 * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
43 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
44 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
45 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
46 * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
47 * &lt;/someelement&gt;<br>
48 * </code>
49 * The element <code>someelement</code> must provide a method
50 * <code>createAcommandline</code> which returns an instance of this class.
51 *
52 */
53public class Commandline implements Cloneable {
54
55 /**
56 * The arguments of the command
57 */
58 private Vector arguments = new Vector();
59
60 /**
61 * the program to execute
62 */
63 private String executable = null;
64
65 protected static final String DISCLAIMER =
66 StringUtils.LINE_SEP
67 + "The \' characters around the executable and arguments are"
68 + StringUtils.LINE_SEP
69 + "not part of the command."
70 + StringUtils.LINE_SEP;
71
72 /**
73 * create a command line from a string
74 * @param toProcess the line: the first element becomes the executable, the rest
75 * the arguments
76 */
77 public Commandline(String toProcess) {
78 super();
79 String[] tmp = translateCommandline(toProcess);
80 if (tmp != null && tmp.length > 0) {
81 setExecutable(tmp[0]);
82 for (int i = 1; i < tmp.length; i++) {
83 createArgument().setValue(tmp[i]);
84 }
85 }
86 }
87
88 /**
89 * Create an empty command line
90 */
91 public Commandline() {
92 super();
93 }
94
95 /**
96 * Used for nested xml command line definitions.
97 */
98 public static class Argument extends ProjectComponent {
99
100 private String[] parts;
101
102 /**
103 * Sets a single commandline argument.
104 *
105 * @param value a single commandline argument.
106 */
107 public void setValue(String value) {
108 parts = new String[] {value};
109 }
110
111 /**
112 * Line to split into several commandline arguments.
113 *
114 * @param line line to split into several commandline arguments
115 */
116 public void setLine(String line) {
117 if (line == null) {
118 return;
119 }
120 parts = translateCommandline(line);
121 }
122
123 /**
124 * Sets a single commandline argument and treats it like a
125 * PATH - ensures the right separator for the local platform
126 * is used.
127 *
128 * @param value a single commandline argument.
129 */
130 public void setPath(Path value) {
131 parts = new String[] {value.toString()};
132 }
133
134 /**
135 * Sets a single commandline argument from a reference to a
136 * path - ensures the right separator for the local platform
137 * is used.
138 *
139 * @param value a single commandline argument.
140 */
141 public void setPathref(Reference value) {
142 Path p = new Path(getProject());
143 p.setRefid(value);
144 parts = new String[] {p.toString()};
145 }
146
147 /**
148 * Sets a single commandline argument to the absolute filename
149 * of the given file.
150 *
151 * @param value a single commandline argument.
152 */
153 public void setFile(File value) {
154 parts = new String[] {value.getAbsolutePath()};
155 }
156
157 /**
158 * Returns the parts this Argument consists of.
159 */
160 public String[] getParts() {
161 return parts;
162 }
163 }
164
165 /**
166 * Class to keep track of the position of an Argument.
167 <p>This class is there to support the srcfile and targetfile
168 elements of &lt;execon&gt; and &lt;transform&gt; - don't know
169 whether there might be additional use cases.</p> --SB
170 */
171 public class Marker {
172
173 private int position;
174 private int realPos = -1;
175
176 Marker(int position) {
177 this.position = position;
178 }
179
180 /**
181 * Return the number of arguments that preceeded this marker.
182 *
183 * <p>The name of the executable - if set - is counted as the
184 * very first argument.</p>
185 */
186 public int getPosition() {
187 if (realPos == -1) {
188 realPos = (executable == null ? 0 : 1);
189 for (int i = 0; i < position; i++) {
190 Argument arg = (Argument) arguments.elementAt(i);
191 realPos += arg.getParts().length;
192 }
193 }
194 return realPos;
195 }
196 }
197
198 /**
199 * Creates an argument object.
200 *
201 * <p>Each commandline object has at most one instance of the
202 * argument class. This method calls
203 * <code>this.createArgument(false)</code>.</p>
204 *
205 * @see #createArgument(boolean)
206 * @return the argument object.
207 */
208 public Argument createArgument() {
209 return this.createArgument(false);
210 }
211
212 /**
213 * Creates an argument object and adds it to our list of args.
214 *
215 * <p>Each commandline object has at most one instance of the
216 * argument class.</p>
217 *
218 * @param insertAtStart if true, the argument is inserted at the
219 * beginning of the list of args, otherwise it is appended.
220 */
221 public Argument createArgument(boolean insertAtStart) {
222 Argument argument = new Argument();
223 if (insertAtStart) {
224 arguments.insertElementAt(argument, 0);
225 } else {
226 arguments.addElement(argument);
227 }
228 return argument;
229 }
230
231 /**
232 * Sets the executable to run. All file separators in the string
233 * are converted to the platform specific value
234 */
235 public void setExecutable(String executable) {
236 if (executable == null || executable.length() == 0) {
237 return;
238 }
239 this.executable = executable.replace('/', File.separatorChar)
240 .replace('\\', File.separatorChar);
241 }
242
243
244 /**
245 * get the executable
246 * @return the program to run -null if not yet set
247 */
248 public String getExecutable() {
249 return executable;
250 }
251
252
253 /**
254 * append the arguments to the existing command
255 * @param line an array of arguments to append
256 */
257 public void addArguments(String[] line) {
258 for (int i = 0; i < line.length; i++) {
259 createArgument().setValue(line[i]);
260 }
261 }
262
263 /**
264 * Returns the executable and all defined arguments.
265 */
266 public String[] getCommandline() {
267 List commands = new LinkedList();
268 ListIterator list = commands.listIterator();
269 addCommandToList(list);
270 final String[] result = new String[commands.size()];
271 return (String[]) commands.toArray(result);
272 }
273
274 /**
275 * add the entire command, including (optional) executable to a list
276 * @param list
277 * @since Ant 1.6
278 */
279 public void addCommandToList(ListIterator list) {
280 if (executable != null) {
281 list.add(executable);
282 }
283 addArgumentsToList(list);
284 }
285
286
287 /**
288 * Returns all arguments defined by <code>addLine</code>,
289 * <code>addValue</code> or the argument object.
290 */
291 public String[] getArguments() {
292 List result = new ArrayList(arguments.size() * 2);
293 addArgumentsToList(result.listIterator());
294 String [] res = new String[result.size()];
295 return (String[]) result.toArray(res);
296 }
297
298 /**
299 * append all the arguments to the tail of a supplied list
300 * @param list
301 * @since Ant 1.6
302 */
303 public void addArgumentsToList(ListIterator list) {
304 for (int i = 0; i < arguments.size(); i++) {
305 Argument arg = (Argument) arguments.elementAt(i);
306 String[] s = arg.getParts();
307 if (s != null) {
308 for (int j = 0; j < s.length; j++) {
309 list.add(s[j]);
310 }
311 }
312 }
313 }
314
315
316 /**
317 * stringify operator returns the command line as a string
318 * @return the command line
319 */
320 public String toString() {
321 return toString(getCommandline());
322 }
323
324 /**
325 * Put quotes around the given String if necessary.
326 *
327 * <p>If the argument doesn't include spaces or quotes, return it
328 * as is. If it contains double quotes, use single quotes - else
329 * surround the argument by double quotes.</p>
330 *
331 * @exception BuildException if the argument contains both, single
332 * and double quotes.
333 */
334 public static String quoteArgument(String argument) {
335 if (argument.indexOf("\"") > -1) {
336 if (argument.indexOf("\'") > -1) {
337 throw new BuildException("Can\'t handle single and double"
338 + " quotes in same argument");
339 } else {
340 return '\'' + argument + '\'';
341 }
342 } else if (argument.indexOf("\'") > -1 || argument.indexOf(" ") > -1) {
343 return '\"' + argument + '\"';
344 } else {
345 return argument;
346 }
347 }
348
349 /**
350 * Quotes the parts of the given array in way that makes them
351 * usable as command line arguments.
352 * @return empty string for null or no command, else every argument split
353 * by spaces and quoted by quoting rules
354 */
355 public static String toString(String [] line) {
356 // empty path return empty string
357 if (line == null || line.length == 0) {
358 return "";
359 }
360
361 // path containing one or more elements
362 final StringBuffer result = new StringBuffer();
363 for (int i = 0; i < line.length; i++) {
364 if (i > 0) {
365 result.append(' ');
366 }
367 result.append(quoteArgument(line[i]));
368 }
369 return result.toString();
370 }
371
372 /**
373 * crack a command line
374 * @param toProcess the command line to process
375 * @return the command line broken into strings.
376 * An empty or null toProcess parameter results in a zero sized array
377 */
378 public static String[] translateCommandline(String toProcess) {
379 if (toProcess == null || toProcess.length() == 0) {
380 //no command? no string
381 return new String[0];
382 }
383
384 // parse with a simple finite state machine
385
386 final int normal = 0;
387 final int inQuote = 1;
388 final int inDoubleQuote = 2;
389 int state = normal;
390 StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
391 Vector v = new Vector();
392 StringBuffer current = new StringBuffer();
393 boolean lastTokenHasBeenQuoted = false;
394
395 while (tok.hasMoreTokens()) {
396 String nextTok = tok.nextToken();
397 switch (state) {
398 case inQuote:
399 if ("\'".equals(nextTok)) {
400 lastTokenHasBeenQuoted = true;
401 state = normal;
402 } else {
403 current.append(nextTok);
404 }
405 break;
406 case inDoubleQuote:
407 if ("\"".equals(nextTok)) {
408 lastTokenHasBeenQuoted = true;
409 state = normal;
410 } else {
411 current.append(nextTok);
412 }
413 break;
414 default:
415 if ("\'".equals(nextTok)) {
416 state = inQuote;
417 } else if ("\"".equals(nextTok)) {
418 state = inDoubleQuote;
419 } else if (" ".equals(nextTok)) {
420 if (lastTokenHasBeenQuoted || current.length() != 0) {
421 v.addElement(current.toString());
422 current = new StringBuffer();
423 }
424 } else {
425 current.append(nextTok);
426 }
427 lastTokenHasBeenQuoted = false;
428 break;
429 }
430 }
431
432 if (lastTokenHasBeenQuoted || current.length() != 0) {
433 v.addElement(current.toString());
434 }
435
436 if (state == inQuote || state == inDoubleQuote) {
437 throw new BuildException("unbalanced quotes in " + toProcess);
438 }
439
440 String[] args = new String[v.size()];
441 v.copyInto(args);
442 return args;
443 }
444
445 /**
446 * size operator. This actually creates the command line, so it is not
447 * a zero cost operation.
448 * @return number of elements in the command, including the executable
449 */
450 public int size() {
451 return getCommandline().length;
452 }
453
454 /**
455 * Generate a deep clone of the contained object.
456 * @return a clone of the contained object
457 */
458 public Object clone() {
459 try {
460 Commandline c = (Commandline) super.clone();
461 c.arguments = (Vector) arguments.clone();
462 return c;
463 } catch (CloneNotSupportedException e) {
464 throw new BuildException(e);
465 }
466 }
467
468 /**
469 * Clear out the whole command line. */
470 public void clear() {
471 executable = null;
472 arguments.removeAllElements();
473 }
474
475 /**
476 * Clear out the arguments but leave the executable in place for
477 * another operation.
478 */
479 public void clearArgs() {
480 arguments.removeAllElements();
481 }
482
483 /**
484 * Return a marker.
485 *
486 * <p>This marker can be used to locate a position on the
487 * commandline - to insert something for example - when all
488 * parameters have been set.</p>
489 */
490 public Marker createMarker() {
491 return new Marker(arguments.size());
492 }
493
494 /**
495 * Returns a String that describes the command and arguments
496 * suitable for verbose output before a call to
497 * <code>Runtime.exec(String[])<code>
498 *
499 * @since Ant 1.5
500 */
501 public String describeCommand() {
502 return describeCommand(this);
503 }
504
505 /**
506 * Returns a String that describes the arguments suitable for
507 * verbose output before a call to
508 * <code>Runtime.exec(String[])<code>
509 *
510 * @since Ant 1.5
511 */
512 public String describeArguments() {
513 return describeArguments(this);
514 }
515
516 /**
517 * Returns a String that describes the command and arguments
518 * suitable for verbose output before a call to
519 * <code>Runtime.exec(String[])<code>
520 *
521 * @since Ant 1.5
522 */
523 public static String describeCommand(Commandline line) {
524 return describeCommand(line.getCommandline());
525 }
526
527 /**
528 * Returns a String that describes the arguments suitable for
529 * verbose output before a call to
530 * <code>Runtime.exec(String[])<code>
531 *
532 * @since Ant 1.5
533 */
534 public static String describeArguments(Commandline line) {
535 return describeArguments(line.getArguments());
536 }
537
538 /**
539 * Returns a String that describes the command and arguments
540 * suitable for verbose output before a call to
541 * <code>Runtime.exec(String[])<code>.
542 *
543 * <p>This method assumes that the first entry in the array is the
544 * executable to run.</p>
545 *
546 * @since Ant 1.5
547 */
548 public static String describeCommand(String[] args) {
549 if (args == null || args.length == 0) {
550 return "";
551 }
552
553 StringBuffer buf = new StringBuffer("Executing \'");
554 buf.append(args[0]);
555 buf.append("\'");
556 if (args.length > 0) {
557 buf.append(" with ");
558 buf.append(describeArguments(args, 1));
559 } else {
560 buf.append(DISCLAIMER);
561 }
562 return buf.toString();
563 }
564
565 /**
566 * Returns a String that describes the arguments suitable for
567 * verbose output before a call to
568 * <code>Runtime.exec(String[])<code>
569 *
570 * @since Ant 1.5
571 */
572 public static String describeArguments(String[] args) {
573 return describeArguments(args, 0);
574 }
575
576 /**
577 * Returns a String that describes the arguments suitable for
578 * verbose output before a call to
579 * <code>Runtime.exec(String[])<code>
580 *
581 * @param offset ignore entries before this index
582 *
583 * @since Ant 1.5
584 */
585 protected static String describeArguments(String[] args, int offset) {
586 if (args == null || args.length <= offset) {
587 return "";
588 }
589
590 StringBuffer buf = new StringBuffer("argument");
591 if (args.length > offset) {
592 buf.append("s");
593 }
594 buf.append(":").append(StringUtils.LINE_SEP);
595 for (int i = offset; i < args.length; i++) {
596 buf.append("\'").append(args[i]).append("\'")
597 .append(StringUtils.LINE_SEP);
598 }
599 buf.append(DISCLAIMER);
600 return buf.toString();
601 }
602}
Note: See TracBrowser for help on using the repository browser.