KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > vladium > util > args > OptsParser


1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2  *
3  * This program and the accompanying materials are made available under
4  * the terms of the Common Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
6  *
7  * $Id: OptsParser.java,v 1.1.1.1 2004/05/09 16:57:57 vlad_r Exp $
8  */

9 package com.vladium.util.args;
10
11 import java.io.CharArrayWriter JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.io.InputStream JavaDoc;
14 import java.io.InputStreamReader JavaDoc;
15 import java.io.PrintWriter JavaDoc;
16 import java.io.Reader JavaDoc;
17 import java.util.ArrayList JavaDoc;
18 import java.util.HashMap JavaDoc;
19 import java.util.HashSet JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Set JavaDoc;
24
25 import com.vladium.util.IConstants;
26 import com.vladium.util.ResourceLoader;
27
28 // ----------------------------------------------------------------------------
29
/**
30  * @author Vlad Roubtsov, (C) 2002
31  */

32 final class OptsParser implements IOptsParser
33 {
34     // public: ................................................................
35

36     // TODO: #-comments
37
// TODO: prefixing for error messages
38
// TODO: support var subst (main class name, etc)
39
// TODO: support short/full usage
40
// TODO: support marking opts as for displayable in full usage only
41

42     public synchronized void usage (final PrintWriter JavaDoc out, final int level, final int width)
43     {
44         // TODO: use width
45
// TODO: cache?
46

47         final String JavaDoc prefix = OPT_PREFIXES [CANONICAL_OPT_PREFIX];
48         
49         for (Iterator JavaDoc i = m_metadata.getOptDefs (); i.hasNext (); )
50         {
51             final OptDef optdef = (OptDef) i.next ();
52             
53             if ((level < 2) && optdef.isDetailedOnly ()) // skip detailed usage only options
54
continue;
55             
56             final StringBuffer JavaDoc line = new StringBuffer JavaDoc (" ");
57             
58             final String JavaDoc canonicalName = optdef.getCanonicalName ();
59             final boolean isPattern = optdef.isPattern ();
60              
61             line.append (prefix);
62             line.append (canonicalName);
63             if (isPattern) line.append ('*');
64             
65             final String JavaDoc [] names = optdef.getNames ();
66             for (int n = 0; n < names.length; ++ n)
67             {
68                 final String JavaDoc name = names [n];
69                 if (! name.equals (canonicalName))
70                 {
71                     line.append (", ");
72                     
73                     line.append (prefix);
74                     line.append (name);
75                     if (isPattern) line.append ('*');
76                 }
77             }
78
79             final String JavaDoc vmnemonic = optdef.getValueMnemonic ();
80             if (vmnemonic != null)
81             {
82                 line.append (' ');
83                 line.append (vmnemonic);
84             }
85
86             
87             int padding = 16 - line.length ();
88             if (padding < 2)
89             {
90                 // end the current line
91
out.println (line);
92                 
93                 line.setLength (0);
94                 for (int p = 0; p < 16; ++ p) line.append (' ');
95             }
96             else
97             {
98                 for (int p = 0; p < padding; ++ p) line.append (' ');
99             }
100             
101             if (optdef.isRequired ()) line.append ("{required} ");
102             line.append (optdef.getDescription ());
103             
104             out.println (line);
105         }
106         
107         if (level < DETAILED_USAGE)
108         {
109             final OptDef usageOptDef = m_metadata.getUsageOptDef ();
110             if ((usageOptDef != null) && (usageOptDef.getNames () != null) && (usageOptDef.getNames ().length > 1))
111             {
112                 out.println ();
113                 out.println (" {use '" + usageOptDef.getNames () [1] + "' option to see detailed usage help}");
114             }
115         }
116     }
117     
118     public synchronized IOpts parse (final String JavaDoc [] args)
119     {
120         if (args == null) throw new IllegalArgumentException JavaDoc ("null input: args");
121         
122         final Opts opts = new Opts ();
123         
124         {
125             final String JavaDoc [] nv = new String JavaDoc [2]; // out buffer for getOptNameAndValue()
126
final String JavaDoc [] pp = new String JavaDoc [1]; // out buffer for getOptDef()
127

128             // running state/current vars:
129
int state = STATE_OPT;
130             OptDef optdef = null;
131             Opt opt = null;
132             String JavaDoc value = null;
133             int valueCount = 0;
134             
135             int a;
136       scan: for (a = 0; a < args.length; )
137             {
138                 final String JavaDoc av = args [a];
139                 if (av == null) throw new IllegalArgumentException JavaDoc ("null input: args[" + a + "]");
140                 
141                 //System.out.println ("[state: " + state + "] av = " + av);
142

143                 switch (state)
144                 {
145                     case STATE_OPT:
146                     {
147                         if (isOpt (av, valueCount, optdef))
148                         {
149                             // 'av' looks like an option: get its name and see if it
150
// is in the metadata
151

152                             valueCount = 0;
153                            
154                             getOptNameAndValue (av, nv); // this can leave nv[1] as null
155

156                             // [assertion: nv [0] != null]
157

158                             final String JavaDoc optName = nv [0]; // is not necessarily canonical
159
optdef = m_metadata.getOptDef (optName, pp); // pp [0] is always set by this
160

161                             if (optdef == null)
162                             {
163                                 // unknown option:
164

165                                 // TODO: coded messages?
166
opts.addError (formatMessage ("unknown option \'" + optName + "\'"));
167                                
168                                 state = STATE_ERROR;
169                             }
170                             else
171                             {
172                                 // merge if necessary:
173

174                                 final String JavaDoc canonicalName = getOptCanonicalName (optName, optdef);
175                                 final String JavaDoc patternPrefix = pp [0];
176     
177                                 opt = opts.getOpt (canonicalName);
178                                 
179                                 if (optdef.isMergeable ())
180                                 {
181                                     if (opt == null)
182                                     {
183                                         opt = new Opt (optName, canonicalName, patternPrefix);
184                                         opts.addOpt (opt, optdef, optName);
185                                     }
186                                 }
187                                 else
188                                 {
189                                     if (opt == null)
190                                     {
191                                         opt = new Opt (optName, canonicalName, patternPrefix);
192                                         opts.addOpt (opt, optdef, optName);
193                                     }
194                                     else
195                                     {
196                                         opts.addError (formatMessage ("option \'" + optName + "\' cannot be specified more than once"));
197                                
198                                         state = STATE_ERROR;
199                                     }
200                                 }
201
202                                 value = nv [1];
203                                 
204                                 if (value == null) ++ a;
205                                 state = STATE_OPT_VALUE;
206                             }
207                         }
208                         else
209                         {
210                             // not in STATE_OPT_VALUE and 'av' does not look
211
// like an option: the rest of args are free
212

213                             state = STATE_FREE_ARGS;
214                         }
215                     }
216                     break;
217                     
218                     
219                     case STATE_OPT_VALUE:
220                     {
221                         // [assertion: opt != null and optdef != null]
222

223                         if (value != null)
224                         {
225                             // value specified explicitly using the <name>separator<value> syntax:
226
// [don't shift a]
227

228                             valueCount = 1;
229                              
230                             final int [] cardinality = optdef.getValueCardinality ();
231                             
232                             if (cardinality [1] < 1)
233                             {
234                                 opts.addError (formatMessage ("option \'" + opt.getName () + "\' does not accept values: \'" + value + "\'"));
235                                
236                                 state = STATE_ERROR;
237                             }
238                             else
239                             {
240                                 ++ a;
241                                 opt.addValue (value);
242                             }
243                         }
244                         else
245                         {
246                             value = args [a];
247                             
248                             final int [] cardinality = optdef.getValueCardinality ();
249                         
250                             if (isOpt (value, valueCount, optdef))
251                             {
252                                 if (valueCount < cardinality [0])
253                                 {
254                                     opts.addError (formatMessage ("option \'" + opt.getName () + "\' does not accept fewer than " + cardinality [0] + " value(s)"));
255                                    
256                                     state = STATE_ERROR;
257                                 }
258                                 else
259                                     state = STATE_OPT;
260                             }
261                             else
262                             {
263                                 if (valueCount < cardinality [1])
264                                 {
265                                     ++ valueCount;
266                                     ++ a;
267                                     opt.addValue (value);
268                                 }
269                                 else
270                                 {
271                                     // this check is redundant:
272
// if (valueCount < cardinality [0])
273
// {
274
// opts.addError (formatMessage ("option \'" + opt.getName () + "\' does not accept fewer than " + cardinality [0] + " value(s)"));
275
//
276
// state = STATE_ERROR;
277
// }
278
// else
279
state = STATE_FREE_ARGS;
280                                 }
281                             }
282                         }
283                         
284                         value = null;
285                     }
286                     break;
287                     
288                     
289                     case STATE_FREE_ARGS:
290                     {
291                         if (isOpt (args [a], valueCount, optdef))
292                         {
293                             state = STATE_OPT;
294                         }
295                         else
296                         {
297                             opts.setFreeArgs (args, a);
298                             break scan;
299                         }
300                     }
301                     break;
302                     
303                     
304                     case STATE_ERROR:
305                     {
306                         break scan; // TODO: could use the current value of 'a' for a better error message
307
}
308                     
309                 } // end of switch
310
}
311             
312             if (a == args.length)
313             {
314                 if (opt != null) // validate the last option's min cardinality
315
{
316                     final int [] cardinality = optdef.getValueCardinality ();
317                     
318                     if (valueCount < cardinality [0])
319                     {
320                         opts.addError (formatMessage ("option \'" + opt.getName () + "\' does not accept fewer than " + cardinality [0] + " value(s)"));
321                     }
322                 }
323                 else
324                 {
325                     opts.setFreeArgs (args, a);
326                 }
327             }
328             
329         } // end of 'args' parsing
330

331         
332         final IOpt [] specified = opts.getOpts ();
333         if (specified != null)
334         {
335             // validation: all required parameters must be specified
336

337             final Set JavaDoc /* String(canonical name) */ required = new HashSet JavaDoc ();
338             required.addAll (m_metadata.getRequiredOpts ());
339             
340             for (int s = 0; s < specified.length; ++ s)
341             {
342                 required.remove (specified [s].getCanonicalName ());
343             }
344             
345             if (! required.isEmpty ())
346             {
347                 for (Iterator JavaDoc i = required.iterator (); i.hasNext (); )
348                 {
349                     opts.addError (formatMessage ("missing required option \'" + (String JavaDoc) i.next () + "\'"));
350                 }
351             }
352             
353             for (int s = 0; s < specified.length; ++ s)
354             {
355                 final IOpt opt = specified [s];
356                 final OptDef optdef = m_metadata.getOptDef (opt.getCanonicalName (), null);
357                 
358 // // validation: value cardinality constraints
359
//
360
// final int [] cardinality = optdef.getValueCardinality ();
361
// if (opt.getValueCount () < cardinality [0])
362
// opts.addError (formatMessage ("option \'" + opt.getName () + "\' must have at least " + cardinality [0] + " value(s)"));
363
// else if (opt.getValueCount () > cardinality [1])
364
// opts.addError (formatMessage ("option \'" + opt.getName () + "\' must not have more than " + cardinality [1] + " value(s)"));
365

366                 // validation: "requires" constraints
367

368                 final String JavaDoc [] requires = optdef.getRequiresSet (); // not canonicalized
369
if (requires != null)
370                 {
371                     for (int r = 0; r < requires.length; ++ r)
372                     {
373                         if (opts.getOpt (requires [r]) == null)
374                             opts.addError (formatMessage ("option \'" + opt.getName () + "\' requires option \'" + requires [r] + "\'"));
375                     }
376                 }
377                 
378                 // validation: "not with" constraints
379

380                 final String JavaDoc [] excludes = optdef.getExcludesSet (); // not canonicalized
381
if (excludes != null)
382                 {
383                     for (int x = 0; x < excludes.length; ++ x)
384                     {
385                         final Opt xopt = opts.getOpt (excludes [x]);
386                         if (xopt != null)
387                             opts.addError (formatMessage ("option \'" + opt.getName () + "\' cannot be used with option \'" + xopt.getName () + "\'"));
388                     }
389                 }
390                 
391                 // side effect: determine if usage is requested
392

393                 if (optdef.isUsage ())
394                 {
395                     opts.setUsageRequested (opt.getName ().equals (opt.getCanonicalName ()) ? SHORT_USAGE : DETAILED_USAGE);
396                 }
397             }
398         }
399         
400         return opts;
401     }
402     
403     private static String JavaDoc getOptCanonicalName (final String JavaDoc n, final OptDef optdef)
404     {
405         if (optdef.isPattern ())
406         {
407             final String JavaDoc canonicalPattern = optdef.getCanonicalName ();
408             final String JavaDoc [] patterns = optdef.getNames ();
409             
410             for (int p = 0; p < patterns.length; ++ p)
411             {
412                 final String JavaDoc pattern = patterns [p];
413                 
414                 if (n.startsWith (pattern))
415                 {
416                     return canonicalPattern.concat (n.substring (pattern.length ()));
417                 }
418             }
419             
420             // this should never happen:
421
throw new IllegalStateException JavaDoc ("failed to detect pattern prefix for [" + n + "]");
422         }
423         else
424         {
425             return optdef.getCanonicalName ();
426         }
427     }
428     
429     /*
430      * ['optdef' can be null if no current opt def context has been established]
431      *
432      * pre: av != null
433      * input not validated
434      */

435     private static boolean isOpt (final String JavaDoc av, final int valueCount, final OptDef optdef)
436     {
437         if (optdef != null)
438         {
439             // if the current optdef calls for more values, consume the next token
440
// as an op value greedily, without looking at its prefix:
441

442             final int [] cardinality = optdef.getValueCardinality ();
443         
444             if (valueCount < cardinality [1]) return false;
445         }
446         
447         // else check av's prefix:
448

449         for (int p = 0; p < OPT_PREFIXES.length; ++ p)
450         {
451             if (av.startsWith (OPT_PREFIXES [p]))
452                 return (av.length () > OPT_PREFIXES [p].length ());
453         }
454         
455         return false;
456     }
457
458     /*
459      * pre: av != null and isOpt(av)=true
460      * input not validated
461      */

462     private static void getOptNameAndValue (final String JavaDoc av, final String JavaDoc [] nv)
463     {
464         nv [0] = null;
465         nv [1] = null;
466         
467         for (int p = 0; p < OPT_PREFIXES.length; ++ p)
468         {
469             if ((av.startsWith (OPT_PREFIXES [p])) && (av.length () > OPT_PREFIXES [p].length ()))
470             {
471                 final String JavaDoc name = av.substring (OPT_PREFIXES [p].length ()); // with a possible value after a separator
472

473                 char separator = 0;
474                 int sindex = Integer.MAX_VALUE;
475                 
476                 for (int s = 0; s < OPT_VALUE_SEPARATORS.length; ++ s)
477                 {
478                     final int index = name.indexOf (OPT_VALUE_SEPARATORS [s]);
479                     if ((index > 0) && (index < sindex))
480                     {
481                         separator = OPT_VALUE_SEPARATORS [s];
482                         sindex = index;
483                     }
484                 }
485                 
486                 if (separator != 0)
487                 {
488                     nv [0] = name.substring (0, sindex);
489                     nv [1] = name.substring (sindex + 1);
490                 }
491                 else
492                 {
493                     nv [0] = name;
494                 }
495                 
496                 return;
497             }
498         }
499     }
500     
501     // protected: .............................................................
502

503     // package: ...............................................................
504

505
506     static final class Opt implements IOptsParser.IOpt
507     {
508         public String JavaDoc getName ()
509         {
510             return m_name;
511         }
512         
513         public String JavaDoc getCanonicalName ()
514         {
515             return m_canonicalName;
516         }
517         
518         public int getValueCount ()
519         {
520             if (m_values == null) return 0;
521             
522             return m_values.size ();
523         }
524
525         public String JavaDoc getFirstValue ()
526         {
527             if (m_values == null) return null;
528             
529             return (String JavaDoc) m_values.get (0);
530         }
531
532         public String JavaDoc [] getValues ()
533         {
534             if (m_values == null) return IConstants.EMPTY_STRING_ARRAY;
535             
536             final String JavaDoc [] result = new String JavaDoc [m_values.size ()];
537             m_values.toArray (result);
538             
539             return result;
540         }
541         
542         public String JavaDoc getPatternPrefix ()
543         {
544             return m_patternPrefix;
545         }
546         
547         public String JavaDoc toString ()
548         {
549             final StringBuffer JavaDoc s = new StringBuffer JavaDoc (m_name);
550             if (! m_canonicalName.equals (m_name)) s.append (" [" + m_canonicalName + "]");
551             
552             if (m_values != null)
553             {
554                 s.append (": ");
555                 s.append (m_values);
556             }
557             
558             return s.toString ();
559         }
560         
561         Opt (final String JavaDoc name, final String JavaDoc canonicalName, final String JavaDoc patternPrefix)
562         {
563             m_name = name;
564             m_canonicalName = canonicalName;
565             m_patternPrefix = patternPrefix;
566         }
567         
568         void addValue (final String JavaDoc value)
569         {
570             if (value == null) throw new IllegalArgumentException JavaDoc ("null input: value");
571             
572             if (m_values == null) m_values = new ArrayList JavaDoc ();
573             m_values.add (value);
574         }
575         
576         
577         private final String JavaDoc m_name, m_canonicalName, m_patternPrefix;
578         private ArrayList JavaDoc m_values;
579
580     } // end of nested class
581

582     
583     static final class Opts implements IOptsParser.IOpts
584     {
585         public int usageRequestLevel ()
586         {
587             return m_usageRequestLevel;
588         }
589         
590         public void error (final PrintWriter JavaDoc out, final int width)
591         {
592             // TODO: use width
593
if (hasErrors ())
594             {
595                 for (Iterator JavaDoc i = m_errors.iterator (); i.hasNext (); )
596                 {
597                     out.println (i.next ());
598                 }
599             }
600         }
601
602         public String JavaDoc [] getFreeArgs ()
603         {
604             if (hasErrors ())
605                 throw new IllegalStateException JavaDoc (errorsToString ());
606             
607             return m_freeArgs;
608         }
609
610         public IOpt [] getOpts ()
611         {
612             if (hasErrors ()) return null;
613             
614             if (m_opts.isEmpty ())
615                 return EMPTY_OPT_ARRAY;
616             else
617             {
618                 final IOpt [] result = new IOpt [m_opts.size ()];
619                 m_opts.toArray (result);
620                 
621                 return result;
622             }
623         }
624         
625         public IOpt [] getOpts (final String JavaDoc pattern)
626         {
627             if (hasErrors ()) return null;
628             
629             final List JavaDoc /* Opt */ patternOpts = (List JavaDoc) m_patternMap.get (pattern);
630             
631             if ((patternOpts == null) || patternOpts.isEmpty ())
632                 return EMPTY_OPT_ARRAY;
633             else
634             {
635                 final IOpt [] result = new IOpt [patternOpts.size ()];
636                 patternOpts.toArray (result);
637                 
638                 return result;
639             }
640         }
641
642
643         public boolean hasArg (final String JavaDoc name)
644         {
645             if (hasErrors ())
646                 throw new IllegalStateException JavaDoc (errorsToString ());
647             
648             return m_nameMap.containsKey (name);
649         }
650         
651         Opts ()
652         {
653             m_opts = new ArrayList JavaDoc ();
654             m_nameMap = new HashMap JavaDoc ();
655             m_patternMap = new HashMap JavaDoc ();
656         }
657         
658         void addOpt (final Opt opt, final OptDef optdef, final String JavaDoc occuranceName)
659         {
660             if (opt == null) throw new IllegalArgumentException JavaDoc ("null input: opt");
661             if (optdef == null) throw new IllegalArgumentException JavaDoc ("null input: optdef");
662             if (occuranceName == null) throw new IllegalArgumentException JavaDoc ("null input: occuranceName");
663             
664             // [name collisions detected elsewhere]
665

666             m_opts.add (opt);
667             
668             final String JavaDoc [] names = optdef.getNames ();
669             final boolean isPattern = (opt.getPatternPrefix () != null);
670             
671             if (isPattern)
672             {
673                 final String JavaDoc unprefixedName = occuranceName.substring (opt.getPatternPrefix ().length ());
674                 
675                 for (int n = 0; n < names.length; ++ n)
676                 {
677                     m_nameMap.put (names [n].concat (unprefixedName), opt);
678                 }
679                 
680                 {
681                     final String JavaDoc canonicalPattern = optdef.getCanonicalName ();
682                 
683                     List JavaDoc patternList = (List JavaDoc) m_patternMap.get (canonicalPattern);
684                     if (patternList == null)
685                     {
686                         patternList = new ArrayList JavaDoc ();
687                         for (int n = 0; n < names.length; ++ n)
688                         {
689                             m_patternMap.put (names [n], patternList);
690                         }
691                     }
692                     
693                     patternList.add (opt);
694                 }
695             }
696             else
697             {
698                 for (int n = 0; n < names.length; ++ n)
699                 {
700                     m_nameMap.put (names [n], opt);
701                 }
702             }
703         }
704         
705         Opt getOpt (final String JavaDoc occuranceName)
706         {
707             if (occuranceName == null) throw new IllegalArgumentException JavaDoc ("null input: occuranceName");
708             
709             return (Opt) m_nameMap.get (occuranceName);
710         }
711         
712         void setFreeArgs (final String JavaDoc [] args, final int start)
713         {
714             if (args == null) throw new IllegalArgumentException JavaDoc ("null input: args");
715             if ((start < 0) || (start > args.length)) throw new IllegalArgumentException JavaDoc ("invalid start index: " + start);
716             
717             m_freeArgs = new String JavaDoc [args.length - start];
718             System.arraycopy (args, start, m_freeArgs, 0, m_freeArgs.length);
719         }
720         
721         void setUsageRequested (final int level)
722         {
723             m_usageRequestLevel = level;
724         }
725         
726         void addError (final String JavaDoc msg)
727         {
728             if (msg != null)
729             {
730                 if (m_errors == null) m_errors = new ArrayList JavaDoc ();
731                 
732                 m_errors.add (msg);
733             }
734         }
735         
736         boolean hasErrors ()
737         {
738             return (m_errors != null) && ! m_errors.isEmpty ();
739         }
740         
741         String JavaDoc errorsToString ()
742         {
743             if (! hasErrors ()) return "<no errors>";
744             
745             final CharArrayWriter JavaDoc caw = new CharArrayWriter JavaDoc ();
746             final PrintWriter JavaDoc pw = new PrintWriter JavaDoc (caw);
747             
748             error (pw, DEFAULT_ERROR_WIDTH);
749             pw.flush ();
750             
751             return caw.toString ();
752         }
753         
754         
755         private final List JavaDoc /* Opt */ m_opts;
756         private final Map JavaDoc /* String(name/pattern-prefixed name)->Opt */ m_nameMap;
757         private final Map JavaDoc /* String(pattern prefix)->List<Opt> */ m_patternMap;
758         private String JavaDoc [] m_freeArgs;
759         private List JavaDoc /* String */ m_errors;
760         private int m_usageRequestLevel;
761         
762         private static final int DEFAULT_ERROR_WIDTH = 80;
763         private static final IOpt [] EMPTY_OPT_ARRAY = new IOpt [0];
764
765     } // end of nested class
766

767
768     static final class OptDef // TODO: merge with Opt?
769
{
770         OptDef (final boolean usage)
771         {
772             m_usage = usage;
773         }
774         
775         boolean isUsage ()
776         {
777             return m_usage;
778         }
779         
780         String JavaDoc getCanonicalName ()
781         {
782             return m_names [0];
783         }
784         
785         String JavaDoc [] getNames ()
786         {
787             return m_names;
788         }
789         
790         boolean isRequired ()
791         {
792             return m_required;
793         }
794         
795         String JavaDoc getValueMnemonic ()
796         {
797             return m_valueMnemonic;
798         }
799         
800         boolean isMergeable ()
801         {
802             return m_mergeable;
803         }
804         
805         boolean isDetailedOnly ()
806         {
807             return m_detailedOnly;
808         }
809         
810         boolean isPattern ()
811         {
812             return m_pattern;
813         }
814         
815         int [] getValueCardinality ()
816         {
817             return m_valueCardinality;
818         }
819         
820         String JavaDoc [] getRequiresSet ()
821         {
822             return m_requiresSet;
823         }
824         
825         String JavaDoc [] getExcludesSet ()
826         {
827             return m_excludesSet;
828         }
829         
830         String JavaDoc getDescription ()
831         {
832             return m_description;
833         }
834         
835         void setNames (final String JavaDoc [] names)
836         {
837             if (names == null) throw new IllegalArgumentException JavaDoc ("null input: names");
838             
839             m_names = names;
840         }
841         
842         void setRequired (final boolean required)
843         {
844             m_required = required;
845         }
846         
847         void setValueMnemonic (final String JavaDoc mnemonic)
848         {
849             if (mnemonic == null) throw new IllegalArgumentException JavaDoc ("null input: mnemonic");
850             
851             m_valueMnemonic = mnemonic;
852         }
853         
854         void setMergeable (final boolean mergeable)
855         {
856             m_mergeable = mergeable;
857         }
858         
859         void setDetailedOnly (final boolean detailedOnly)
860         {
861             m_detailedOnly = detailedOnly;
862         }
863         
864         void setPattern (final boolean pattern)
865         {
866             m_pattern = pattern;
867         }
868         
869         void setValueCardinality (final int [] cardinality)
870         {
871             if ((cardinality == null) || (cardinality.length != 2)) throw new IllegalArgumentException JavaDoc ("null or invalid input: cardinality");
872             
873             m_valueCardinality = cardinality;
874         }
875         
876         void setRequiresSet (final String JavaDoc [] names)
877         {
878             if (names == null) throw new IllegalArgumentException JavaDoc ("null input: names");
879             
880             m_requiresSet = names.length > 0 ? names : null;
881         }
882         
883         void setExcludesSet (final String JavaDoc [] names)
884         {
885             if (names == null) throw new IllegalArgumentException JavaDoc ("null input: names");
886             
887             m_excludesSet = names.length > 0 ? names : null;
888         }
889         
890         void setDescription (final String JavaDoc description)
891         {
892             if (description == null) throw new IllegalArgumentException JavaDoc ("null input: description");
893             
894             m_description = description;
895         }
896         
897         
898         static final int [] C_ZERO = new int [] {0, 0};
899         static final int [] C_ONE = new int [] {1, 1};
900         static final int [] C_ZERO_OR_ONE = new int [] {0, 1};
901         static final int [] C_ZERO_OR_MORE = new int [] {0, Integer.MAX_VALUE};
902         static final int [] C_ONE_OR_MORE = new int [] {1, Integer.MAX_VALUE};
903         
904         
905         private final boolean m_usage;
906         private String JavaDoc [] m_names;
907         private boolean m_required;
908         private String JavaDoc m_valueMnemonic;
909         private boolean m_mergeable;
910         private boolean m_detailedOnly;
911         private boolean m_pattern;
912         private int [] m_valueCardinality;
913         private String JavaDoc [] m_requiresSet, m_excludesSet;
914         private String JavaDoc m_description;
915         
916     } // end of nested class
917

918     
919     static final class OptDefMetadata
920     {
921         OptDefMetadata ()
922         {
923             m_optdefs = new ArrayList JavaDoc ();
924             m_optdefMap = new HashMap JavaDoc ();
925             m_requiredOpts = new HashSet JavaDoc ();
926             m_patternOptDefMap = new HashMap JavaDoc ();
927         }
928         
929         OptDef getOptDef (final String JavaDoc name, final String JavaDoc [] prefixout)
930         {
931             if (name == null) throw new IllegalArgumentException JavaDoc ("null input: name");
932             
933             if (prefixout != null) prefixout [0] = null;
934             
935             // first, see if this is a regular option:
936
OptDef result = (OptDef) m_optdefMap.get (name);
937             
938             // next, see if this is a prefixed option:
939
if (result == null)
940             {
941                 for (Iterator JavaDoc ps = m_patternOptDefMap.entrySet ().iterator ();
942                      ps.hasNext (); )
943                 {
944                     final Map.Entry JavaDoc entry = (Map.Entry JavaDoc) ps.next ();
945                     final String JavaDoc pattern = (String JavaDoc) entry.getKey ();
946                     
947                     if (name.startsWith (pattern))
948                     {
949                         if (prefixout != null) prefixout [0] = pattern;
950                         result = (OptDef) entry.getValue ();
951                         break;
952                     }
953                 }
954             }
955             
956             return result;
957         }
958         
959         Iterator JavaDoc /* OptDef */ getOptDefs ()
960         {
961             return m_optdefs.iterator ();
962         }
963         
964         OptDef getPatternOptDefs (final String JavaDoc pattern) // returns null if no such pattern is defined
965
{
966             if (pattern == null) throw new IllegalArgumentException JavaDoc ("null input: pattern");
967             
968             return (OptDef) m_patternOptDefMap.get (pattern);
969         }
970         
971         Set JavaDoc /* String(canonical name) */ getRequiredOpts ()
972         {
973             return m_requiredOpts;
974         }
975         
976         OptDef getUsageOptDef ()
977         {
978             return m_usageOptDef;
979         }
980         
981         void addOptDef (final OptDef optdef)
982         {
983             if (optdef == null) throw new IllegalArgumentException JavaDoc ("null input: optdef");
984             
985             final Map JavaDoc map = optdef.isPattern () ? m_patternOptDefMap : m_optdefMap;
986             final String JavaDoc [] names = optdef.getNames ();
987             
988             for (int n = 0; n < names.length; ++ n)
989             {
990                 if (map.containsKey (names [n]))
991                     throw new IllegalArgumentException JavaDoc ("duplicate option name [" + names [n] + "]");
992                     
993                 map.put (names [n], optdef);
994             }
995             
996             m_optdefs.add (optdef);
997             
998             if (optdef.isRequired ())
999                 m_requiredOpts.add (optdef.getCanonicalName ());
1000            
1001            if (optdef.isUsage ())
1002            {
1003                if (m_usageOptDef != null)
1004                    throw new IllegalArgumentException JavaDoc ("usage optdef set already");
1005                    
1006                m_usageOptDef = optdef;
1007            }
1008        }
1009        
1010        
1011        final List JavaDoc /* OptDef */ m_optdefs; // keeps the addition order
1012
final Map JavaDoc /* String(name)->OptDef */ m_optdefMap;
1013        final Set JavaDoc /* String(canonical name) */ m_requiredOpts;
1014        final Map JavaDoc /* String(pattern name)->OptDef */ m_patternOptDefMap;
1015        private OptDef m_usageOptDef;
1016        
1017    } // end of nested class
1018

1019  
1020    static final class MetadataParser
1021    {
1022        /*
1023         * metadata := ( optdef )* <EOF>
1024         *
1025         * optdef := optnamelist ":" optmetadata ";"
1026         * optnamelist := namelist
1027         * optmetadata :=
1028         * ("optional" | "required" )
1029         * [ "," "mergeable" ]
1030         * [ "," "detailedonly" ]
1031         * [ "," "pattern" ]
1032         * "," "values" ":" cardinality
1033         * [ "," name ]
1034         * [ "," "requires" "{" namelist "}" ]
1035         * [ "," "notwith" "{" namelist "}" ]
1036         * "," text
1037         * cardinality := "0" | "1" | "?"
1038         * namelist := name ( "," name )*
1039         * name := <single quoted string>
1040         * text := <double quoted string>
1041         */

1042         OptDef [] parse (final Reader JavaDoc in)
1043         {
1044             if (in == null) throw new IllegalArgumentException JavaDoc ("null input: in");
1045             m_in = in;
1046             
1047             nextChar ();
1048             nextToken ();
1049             
1050             while (m_token != Token.EOF)
1051             {
1052                 if (m_opts == null) m_opts = new ArrayList JavaDoc ();
1053                 m_opts.add (optdef ());
1054             }
1055             
1056             final OptDef [] result;
1057             
1058             if ((m_opts == null) || (m_opts.size () == 0))
1059                result = EMPTY_OPTDEF_ARRAY;
1060             else
1061             {
1062                 result = new OptDef [m_opts.size ()];
1063                 m_opts.toArray (result);
1064             }
1065             
1066             m_in = null;
1067             m_opts = null;
1068             
1069             return result;
1070         }
1071         
1072         OptDef optdef ()
1073         {
1074             final OptDef optdef = new OptDef (false);
1075             
1076             optdef.setNames (optnamelist ());
1077             accept (Token.COLON_ID);
1078             optmetadata (optdef);
1079             accept (Token.SEMICOLON_ID);
1080             
1081             return optdef;
1082         }
1083         
1084         String JavaDoc [] optnamelist ()
1085         {
1086             return namelist ();
1087         }
1088         
1089         void optmetadata (final OptDef optdef)
1090         {
1091             switch (m_token.getID ())
1092             {
1093                 case Token.REQUIRED_ID:
1094                 {
1095                     accept ();
1096                     optdef.setRequired (true);
1097                 }
1098                 break;
1099                 
1100                 case Token.OPTIONAL_ID:
1101                 {
1102                     accept ();
1103                     optdef.setRequired (false);
1104                 }
1105                 break;
1106                 
1107                 default:
1108                    throw new IllegalArgumentException JavaDoc ("parse error: invalid token " + m_token + ", expected " + Token.REQUIRED + " or " + Token.OPTIONAL);
1109                 
1110             } // end of switch
1111

1112             accept (Token.COMMA_ID);
1113             
1114             if (m_token.getID () == Token.MERGEABLE_ID)
1115             {
1116                 accept ();
1117                 optdef.setMergeable (true);
1118                 
1119                 accept (Token.COMMA_ID);
1120             }
1121             
1122             if (m_token.getID () == Token.DETAILEDONLY_ID)
1123             {
1124                 accept ();
1125                 optdef.setDetailedOnly (true);
1126                 
1127                 accept (Token.COMMA_ID);
1128             }
1129             
1130             if (m_token.getID () == Token.PATTERN_ID)
1131             {
1132                 accept ();
1133                 optdef.setPattern (true);
1134                 
1135                 accept (Token.COMMA_ID);
1136             }
1137             
1138             accept (Token.VALUES_ID);
1139             accept (Token.COLON_ID);
1140             optdef.setValueCardinality (cardinality ());
1141             
1142             accept (Token.COMMA_ID);
1143             if (m_token.getID () == Token.STRING_ID)
1144             {
1145                 optdef.setValueMnemonic (m_token.getValue ());
1146                 accept ();
1147                 
1148                 accept (Token.COMMA_ID);
1149             }
1150             
1151             if (m_token.getID () == Token.REQUIRES_ID)
1152             {
1153                 accept ();
1154                 
1155                 accept (Token.LBRACKET_ID);
1156                 optdef.setRequiresSet (namelist ());
1157                 accept (Token.RBRACKET_ID);
1158                 
1159                 accept (Token.COMMA_ID);
1160             }
1161             
1162             if (m_token.getID () == Token.EXCLUDES_ID)
1163             {
1164                 accept ();
1165                 
1166                 accept (Token.LBRACKET_ID);
1167                 optdef.setExcludesSet (namelist ());
1168                 accept (Token.RBRACKET_ID);
1169                 
1170                 accept (Token.COMMA_ID);
1171             }
1172             
1173             optdef.setDescription (accept (Token.TEXT_ID).getValue ());
1174         }
1175         
1176         int [] cardinality ()
1177         {
1178             final Token result = accept (Token.CARD_ID);
1179             
1180             if ("0".equals (result.getValue ()))
1181                return OptDef.C_ZERO;
1182             else if ("1".equals (result.getValue ()))
1183                return OptDef.C_ONE;
1184             else // ?
1185
return OptDef.C_ZERO_OR_ONE;
1186         }
1187         
1188         String JavaDoc [] namelist ()
1189         {
1190             final List JavaDoc _result = new ArrayList JavaDoc ();
1191             
1192             _result.add (accept (Token.STRING_ID).getValue ());
1193             while (m_token.getID () == Token.COMMA_ID)
1194             {
1195                 accept ();
1196                 _result.add (accept (Token.STRING_ID).getValue ());
1197             }
1198             
1199             final String JavaDoc [] result = new String JavaDoc [_result.size ()];
1200             _result.toArray (result);
1201             
1202             return result;
1203         }
1204         
1205         
1206         Token accept ()
1207         {
1208             final Token current = m_token;
1209             nextToken ();
1210             
1211             return current;
1212         }
1213         
1214         Token accept (final int tokenID)
1215         {
1216             final Token current = m_token;
1217             
1218             if (m_token.getID () == tokenID)
1219                nextToken ();
1220             else
1221                throw new IllegalArgumentException JavaDoc ("parse error: invalid token [" + m_token + "], expected type [" + tokenID + "]");
1222                
1223             return current;
1224         }
1225         
1226         // "scanner":
1227

1228         void nextToken ()
1229         {
1230             consumeWS ();
1231             
1232             switch (m_currentChar)
1233             {
1234                 case -1: m_token = Token.EOF; break;
1235                 
1236                 case ':':
1237                 {
1238                     nextChar ();
1239                     m_token = Token.COLON;
1240                 }
1241                 break;
1242                 
1243                 case ';':
1244                 {
1245                     nextChar ();
1246                     m_token = Token.SEMICOLON;
1247                 }
1248                 break;
1249                 
1250                 case ',':
1251                 {
1252                     nextChar ();
1253                     m_token = Token.COMMA;
1254                 }
1255                 break;
1256                 
1257                 case '{':
1258                 {
1259                     nextChar ();
1260                     m_token = Token.LBRACKET;
1261                 }
1262                 break;
1263                 
1264                 case '}':
1265                 {
1266                     nextChar ();
1267                     m_token = Token.RBRACKET;
1268                 }
1269                 break;
1270                 
1271                 case '0':
1272                 {
1273                     nextChar ();
1274                     m_token = new Token (Token.CARD_ID, "0");
1275                 }
1276                 break;
1277                 
1278                 case '1':
1279                 {
1280                     nextChar ();
1281                     m_token = new Token (Token.CARD_ID, "1");
1282                 }
1283                 break;
1284                 
1285                 case '?':
1286                 {
1287                     nextChar ();
1288                     m_token = new Token (Token.CARD_ID, "?");
1289                 }
1290                 break;
1291                                 
1292                 case '\'':
1293                 {
1294                     final StringBuffer JavaDoc value = new StringBuffer JavaDoc ();
1295                     
1296                     nextChar ();
1297                     while (m_currentChar != '\'')
1298                     {
1299                         value.append ((char) m_currentChar);
1300                         nextChar ();
1301                     }
1302                     nextChar ();
1303                     
1304                     m_token = new Token (Token.STRING_ID, value.toString ());
1305                 }
1306                 break;
1307                 
1308                 case '\"':
1309                 {
1310                     final StringBuffer JavaDoc value = new StringBuffer JavaDoc ();
1311                     
1312                     nextChar ();
1313                     while (m_currentChar != '\"')
1314                     {
1315                         value.append ((char) m_currentChar);
1316                         nextChar ();
1317                     }
1318                     nextChar ();
1319                     
1320                     m_token = new Token (Token.TEXT_ID, value.toString ());
1321                 }
1322                 break;
1323                 
1324                 default:
1325                 {
1326                     final StringBuffer JavaDoc value = new StringBuffer JavaDoc ();
1327                     
1328                     while (Character.isLetter ((char) m_currentChar))
1329                     {
1330                         value.append ((char) m_currentChar);
1331                         nextChar ();
1332                     }
1333                     
1334                     final Token token = (Token) KEYWORDS.get (value.toString ());
1335                     if (token == null)
1336                        throw new IllegalArgumentException JavaDoc ("parse error: unrecognized keyword [" + value + "]");
1337                     
1338                     m_token = token;
1339                 }
1340                                  
1341             } // end of switch
1342
}
1343         
1344         
1345         private void consumeWS ()
1346         {
1347             if (m_currentChar == -1)
1348                return;
1349             else
1350             {
1351                 while (Character.isWhitespace ((char) m_currentChar))
1352                 {
1353                    nextChar ();
1354                 }
1355             }
1356
1357             // TODO: #-comments
1358
}
1359         
1360         private void nextChar ()
1361         {
1362             try
1363             {
1364                m_currentChar = m_in.read ();
1365             }
1366             catch (IOException JavaDoc ioe)
1367             {
1368                 throw new RuntimeException JavaDoc ("I/O error while parsing: " + ioe);
1369             }
1370         }
1371         
1372         
1373         private Reader JavaDoc m_in;
1374         private List JavaDoc m_opts;
1375         
1376         private Token m_token;
1377         private int m_currentChar;
1378         
1379         private static final Map JavaDoc KEYWORDS;
1380         
1381         private static final OptDef [] EMPTY_OPTDEF_ARRAY = new OptDef [0];
1382         
1383         static
1384         {
1385             KEYWORDS = new HashMap JavaDoc (17);
1386             
1387             KEYWORDS.put (Token.OPTIONAL.getValue (), Token.OPTIONAL);
1388             KEYWORDS.put (Token.REQUIRED.getValue (), Token.REQUIRED);
1389             KEYWORDS.put (Token.VALUES.getValue (), Token.VALUES);
1390             KEYWORDS.put (Token.REQUIRES.getValue (), Token.REQUIRES);
1391             KEYWORDS.put (Token.EXCLUDES.getValue (), Token.EXCLUDES);
1392             KEYWORDS.put (Token.MERGEABLE.getValue (), Token.MERGEABLE);
1393             KEYWORDS.put (Token.DETAILEDONLY.getValue (), Token.DETAILEDONLY);
1394             KEYWORDS.put (Token.PATTERN.getValue (), Token.PATTERN);
1395         }
1396        
1397    } // end of nested class
1398

1399    
1400    OptsParser (final String JavaDoc metadataResourceName, final ClassLoader JavaDoc loader, final String JavaDoc [] usageOpts)
1401    {
1402        this (metadataResourceName, loader, null, usageOpts);
1403    }
1404    
1405    OptsParser (final String JavaDoc metadataResourceName, final ClassLoader JavaDoc loader, final String JavaDoc msgPrefix, final String JavaDoc [] usageOpts)
1406    {
1407        if (metadataResourceName == null) throw new IllegalArgumentException JavaDoc ("null input: metadataResourceName");
1408        
1409        m_msgPrefix = msgPrefix;
1410        
1411        InputStream JavaDoc in = null;
1412        try
1413        {
1414            in = ResourceLoader.getResourceAsStream (metadataResourceName, loader);
1415            if (in == null)
1416                throw new IllegalArgumentException JavaDoc ("resource [" + metadataResourceName + "] could not be loaded via [" + loader + "]");
1417            
1418            // TODO: encoding
1419
final Reader JavaDoc rin = new InputStreamReader JavaDoc (in);
1420            
1421            m_metadata = parseOptDefMetadata (rin, usageOpts);
1422        }
1423        finally
1424        {
1425            if (in != null) try { in.close (); } catch (IOException JavaDoc ignore) {}
1426        }
1427    }
1428    
1429    // private: ...............................................................
1430

1431
1432    private static final class Token
1433    {
1434        Token (final int ID, final String JavaDoc value)
1435        {
1436            if (value == null) throw new IllegalArgumentException JavaDoc ("null input: value");
1437            
1438            m_ID = ID;
1439            m_value = value;
1440        }
1441        
1442        int getID ()
1443        {
1444            return m_ID;
1445        }
1446        
1447        String JavaDoc getValue ()
1448        {
1449            return m_value;
1450        }
1451        
1452        public String JavaDoc toString ()
1453        {
1454            return m_ID + ": [" + m_value + "]";
1455        }
1456        
1457        
1458        static final int EOF_ID = 0;
1459        static final int STRING_ID = 1;
1460        static final int COLON_ID = 2;
1461        static final int SEMICOLON_ID = 3;
1462        static final int COMMA_ID = 4;
1463        static final int LBRACKET_ID = 5;
1464        static final int RBRACKET_ID = 6;
1465        static final int OPTIONAL_ID = 7;
1466        static final int REQUIRED_ID = 8;
1467        static final int CARD_ID = 9;
1468        static final int VALUES_ID = 10;
1469        static final int TEXT_ID = 11;
1470        static final int REQUIRES_ID = 12;
1471        static final int EXCLUDES_ID = 13;
1472        static final int MERGEABLE_ID = 14;
1473        static final int DETAILEDONLY_ID = 15;
1474        static final int PATTERN_ID = 16;
1475        
1476        static final Token EOF = new Token (EOF_ID, "<EOF>");
1477        static final Token COLON = new Token (COLON_ID, ":");
1478        static final Token SEMICOLON = new Token (SEMICOLON_ID, ";");
1479        static final Token COMMA = new Token (COMMA_ID, ",");
1480        static final Token LBRACKET = new Token (LBRACKET_ID, "{");
1481        static final Token RBRACKET = new Token (RBRACKET_ID, "}");
1482        static final Token OPTIONAL = new Token (OPTIONAL_ID, "optional");
1483        static final Token REQUIRED = new Token (REQUIRED_ID, "required");
1484        static final Token VALUES = new Token (VALUES_ID, "values");
1485        static final Token REQUIRES = new Token (REQUIRES_ID, "requires");
1486        static final Token EXCLUDES = new Token (EXCLUDES_ID, "excludes");
1487        static final Token MERGEABLE = new Token (MERGEABLE_ID, "mergeable");
1488        static final Token DETAILEDONLY = new Token (DETAILEDONLY_ID, "detailedonly");
1489        static final Token PATTERN = new Token (PATTERN_ID, "pattern");
1490        
1491        private final int m_ID;
1492        private final String JavaDoc m_value;
1493        
1494    } // end of nested class
1495

1496    
1497    private static OptDefMetadata parseOptDefMetadata (final Reader JavaDoc in, final String JavaDoc [] usageOpts)
1498    {
1499        final MetadataParser parser = new MetadataParser ();
1500        final OptDef [] optdefs = parser.parse (in);
1501        
1502        // validate:
1503

1504// for (int o = 0; o < optdefs.length; ++ o)
1505
// {
1506
// final OptDef optdef = optdefs [o];
1507
// final int [] cardinality = optdef.getValueCardinality ();
1508
//
1509
// if (optdef.isMergeable ())
1510
// {
1511
// if ((cardinality [1] != 0) && (cardinality [1] != Integer.MAX_VALUE))
1512
// throw new IllegalArgumentException ("option [" + optdef.getCanonicalName () + "] is mergeable and can only specify {0, +inf} for max value cardinality: " + cardinality [1]);
1513
// }
1514
// }
1515

1516        final OptDefMetadata result = new OptDefMetadata ();
1517        for (int o = 0; o < optdefs.length; ++ o)
1518        {
1519            result.addOptDef (optdefs [o]);
1520        }
1521        
1522        // add usage opts:
1523
if (usageOpts != null)
1524        {
1525            final OptDef usage = new OptDef (true);
1526            
1527            usage.setNames (usageOpts);
1528            usage.setDescription ("display usage information");
1529            usage.setValueCardinality (OptDef.C_ZERO);
1530            usage.setRequired (false);
1531            usage.setDetailedOnly (false);
1532            usage.setMergeable (false);
1533            
1534            result.addOptDef (usage);
1535        }
1536        
1537        // TODO: fix this to be pattern-savvy
1538

1539        for (int o = 0; o < optdefs.length; ++ o)
1540        {
1541            final OptDef optdef = optdefs [o];
1542            
1543            final String JavaDoc [] requires = optdef.getRequiresSet ();
1544            if (requires != null)
1545            {
1546                for (int r = 0; r < requires.length; ++ r)
1547                {
1548                    final OptDef ropt = result.getOptDef (requires [r], null);
1549                    if (ropt == null)
1550                        throw new IllegalArgumentException JavaDoc ("option [" + optdef.getCanonicalName () + "] specifies an unknown option [" + requires [r] + "] in its \'requires\' set");
1551                    
1552                    if (ropt == optdef)
1553                        throw new IllegalArgumentException JavaDoc ("option [" + optdef.getCanonicalName () + "] specifies itself in its \'requires\' set");
1554                }
1555            }
1556            
1557            final String JavaDoc [] excludes = optdef.getExcludesSet ();
1558            if (excludes != null)
1559            {
1560                for (int x = 0; x < excludes.length; ++ x)
1561                {
1562                    final OptDef xopt = result.getOptDef (excludes [x], null);
1563                    if (xopt == null)
1564                        throw new IllegalArgumentException JavaDoc ("option [" + optdef.getCanonicalName () + "] specifies an unknown option [" + excludes [x] + "] in its \'excludes\' set");
1565                    
1566                    if (xopt.isRequired ())
1567                        throw new IllegalArgumentException JavaDoc ("option [" + optdef.getCanonicalName () + "] specifies a required option [" + excludes [x] + "] in its \'excludes\' set");
1568                    
1569                    if (xopt == optdef)
1570                        throw new IllegalArgumentException JavaDoc ("option [" + optdef.getCanonicalName () + "] specifies itself in its \'excludes\' set");
1571                }
1572            }
1573        }
1574        
1575        return result;
1576    }
1577    
1578    private String JavaDoc formatMessage (final String JavaDoc msg)
1579    {
1580        if (m_msgPrefix == null) return msg;
1581        else
1582        {
1583            return m_msgPrefix.concat (msg);
1584        }
1585    }
1586    
1587    
1588    private final String JavaDoc m_msgPrefix;
1589    private final OptDefMetadata m_metadata;
1590    
1591    private static final int CANONICAL_OPT_PREFIX = 1; // indexes into OPT_PREFIXES
1592
private static final String JavaDoc [] OPT_PREFIXES = new String JavaDoc [] {"--", "-"}; // HACK: these must appear in decreasing length order
1593
private static final char [] OPT_VALUE_SEPARATORS = new char [] {':', '='};
1594    
1595    private static final int STATE_OPT = 0, STATE_OPT_VALUE = 1, STATE_FREE_ARGS = 2, STATE_ERROR = 3;
1596    
1597} // end of class
1598
// ----------------------------------------------------------------------------
Popular Tags