KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sapia > console > CmdLine


1 package org.sapia.console;
2
3 import java.io.File JavaDoc;
4
5 import java.util.ArrayList JavaDoc;
6 import java.util.HashMap JavaDoc;
7 import java.util.List JavaDoc;
8 import java.util.Map JavaDoc;
9
10
11 /**
12  * Models a command-line. A command line is a list of arguments and options.
13  * Options consist of a name/value combination, where value is optional. Arguments
14  * have a name only. Options are delimited by a heading dash ('-'). The following
15  * is parseable as a command line:
16  * <p>
17  * <pre>
18  * start app -log debug "Hello World"
19  * </pre>
20  * <p>
21  * This command line consists of two arguments (start and app) followed by
22  * an option and its value (log/debug), and a trailing argument. Note that
23  * if the <code>log</code> option would not have had a value specified,
24  * <code>Hello World</code> would have been used as a value. In addition,
25  * enclosing <code>Hello World</code> between quotes ensures that this
26  * sentence is considered as a single argument. Without quotes, both
27  * <code>Hello</code> and <code>World</code> would have been treated as
28  * two separate arguments.
29  * <p>
30  * It is recommended that options that are not taking a mandatory value not
31  * be followed by an argument.
32  * <p>
33  * <b>EXAMPLES</b>
34  * <p>
35  * An instance of this class can be created through parsing a command-line string:
36  * <p>
37  * <pre>
38  * CmdLine cmd = CmdLine.parse("start app -log debug \"Hello World\"");
39  * </pre>
40  * <p>
41  * It can also be created from an array of strings (such as specified by
42  * <code>main</code> methods).
43  * <p>
44  * <pre>
45  * ...
46  * public static void main(String[] args){
47  * CmdLine cmd = CmdLine.fromArray(args);
48  * }
49  * ...
50  * </pre>
51  * <p>
52  * Lastly, it can also be constructed programmatically:
53  * <p>
54  * <pre>
55  * CmdLine cmd = new CmdLine().addArg("start").addArg("app").
56  * addOpt("log", "debug").addArg("\"Hello World\"");
57  * </pre>
58  * <p>
59  * Instances of this class encapsulate <code>Arg</code> and <code>Option</code>
60  * objects in a list. The list contains the objects in the order in which
61  * they were added.
62  * <p>
63  * Once a CmdLine object is created, you can start a native process with it:
64  * <pre>
65  * ExecHandle handle = cmd.exec();
66  *
67  * // you can get the internal Process instance:
68  * Process p = handle.getProcess();
69  * // do stuff with it...
70  *
71  * // or you can acquire the process' input and error streams (corresponding
72  * to that process' stderr and stdout):
73  *
74  * InputStream in = handler.getInputStream();
75  * InputStream err = handler.getErrStream();
76  * </pre>
77  * <p>
78  * <b>IMPORTANT</b>: in any case, it is important that you dispose of
79  * the ExecHandle cleanly, by calling its <code>close()</code> method, or
80  * by calling the <code>close()</code> method on any of the streams that you
81  * might have acquired from it:
82  *
83  * <pre>
84  * try{
85  * // do something with stream.
86  * }finally{
87  * in.close();
88  * }
89  * </pre>
90  *
91  *
92  *
93  *
94  *
95  *
96  *
97  *
98  *
99  * @author Yanick Duchesne
100  * 23-Dec-02
101  */

