View Javadoc

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