View Javadoc

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  package org.apache.jdo.impl.enhancer;
19  
20  import java.io.PrintWriter;
21  
22  import java.util.Arrays;
23  import java.util.Map;
24  import java.util.List;
25  import java.util.Iterator;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.Properties;
29  
30  
31  /***
32   * Represents a set of options a program may support.
33   *
34   * @author Martin Zaun
35   */
36  public class OptionSet
37      extends LogSupport
38  {
39      // return values of parse/check methods
40      static public final int OK = 0;
41      static public final int USAGE_ERROR = -1;
42  
43      // command-line option prefixes
44      static public final String prefix = "-";
45      static public final String lprefix = "--";
46  
47      // ----------------------------------------------------------------------
48  
49      /***
50       * The base class of all option types.
51       */
52      static public abstract class Option
53      {
54          /***
55           * The set the option is registered with.
56           */
57          protected OptionSet set;
58  
59          /***
60           * The long form name of this option.
61           */
62          public final String name;
63  
64          /***
65           * The short form name of this option.
66           */
67          public final String abbrev;
68  
69          /***
70           * A description of this option.
71           */
72          public final String descr;
73  
74          /***
75           * Creates an instance.
76           */
77          public Option(String name,
78                        String abbrev,
79                        String descr)
80          {
81              affirm(name != null);
82              this.name = name;
83              this.abbrev = abbrev;
84              this.descr = descr;            
85          }
86  
87          /***
88           * Parse this option for arguments it may require.
89           */
90          abstract public int parse(Iterator i);
91  
92          /***
93           * Returns a <code>String</code> representation of this option's
94           * value for printing.
95           */
96          abstract public String asNameValue();
97  
98          /***
99           * Returns a usage description of this option.
100          */
101         public String asUsageHelp()
102         {
103             String abbr = (abbrev == null ? "   " : prefix + abbrev + "|");
104             return (abbr + lprefix + name + " " + descr);
105         }
106     }
107 
108     /***
109      * An option that always causes a USAGE_ERROR when parsed (used for
110      * '-h|--help' kind of options).
111      */
112     static public class HelpOption extends Option
113     {
114         /***
115          * Creates an instance.
116          */
117         public HelpOption(String name,
118                           String abbrev,
119                           String descr)
120         {
121             super(name, abbrev, descr);
122         }
123         
124         public int parse(Iterator i) 
125         {
126             return USAGE_ERROR;
127         }
128 
129         public String asNameValue()
130         {
131             return ("help = false");
132         }
133     }
134 
135     /***
136      * An option representing a boolean flag.
137      */
138     static public class FlagOption extends Option
139     {
140         /***
141          * The default value for this option.
142          */
143         public final boolean deflt;
144 
145         /***
146          * The value of this option.
147          */
148         public boolean value;
149 
150         /***
151          * Creates an instance.
152          */
153         public FlagOption(String name,
154                           String abbrev,
155                           String descr)
156         {
157             this(name, abbrev, descr, false);
158         }
159 
160         /***
161          * Creates an instance.
162          */
163         public FlagOption(String name,
164                           String abbrev,
165                           String descr,
166                           boolean deflt)
167         {
168             super(name, abbrev, descr);
169             this.deflt = deflt;
170             this.value = deflt;
171         }
172 
173         public int parse(Iterator i) 
174         {
175             if (value != deflt) {
176                 set.printUsageError("Repeated option: "
177                                     + prefix + abbrev + "/" + lprefix + name);
178                 return USAGE_ERROR;
179             }
180             value = true;
181             return OK;
182         }
183 
184         public String asNameValue()
185         {
186             return (name + " = " + String.valueOf(value));
187         }
188     }
189 
190     /***
191      * An option representing a <code>int</code> value.
192      */
193     static public class IntOption extends Option
194     {
195         /***
196          * The default value for this option.
197          */
198         public final int deflt;
199 
200         /***
201          * The value of this option.
202          */
203         public int value;
204 
205         /***
206          * Creates an instance.
207          */
208         public IntOption(String name,
209                          String abbrev,
210                          String descr)
211         {
212             this(name, abbrev, descr, 0);
213         }
214 
215         /***
216          * Creates an instance.
217          */
218         public IntOption(String name,
219                          String abbrev,
220                          String descr,
221                          int deflt)
222         {
223             super(name, abbrev, descr);
224             this.deflt = deflt;
225             this.value = deflt;
226         }
227 
228         public int parse(Iterator i) 
229         {
230             if (value != deflt) {
231                 set.printUsageError("Repeated option: "
232                                     + prefix + abbrev + "/" + lprefix + name);
233                 return USAGE_ERROR;
234             }
235             if (!i.hasNext()) {
236                 set.printUsageError("Missing argument to option: "
237                                     + prefix + abbrev + "/" + lprefix + name);
238                 return USAGE_ERROR;
239             }
240             try {
241                 value = Integer.valueOf((String)i.next()).intValue();
242             } catch (NumberFormatException ex) {
243                 set.printUsageError("Illegal argument to option: "
244                                     + prefix + abbrev + "/" + lprefix + name);
245                 return USAGE_ERROR;
246             }
247             return OK;
248         }
249 
250         public String asNameValue()
251         {
252             return (name + " = " + String.valueOf(value));
253         }
254     }
255 
256     /***
257      * An option representing a <code>String</code> value.
258      */
259     static public class StringOption extends Option
260     {
261         /***
262          * The default value for this option.
263          */
264         public final String deflt;
265 
266         /***
267          * The value of this option.
268          */
269         public String value;
270 
271         /***
272          * Creates an instance.
273          */
274         public StringOption(String name,
275                             String abbrev,
276                             String descr)
277         {
278             this(name, abbrev, descr, null);
279         }
280 
281         /***
282          * Creates an instance.
283          */
284         public StringOption(String name,
285                             String abbrev,
286                             String descr,
287                             String deflt)
288         {
289             super(name, abbrev, descr);
290             this.deflt = deflt;
291             this.value = deflt;
292         }
293 
294         public int parse(Iterator i) 
295         {
296             if (value != deflt) {
297                 set.printUsageError("Repeated option: "
298                                     + prefix + abbrev + "/" + lprefix + name);
299                 return USAGE_ERROR;
300             }
301             if (!i.hasNext()) {
302                 set.printUsageError("Missing argument to option: "
303                                     + prefix + abbrev + "/" + lprefix + name);
304                 return USAGE_ERROR;
305             }
306             value = (String)i.next();
307             if (value.startsWith(prefix)) {
308                 set.printUsageError("Missing argument to option: "
309                                     + prefix + abbrev + "/" + lprefix + name);
310                 return USAGE_ERROR;
311             }
312             return OK;
313         }
314 
315         public String asNameValue()
316         {
317             return (name + " = " + String.valueOf(value));
318         }
319     }
320 
321     // ----------------------------------------------------------------------
322 
323     /***
324      * The list of registered options.
325      */
326     protected final List options = new ArrayList();
327 
328     /***
329      * Maps the option's long form against option instances.
330      */
331     protected final Map names = new HashMap();
332 
333     /***
334      * Maps the option's short form against option instances.
335      */
336     protected final Map abbrevs = new HashMap();
337 
338     /***
339      * The collected arguments.
340      */
341     protected final List arguments = new ArrayList();
342 
343     /***
344      * Usage printout.
345      */
346     public String usageHeader
347         = "Usage: <options>.. <arguments>..";
348 
349     /***
350      * Usage printout.
351      */
352     public String optionsHeader
353         = "Options:";
354 
355     /***
356      * Usage printout.
357      */
358     public String argumentsHeader
359         = "Arguments:";
360 
361     /***
362      * Usage printout.
363      */
364     public String returnHeader
365         = "Returns: A non-zero value in case of errors.";
366 
367     /***
368      * Usage printout.
369      */
370     public String indent
371         = "    ";
372 
373     /***
374      * Creates an instance.
375      */
376     public OptionSet(PrintWriter out,
377                      PrintWriter err) 
378     {
379         super(out, err);
380     }
381 
382     /***
383      * Creates an instance.
384      */
385     public OptionSet(PrintWriter out,
386                      PrintWriter err,
387                      String usageHeader,
388                      String optionsHeader,
389                      String argumentsHeader,
390                      String returnHeader,
391                      String indent)
392     {
393         this(out, err);
394         this.usageHeader = usageHeader;
395         this.optionsHeader = optionsHeader;
396         this.argumentsHeader = argumentsHeader;
397         this.returnHeader = returnHeader;
398         this.indent = indent;
399     }
400 
401     // ----------------------------------------------------------------------
402 
403     /***
404      * Registers an option with the set.
405      */
406     public void register(Option option) 
407     {
408         affirm(option != null);
409         option.set = this;
410         options.add(option);
411 
412         affirm(option.name != null);
413         Object obj = names.put(lprefix + option.name, option);
414         affirm(obj == null, "Option already registered: " + option.name);
415 
416         if (option.abbrev != null) {
417             obj = abbrevs.put(prefix + option.abbrev, option);
418             affirm(obj == null, "Option already registered: " + option.name);
419         }
420     }
421 
422     /***
423      * Creates and registers an option representing a usage-help request.
424      */
425     public HelpOption createHelpOption(String name,
426                                        String abbrev,
427                                        String descr)
428     {
429         final HelpOption opt = new HelpOption(name, abbrev, descr);
430         register(opt);
431         return opt;
432     }
433 
434     /***
435      * Creates and registers an option representing a boolean flag.
436      */
437     public FlagOption createFlagOption(String name,
438                                        String abbrev,
439                                        String descr)
440     {
441         final FlagOption opt = new FlagOption(name, abbrev, descr);
442         register(opt);
443         return opt;
444     }
445 
446     /***
447      * Creates and registers an option representing a boolean flag.
448      */
449     public FlagOption createFlagOption(String name,
450                                        String abbrev,
451                                        String descr,
452                                        boolean deflt)
453     {
454         final FlagOption opt = new FlagOption(name, abbrev, descr, deflt);
455         register(opt);
456         return opt;
457     }
458 
459     /***
460      * Creates and registers an option representing a <code>int</code>
461      * value.
462      */
463     public IntOption createIntOption(String name,
464                                      String abbrev,
465                                      String descr)
466     {
467         final IntOption opt = new IntOption(name, abbrev, descr);
468         register(opt);
469         return opt;
470     }
471 
472     /***
473      * Creates and registers an option representing a <code>int</code>
474      * value.
475      */
476     public IntOption createIntOption(String name,
477                                      String abbrev,
478                                      String descr,
479                                      int deflt)
480     {
481         final IntOption opt = new IntOption(name, abbrev, descr, deflt);
482         register(opt);
483         return opt;
484     }
485 
486     /***
487      * Creates and registers an option representing a <code>String</code>
488      * value.
489      */
490     public StringOption createStringOption(String name,
491                                            String abbrev,
492                                            String descr)
493     {
494         final StringOption opt = new StringOption(name, abbrev, descr);
495         register(opt);
496         return opt;
497     }
498 
499     /***
500      * Creates and registers an option representing a <code>String</code>
501      * value.
502      */
503     public StringOption createStringOption(String name,
504                                            String abbrev,
505                                            String descr,
506                                            String deflt)
507     {
508         final StringOption opt
509             = new StringOption(name, abbrev, descr, deflt);
510         register(opt);
511         return opt;
512     }
513 
514     // ----------------------------------------------------------------------
515 
516     /***
517      * Parses options and arguments.
518      */
519     public int parse(String[] argv)
520     {
521         affirm(argv != null);
522         for (Iterator i = Arrays.asList(argv).iterator(); i.hasNext();) {
523             final String arg = (String)i.next();
524 
525             // ignore empty arguments
526             if (arg == null || arg.length() == 0) {
527                 //println("Ignoring empty command line argument.");
528                 continue;
529             }
530 
531             // collect as argument if not option
532             if (!arg.startsWith(prefix)) {
533                 arguments.add(arg);
534                 continue;                
535             }
536 
537             // lookup option by short and long form
538             Option option = (Option)abbrevs.get(arg);
539             if (option == null) {
540                 option = (Option)names.get(arg);
541             }
542 
543             // return if option still not recognized
544             if (option == null) {
545                 printlnErr("Unrecognized option: " + arg);
546                 return USAGE_ERROR;
547             }
548 
549             // parse option for arguments
550             int res = option.parse(i);
551             if (res != OK) {
552                 return res;
553             }
554         }
555         return OK;
556     }
557 
558     /***
559      * Checks options and arguments.
560      */
561     public int check()
562     {
563         return OK;
564     }
565 
566     /***
567      * Parse and check options and arguments.
568      */
569     public int process(String[] args)
570     {
571         int res = OK;
572         if ((res = parse(args)) != OK) {
573             printUsage();
574             return res;
575         }
576         if ((res = check()) != OK) {
577             printUsage();
578             return res;
579         }
580         return res;
581     }
582 
583     // ----------------------------------------------------------------------
584 
585     /***
586      * Print a usage error message to System.err.
587      */
588     public void printUsageError(String msg)
589     {
590         printlnErr("USAGE ERROR: " + msg);
591     }
592     
593     /***
594      * Print a usage message to System.err.
595      */
596     public void printUsage()
597     {
598         println();
599         printUsageHeader();
600         printOptionHeader();
601         printOptionUsage();
602         printArgumentHeader();
603         printArgumentUsage();
604         printReturnHeader();
605         printReturnUsage();
606     }
607 
608     /***
609      * Print a usage message to System.err.
610      */
611     public void printUsageHeader()
612     {
613         printlnErr(usageHeader);
614     }
615 
616     /***
617      * Print a usage message to System.err.
618      */
619     public void printOptionHeader()
620     {
621         printlnErr();
622         printlnErr(optionsHeader);
623     }
624 
625     /***
626      * Print a usage message to System.err.
627      */
628     public void printOptionUsage()
629     {
630         for (Iterator i = options.iterator(); i.hasNext();) {
631             printlnErr(indent + ((Option)i.next()).asUsageHelp());
632         }
633     }
634 
635     /***
636      * Print a usage message to System.err.
637      */
638     public void printArgumentHeader()
639     {
640         printlnErr();
641         printlnErr(argumentsHeader);
642     }
643 
644     /***
645      * Print a usage message to System.err.
646      */
647     public void printArgumentUsage()
648     {}
649 
650     /***
651      * Print a usage message to System.err.
652      */
653     public void printReturnHeader()
654     {
655         printlnErr();
656         printlnErr(returnHeader);
657     }
658 
659     /***
660      * Print a usage message to System.err.
661      */
662     public void printReturnUsage()
663     {}
664 
665     // ----------------------------------------------------------------------
666 
667     /***
668      * Print options and arguments.
669      */
670     public void printAll()
671     {
672         printOptions();
673         printArguments();
674     }
675 
676     /***
677      * Print options.
678      */
679     public void printOptions()
680     {
681         println();
682         println(optionsHeader);
683         for (Iterator i = options.iterator(); i.hasNext();) {
684             println(indent + ((Option)i.next()).asNameValue());
685         }
686     }
687     
688     /***
689      * Print arguments.
690      */
691     public void printArguments()
692     {
693         println();
694         println(argumentsHeader);
695         print(indent);
696         for (Iterator i = arguments.iterator(); i.hasNext();) {
697             print(" " + i.next());
698         }
699     }
700 
701     // ----------------------------------------------------------------------
702 
703     /***
704      * Tests the class.
705      */
706     static public void main(String[] args)
707     {
708         final PrintWriter out = new PrintWriter(System.out, true);
709         out.println("--> OptionSet.main()");
710         final OptionSet options = new OptionSet(out, out);
711         out.println("    options.process() ...");
712         int res = options.process(args);
713         out.println("    return value: " + res);
714         out.println("<-- OptionSet.main()");
715     }
716 }