102 public class CmdLine implements Cloneable JavaDoc {
103   static final char SPACE = ' ';
104   static final char QUOTE = '\"';
105   static final char ESCAPE = '\\';
106   static final char DASH = '-';
107   private List JavaDoc _elems = new ArrayList JavaDoc();
108   private Map JavaDoc _options = new HashMap JavaDoc();
109   private int _index;
110
111   public CmdLine() {
112   }
113
114   private CmdLine(List JavaDoc elems, Map JavaDoc options) {
115     _elems = elems;
116     _options = options;
117   }
118
119   /**
120    * Adds an argument with the given name.
121    *
122    * @param name the name of the argument to add.
123    */

124   public CmdLine addArg(String JavaDoc name) {
125     return addArg(new Arg(name));
126   }
127
128   /**
129    * Adds an argument with the given name.
130    *
131    * @param index the index at which to add the argument.
132    * @param name the name of the argument to add.
133    */

134   public CmdLine addArg(int index, String JavaDoc name) {
135     return addArg(index, new Arg(name));
136   }
137
138   /**
139    * Adds an argument.
140    *
141    * @param arg an <code>Arg</code> instance.
142    */

143   public CmdLine addArg(Arg arg) {
144     _elems.add(arg);
145
146     return this;
147   }
148
149   /**
150    * Adds an argument.
151    *
152    * @param index the index at which to add the argument.
153    * @param arg an <code>Arg</code> instance.
154    */

155   public CmdLine addArg(int index, Arg arg) {
156     _elems.add(index, arg);
157
158     return this;
159   }
160
161   /**
162    * Adds an option with the given name and value.
163    *
164    * @param name the name of the option to add.
165    * @param value the value of the option.
166    */

167   public CmdLine addOpt(String JavaDoc name, String JavaDoc value) {
168     Option opt = new Option(name, value);
169
170     return addOpt(opt);
171   }
172
173   /**
174    * Adds an option.
175    *
176    * @param opt an <code>Option</code>.
177    */

178   public CmdLine addOpt(Option opt) {
179     _elems.add(opt);
180     _options.put(opt.getName(), opt);
181
182     return this;
183   }
184
185   /**
186    * Returns the <code>CmdElement</code> at the specified index.
187    *
188    * @param i an index.
189    * @return a <code>CmdElement</code>.
190    */

191   public CmdElement get(int i) {
192     return (CmdElement) _elems.get(i);
193   }
194
195   /**
196    * Returns the last <code>CmdElement</code> in this command-line.
197    * @return a <code>CmdElement</code>.
198    */

199   public CmdElement last() {
200     return (CmdElement) _elems.get(_elems.size() - 1);
201   }
202
203   /**
204    * Returns the first <code>CmdElement</code> in this command-line.
205    * @return a <code>CmdElement</code>.
206    */

207   public CmdElement first() {
208     return (CmdElement) _elems.get(0);
209   }
210
211   /**
212    * Returns true if the <code>next()</code> method can be called (i.e.:
213    * if the iteration can continue).
214    *
215    * @return <code>true</code> if the iteration can continue.
216    */

217   public boolean hasNext() {
218     return _index < _elems.size();
219   }
220
221   /**
222    * Returns the next command-line element (and internally increments
223    * the iteration counter).
224    *
225    * @return a <code>CmdElement</code>.
226    */

227   public CmdElement next() {
228     return (CmdElement) _elems.get(_index++);
229   }
230
231   /**
232    * Returns true if the next element in this instance is an
233    * <code>Arg</code> instance.
234    * <p>
235    * The <code>hasNext</code> method should be called priorly to ensure
236    * that a "next" element is indeed present:
237    * <p>
238    * <pre>
239    * if(cmdLine.hasNext()){
240    * if(cmdLine.isNextArg()){
241    * Arg arg = (Arg)cmdLine.next();
242    * }
243    * }
244    * </pre>
245    */

246   public boolean isNextArg() {
247     return _elems.get(_index) instanceof Arg;
248   }
249
250   /**
251    * Returns the next command-line element as an <code>Arg</code> instance.
252    * This method internally makes sure that there is a "next" command-line
253    * element, in that it is indeed an <code>Arg</code> instance. If these
254    * conditions are met, the instance is returned; if not, an exception
255    * is thrown.
256    *
257    * @throws InputException if no "next" argument exists.
258    */

259   public Arg assertNextArg() throws InputException {
260     if (!hasNext() || !isNextArg()) {
261       throw new InputException("argument expected");
262     }
263
264     return (Arg) _elems.get(_index++);
265   }
266
267   /**
268    * This method filters out all <code>Option</code> instances from its
269    * internal list, keeping only <code>Arg</code> instances. Mine you,
270    * the <code>Option</code> instances are still present in the returned
271    * <code>CommandLine</code> instance (and so available for processing);
272    * the difference is that successive calls to <code>next()</code>
273    * will only return <code>Arg</code> instances. This is useful when the
274    * iteration technique is used for processing arguments only, while
275    * the <code>assertOption(...)</code> and <code>containsOption(...)</code>
276    * methods are used for processing <code>Option</code> objects.
277    *
278    * @see #assertOption(String, String)
279    * @see #assertOption(String, String[])
280    * @see #containsOption(String, String)
281    */

282   public CmdLine filterArgs() {
283     List JavaDoc args = new ArrayList JavaDoc();
284
285     for (int i = 0; i < _elems.size(); i++) {
286       if (_elems.get(i) instanceof Arg) {
287         args.add((Arg) _elems.get(i));
288       }
289     }
290
291     return new CmdLine(args, _options);
292   }
293
294   /**
295    * Asserts that this instance contains a "next" argument whose name is
296    * in the argument names passed as parameters.
297    *
298    * @return an <code>Arg</code> that corresponds one of the
299    * passed in names.
300    * @param argNames an array of argument names.
301    * @throws InputException if no "next" argument with one of the given names
302    * could be found.
303    */

304   public Arg assertNextArg(String JavaDoc[] argNames) throws InputException {
305     if (!hasNext() || !isNextArg()) {
306       throw new InputException("argument expected");
307     }
308
309     Arg arg = (Arg) _elems.get(_index++);
310
311     for (int i = 0; i < argNames.length; i++) {
312       if (arg.getName().equals(argNames[i])) {
313         return arg;
314       }
315     }
316
317     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
318
319     for (int i = 0; i < argNames.length; i++) {
320       buf.append(argNames[i]);
321
322       if (i < (argNames.length - 1)) {
323         buf.append(" | ");
324       }
325     }
326
327     throw new InputException("one of the following arguments expected: " +
328       buf.toString() + ".");
329   }
330
331   /**
332    * Returns <code>true</code> if the "next" command-line element is
333    * an <code>Option</code> object.
334    *
335    * @return <code>true</code> if the next command-line element is
336    * an option.
337    */

338   public boolean isNextOption() {
339     return _elems.get(_index) instanceof Option;
340   }
341
342   /**
343    * Asserts that an option with the given name exists and returns it.
344    *
345    * @return an <code>Option</code> that corresponds to the
346    * passed in name.
347    * @param hasValue indicates if the option must have a value. if <code>hasValue</code> is true, and
348    * this instance has an <code>Option</code> for the given name but that option has no
349    * value, this method throws an <code>InputException</code>.
350    * @param optName the required option's name.
351    * @throws InputException if no option with the given name
352    * could be found.
353    */

354   public Option assertOption(String JavaDoc optName, boolean hasValue)
355     throws InputException {
356     if (!containsOption(optName, hasValue)) {
357       throw new InputException("option '" + optName + "' not specified.");
358     }
359
360     return (Option) _options.get(optName);
361   }
362
363   /**
364    * Asserts that an option with the given name and value exists
365    * and returns it.
366    *
367    *
368    * @param optName the required option's name.
369    * @param value the required option's value.
370    * @return an <code>Option</code> that corresponds to the
371    * passed in name and value.
372    * @throws InputException if no option with the given name and value
373    * could be found.
374    */

375   public Option assertOption(String JavaDoc optName, String JavaDoc value)
376     throws InputException {
377     Option opt = assertOption(optName, true);
378
379     if (!opt.getValue().equals(value)) {
380       throw new InputException("option '" + optName + "' expects value '" +
381         value + "'.");
382     }
383
384     return opt;
385   }
386
387   /**
388    * Asserts that an option with the given name and one of the
389    * given values exists and returns it.
390    *
391    *
392    * @param optName the required option's name.
393    * @param values the required option's values.
394    * @return an <code>Option</code> that corresponds to the
395    * passed in name and one of the given values.
396    * @throws InputException if no option with the given name and one
397    * of the given values could be found.
398    */

399   public Option assertOption(String JavaDoc optName, String JavaDoc[] values)
400     throws InputException {
401     Option opt = assertOption(optName, true);
402
403     for (int i = 0; i < values.length; i++) {
404       if (opt.getValue().equals(values[i])) {
405         return opt;
406       }
407     }
408
409     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
410
411     for (int i = 0; i < values.length; i++) {
412       buf.append(values[i]);
413
414       if (i < (values.length - 1)) {
415         buf.append(" | ");
416       }
417     }
418
419     throw new InputException("option '" + optName +
420       "' expects one of the following values: " + buf.toString() + ".");
421   }
422
423   /**
424    * Returns <code>true</code> if this instance containes an
425    * option with the given name.
426    *
427    * @param name the option's name.
428    * @param hasValue indicates if the option must have a value. if <code>hasValue</code> is true, and
429    * this instance has an <code>Option</code> for the given name but that option has no
430    * value, this method returns <code>false</code>.
431    * @return <code>true</code> if an option with the given name exists.
432    */

433   public boolean containsOption(String JavaDoc name, boolean hasValue) {
434     Option opt = (Option) _options.get(name);
435
436     if (opt == null) {
437       return false;
438     }
439
440     if ((opt.getValue() == null) && hasValue) {
441       return false;
442     }
443
444     return true;
445   }
446
447   /**
448    * Returns <code>true</code> if this instance containes an
449    * option with the given name and value..
450    *
451    * @param name the option's name.
452    * @param value the option's value.
453    * @return <code>true</code> if an option with the given name and value exists.
454    */

455   public boolean containsOption(String JavaDoc name, String JavaDoc value) {
456     Option opt = (Option) _options.get(name);
457
458     if (opt == null) {
459       return false;
460     }
461
462     return (opt.getValue() != null) && opt.getValue().equals(value);
463   }
464
465   /**
466    * Resets this instance's iteration counter to 0.
467    */

468   public void reset() {
469     _index = 0;
470   }
471
472   /**
473    * Adds a <code>CmdElement</code> instance.
474    *
475    * @param elem a <code>CmdElement</code> instance.
476    */

477   public void addElement(CmdElement elem) {
478     if (elem instanceof Option) {
479       addOpt((Option) elem);
480     } else if (elem instanceof Arg) {
481       addArg((Arg) elem);
482     } else {
483       throw new IllegalArgumentException JavaDoc("argument must be an instance of " +
484         Option.class.getName() + " or " + Arg.class.getName());
485     }
486   }
487
488   /**
489    * Removes the first element in this instance and returns it.
490    */

491   public CmdElement chop() {
492     CmdElement elem = (CmdElement) _elems.remove(0);
493
494     if (_index > 0) {
495       _index--;
496     }
497
498     return elem;
499   }
500
501   /**
502    * Removes the first element in this instance and returns it as
503    * an <code>Arg</code>, if it is such.
504    */

505   public Arg chopArg() {
506     Object JavaDoc elem = _elems.remove(0);
507
508     if (_index > 0) {
509       _index--;
510     }
511
512     if (elem instanceof Arg) {
513       return (Arg) elem;
514     } else {
515       return null;
516     }
517   }
518
519   /**
520    * Returns the number of command-line elements in this instance.
521    */

522   public int size() {
523     return _elems.size();
524   }
525
526   /**
527    * Returns this instance as an array of strings, such as would be
528    * passed to a <code>main</code> method.
529    *
530    * @return an array of <code>String</code>.
531    */

532   public String JavaDoc[] toArray() {
533     List JavaDoc all = new ArrayList JavaDoc();
534     Option opt;
535     String JavaDoc name;
536     String JavaDoc value;
537
538     for (int i = 0; i < _elems.size(); i++) {
539       if (_elems.get(i) instanceof Arg) {
540         Arg arg = (Arg) _elems.get(i);
541
542         if (arg.getName() != null) {
543           all.add(arg.getName());
544         }
545       } else {
546         opt = (Option) _elems.get(i);
547
548         if (opt.getName() != null) {
549           all.add("-" + opt.getName());
550
551           if (opt.getValue() != null) {
552             all.add(opt.getValue());
553           }
554         }
555       }
556     }
557
558     return (String JavaDoc[]) all.toArray(new String JavaDoc[all.size()]);
559   }
560
561   /**
562    * Returns this instance as an array of strings, such as would be
563    * passed to a <code>main</code> method. This instance's options
564    * are placed at the end of the array.
565    *
566    * @return an array of <code>String</code>, with options at the end.
567    */

568   public String JavaDoc[] toArrayOptionsLast() {
569     List JavaDoc all = new ArrayList JavaDoc();
570     List JavaDoc options = new ArrayList JavaDoc();
571     Option opt;
572     String JavaDoc name;
573     String JavaDoc value;
574
575     for (int i = 0; i < _elems.size(); i++) {
576       if (_elems.get(i) instanceof Arg) {
577         all.add(((Arg) _elems.get(i)).getName());
578       } else {
579         opt = (Option) _elems.get(i);
580         options.add("-" + opt.getName());
581
582         if (opt.getValue() != null) {
583           options.add(opt.getValue());
584         }
585       }
586     }
587
588     all.addAll(options);
589
590     return (String JavaDoc[]) all.toArray(new String JavaDoc[all.size()]);
591   }
592
593   /**
594    * Returns this instance as an array of strings, such as would be
595    * passed to a <code>main</code> method. This instance's options
596    * are placed at the beginning of the array.
597    *
598    * @return an array of <code>String</code>, with options at the beginning.
599    */

600   public String JavaDoc[] toArrayOptionsFirst() {
601     List JavaDoc all = new ArrayList JavaDoc();
602     List JavaDoc options = new ArrayList JavaDoc();
603     Option opt;
604     String JavaDoc name;
605     String JavaDoc value;
606
607     for (int i = 0; i < _elems.size(); i++) {
608       if (_elems.get(i) instanceof Arg) {
609         all.add(((Arg) _elems.get(i)).getName());
610       } else {
611         opt = (Option) _elems.get(i);
612         options.add("-" + opt.getName());
613
614         if (opt.getValue() != null) {
615           options.add(opt.getValue());
616         }
617       }
618     }
619
620     options.addAll(all);
621
622     return (String JavaDoc[]) options.toArray(new String JavaDoc[options.size()]);
623   }
624
625   /**
626    * Returns a clone of this instance.
627    *
628    * @return a <code>CmdLine</code> that is this instance's copy.
629    */

630   public Object JavaDoc clone() {
631     return new CmdLine(_elems, _options);
632   }
633
634   /**
635    * Returns this instance as a string.
636    *
637    * @return this instance as a <code>String</code>.
638    */

639   public String JavaDoc toString() {
640     CmdElement elem;
641     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
642
643     for (int i = 0; i < _elems.size(); i++) {
644       elem = (CmdElement) _elems.get(i);
645       buf.append(elem.toString());
646
647       if (i < (_elems.size() - 1)) {
648         buf.append(SPACE);
649       }
650     }
651
652     return buf.toString();
653   }
654
655   /**
656    * Returns a <code>CmdLine</code> containing the given parameters. This
657    * method is convenient to recuperate parameters from a main method.
658    *
659    * @return a <code>CmdLine</code> instance.
660    */

661   public static CmdLine parse(String JavaDoc[] args) {
662     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
663
664     for (int i = 0; i < args.length; i++) {
665       buf.append(args[i]).append(' ');
666     }
667
668     return parse(buf.toString().trim());
669   }
670
671   /**
672    * Executes this command-line. Returns the corresponding
673    * <code>ExecHandle</code>, which wraps the executed process.
674    *
675    * @return <code>ExecHandle</code>
676    */

677   public ExecHandle exec() {
678     CmdLineThread th = new CmdLineThread(this);
679     th.start();
680
681     return new ExecHandle(th);
682   }
683
684   /**
685    * Executes this command-line. Returns the corresponding
686    * <code>ExecHandle</code>, which wraps the executed process.
687    *
688    * @param env the environment variables, or <code>null</code> if no env. variables are to
689    * be passed to the process.
690    * @return <code>ExecHandle</code>
691    */

692   public ExecHandle exec(String JavaDoc[] env) {
693     CmdLineThread th = new CmdLineThread(this, env);
694     th.start();
695
696     return new ExecHandle(th);
697   }
698
699   /**
700    * Executes this command-line. Returns the corresponding
701    * <code>ExecHandle</code>, which wraps the executed process.
702    *
703    * @param processDir the process base directory.
704    * @param env the environment variables, or <code>null</code> if no env. variables are to
705    * be passed to the process.
706    * @return <code>ExecHandle</code>
707    */

708   public ExecHandle exec(File JavaDoc processDir, String JavaDoc[] env) {
709     CmdLineThread th = new CmdLineThread(this, processDir, env);
710     th.start();
711
712     return new ExecHandle(th);
713   }
714
715   /**
716    * Parses the given command-line and returns an instance of
717    * this class.
718    *
719    * @return a <code>CmdLine</code> instance.
720    */

721   public static CmdLine parse(String JavaDoc line) {
722     CmdLine cmd = new CmdLine();
723
724     char c;
725     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
726     boolean quote = false;
727
728     for (int i = 0; i < line.length(); i++) {
729       c = line.charAt(i);
730
731       if (c == SPACE) {
732         if (quote) {
733           buf.append(c);
734         } else {
735           addElem(cmd, buf);
736         }
737       } else if (c == QUOTE) {
738         if ((i > 0) && (line.charAt(i - 1) == ESCAPE)) {
739           buf.append(c);
740         } else if (quote) {
741           quote = false;
742         } else {
743           quote = true;
744         }
745       } else {
746         buf.append(c);
747       }
748     }
749
750     addElem(cmd, buf);
751
752     return cmd;
753   }
754
755   private static void addElem(CmdLine cmd, StringBuffer JavaDoc buf) {
756     if ((buf.length() > 0) && (buf.toString().charAt(0) == DASH)) {
757       StringBuffer JavaDoc buf2 = new StringBuffer JavaDoc(buf.toString().substring(1));
758       Option opt = new Option(buf2.toString());
759       cmd.addOpt(opt);
760     } else if (buf.length() > 0) {
761       if (cmd.size() > 0) {
762         if (cmd.last() instanceof Option) {
763           Option opt = (Option) cmd.last();
764
765           if (opt.getValue() == null) {
766             opt.setValue(buf.toString());
767           } else {
768             cmd.addArg(new Arg(buf.toString()));
769           }
770         } else {
771           cmd.addArg(new Arg(buf.toString()));
772         }
773       } else {
774         cmd.addArg(new Arg(buf.toString()));
775       }
776     }
777
778     buf.delete(0, buf.length());
779   }
780 }
781
Popular Tags