KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > axis > utils > CLArgsParser


1 /*
2  * Copyright (C) The Apache Software Foundation. All rights reserved.
3  *
4  * This software is published under the terms of the Apache Software License
5  * version 1.1, a copy of which has been included with this distribution in
6  * the docs/licenses/apache-1.1.txt file.
7  */

8 // This file is pulled from package org.apache.avalon.excalibur.cli Excalibur
9
// version 4.1 (Jan 30, 2002). Only the package name has been changed.
10
package org.jboss.axis.utils;
11
12 import java.text.ParseException JavaDoc;
13 import java.util.Hashtable JavaDoc;
14 import java.util.Vector JavaDoc;
15
16 /**
17  * Parser for command line arguments.
18  * <p/>
19  * This parses command lines according to the standard (?) of
20  * GNU utilities.
21  * <p/>
22  * Note: This is still used in 1.1 libraries so do not add 1.2+ dependencies.
23  *
24  * @author <a HREF="mailto:peter@apache.org">Peter Donald</a>
25  * @since 4.0
26  */

27 public final class CLArgsParser
28 {
29    private static final int STATE_NORMAL = 0;
30    private static final int STATE_REQUIRE_2ARGS = 1;
31    private static final int STATE_REQUIRE_ARG = 2;
32    private static final int STATE_OPTIONAL_ARG = 3;
33    private static final int STATE_NO_OPTIONS = 4;
34    private static final int STATE_OPTION_MODE = 5;
35
36    private static final int TOKEN_SEPARATOR = 0;
37    private static final int TOKEN_STRING = 1;
38
39
40    private static final char[] ARG2_SEPARATORS =
41            new char[]{(char)0, '=', '-'};
42
43    private static final char[] ARG_SEPARATORS =
44            new char[]{(char)0, '='};
45
46    private static final char[] NULL_SEPARATORS =
47            new char[]{(char)0};
48
49    private final CLOptionDescriptor[] m_optionDescriptors;
50    private final Vector JavaDoc m_options;
51    private Hashtable JavaDoc m_optionIndex;
52    private final ParserControl m_control;
53
54    private String JavaDoc m_errorMessage;
55    private String JavaDoc[] m_unparsedArgs = new String JavaDoc[]{};
56
57    //variables used while parsing options.
58
private char ch;
59    private String JavaDoc[] args;
60    private boolean isLong;
61    private int argIndex;
62    private int stringIndex;
63    private int stringLength;
64
65    //cached character == Integer.MAX_VALUE when invalid
66
private static final int INVALID = Integer.MAX_VALUE;
67    private int m_lastChar = INVALID;
68
69    private int m_lastOptionId;
70    private CLOption m_option;
71    private int m_state = STATE_NORMAL;
72
73    public final String JavaDoc[] getUnparsedArgs()
74    {
75       return m_unparsedArgs;
76    }
77
78    /**
79     * Retrieve a list of options that were parsed from command list.
80     *
81     * @return the list of options
82     */

83    public final Vector JavaDoc getArguments()
84    {
85       //System.out.println( "Arguments: " + m_options );
86
return m_options;
87    }
88
89    /**
90     * Retrieve the {@link CLOption} with specified id, or
91     * <code>null</code> if no command line option is found.
92     *
93     * @param id the command line option id
94     * @return the {@link CLOption} with the specified id, or
95     * <code>null</code> if no CLOption is found.
96     * @see CLOption
97     */

98    public final CLOption getArgumentById(final int id)
99    {
100       return (CLOption)m_optionIndex.get(new Integer JavaDoc(id));
101    }
102
103    /**
104     * Retrieve the {@link CLOption} with specified name, or
105     * <code>null</code> if no command line option is found.
106     *
107     * @param name the command line option name
108     * @return the {@link CLOption} with the specified name, or
109     * <code>null</code> if no CLOption is found.
110     * @see CLOption
111     */

112    public final CLOption getArgumentByName(final String JavaDoc name)
113    {
114       return (CLOption)m_optionIndex.get(name);
115    }
116
117    /**
118     * Get Descriptor for option id.
119     *
120     * @param id the id
121     * @return the descriptor
122     */

123    private final CLOptionDescriptor getDescriptorFor(final int id)
124    {
125       for (int i = 0; i < m_optionDescriptors.length; i++)
126       {
127          if (m_optionDescriptors[i].getId() == id)
128          {
129             return m_optionDescriptors[i];
130          }
131       }
132
133       return null;
134    }
135
136    /**
137     * Retrieve a descriptor by name.
138     *
139     * @param name the name
140     * @return the descriptor
141     */

142    private final CLOptionDescriptor getDescriptorFor(final String JavaDoc name)
143    {
144       for (int i = 0; i < m_optionDescriptors.length; i++)
145       {
146          if (m_optionDescriptors[i].getName().equals(name))
147          {
148             return m_optionDescriptors[i];
149          }
150       }
151
152       return null;
153    }
154
155    /**
156     * Retrieve an error message that occured during parsing if one existed.
157     *
158     * @return the error string
159     */

160    public final String JavaDoc getErrorString()
161    {
162       //System.out.println( "ErrorString: " + m_errorMessage );
163
return m_errorMessage;
164    }
165
166    /**
167     * Require state to be placed in for option.
168     *
169     * @param descriptor the Option Descriptor
170     * @return the state
171     */

172    private final int getStateFor(final CLOptionDescriptor descriptor)
173    {
174       int flags = descriptor.getFlags();
175       if ((flags & CLOptionDescriptor.ARGUMENTS_REQUIRED_2) ==
176               CLOptionDescriptor.ARGUMENTS_REQUIRED_2)
177       {
178          return STATE_REQUIRE_2ARGS;
179       }
180       else if ((flags & CLOptionDescriptor.ARGUMENT_REQUIRED) ==
181               CLOptionDescriptor.ARGUMENT_REQUIRED)
182       {
183          return STATE_REQUIRE_ARG;
184       }
185       else if ((flags & CLOptionDescriptor.ARGUMENT_OPTIONAL) ==
186               CLOptionDescriptor.ARGUMENT_OPTIONAL)
187       {
188          return STATE_OPTIONAL_ARG;
189       }
190       else
191       {
192          return STATE_NORMAL;
193       }
194    }
195
196    /**
197     * Create a parser that can deal with options and parses certain args.
198     *
199     * @param args[] the args, typically that passed to the
200     * <code>public static void main(String[] args)</code> method.
201     * @param optionDescriptors[] the option descriptors
202     */

203    public CLArgsParser(final String JavaDoc[] args,
204                        final CLOptionDescriptor[] optionDescriptors,
205                        final ParserControl control)
206    {
207       m_optionDescriptors = optionDescriptors;
208       m_control = control;
209       m_options = new Vector JavaDoc();
210       this.args = args;
211
212       try
213       {
214          parse();
215          checkIncompatibilities(m_options);
216          buildOptionIndex();
217       }
218       catch (final ParseException JavaDoc pe)
219       {
220          m_errorMessage = pe.getMessage();
221       }
222
223       //System.out.println( "Built : " + m_options );
224
//System.out.println( "From : " + Arrays.asList( args ) );
225
}
226
227    /**
228     * Check for duplicates of an option.
229     * It is an error to have duplicates unless appropriate flags is set in descriptor.
230     *
231     * @param arguments the arguments
232     */

233    private final void checkIncompatibilities(final Vector JavaDoc arguments)
234            throws ParseException JavaDoc
235    {
236       final int size = arguments.size();
237
238       for (int i = 0; i < size; i++)
239       {
240          final CLOption option = (CLOption)arguments.elementAt(i);
241          final int id = option.getId();
242          final CLOptionDescriptor descriptor = getDescriptorFor(id);
243
244          //this occurs when id == 0 and user has not supplied a descriptor
245
//for arguments
246
if (null == descriptor)
247          {
248             continue;
249          }
250
251          final int[] incompatible = descriptor.getIncompatible();
252
253          checkIncompatible(arguments, incompatible, i);
254       }
255    }
256
257    private final void checkIncompatible(final Vector JavaDoc arguments,
258                                         final int[] incompatible,
259                                         final int original)
260            throws ParseException JavaDoc
261    {
262       final int size = arguments.size();
263
264       for (int i = 0; i < size; i++)
265       {
266          if (original == i)
267          {
268             continue;
269          }
270
271          final CLOption option = (CLOption)arguments.elementAt(i);
272          final int id = option.getId();
273 // final CLOptionDescriptor descriptor = getDescriptorFor( id );
274

275          for (int j = 0; j < incompatible.length; j++)
276          {
277             if (id == incompatible[j])
278             {
279                final CLOption originalOption = (CLOption)arguments.elementAt(original);
280                final int originalId = originalOption.getId();
281
282                String JavaDoc message = null;
283
284                if (id == originalId)
285                {
286                   message =
287                           "Duplicate options for " + describeDualOption(originalId) +
288                           " found.";
289                }
290                else
291                {
292                   message = "Incompatible options -" +
293                           describeDualOption(id) + " and " +
294                           describeDualOption(originalId) + " found.";
295                }
296                throw new ParseException JavaDoc(message, 0);
297             }
298          }
299       }
300    }
301
302    private final String JavaDoc describeDualOption(final int id)
303    {
304       final CLOptionDescriptor descriptor = getDescriptorFor(id);
305       if (null == descriptor)
306       {
307          return "<parameter>";
308       }
309       else
310       {
311          final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
312          boolean hasCharOption = false;
313
314          if (Character.isLetter((char)id))
315          {
316             sb.append('-');
317             sb.append((char)id);
318             hasCharOption = true;
319          }
320
321          final String JavaDoc longOption = descriptor.getName();
322          if (null != longOption)
323          {
324             if (hasCharOption)
325             {
326                sb.append('/');
327             }
328             sb.append("--");
329             sb.append(longOption);
330          }
331
332          return sb.toString();
333       }
334    }
335
336    /**
337     * Create a parser that deals with options and parses certain args.
338     *
339     * @param args[] the args
340     * @param optionDescriptors[] the option descriptors
341     */

342    public CLArgsParser(final String JavaDoc[] args,
343                        final CLOptionDescriptor[] optionDescriptors)
344    {
345       this(args, optionDescriptors, null);
346    }
347
348    /**
349     * Create a string array that is subset of input array.
350     * The sub-array should start at array entry indicated by index. That array element
351     * should only include characters from charIndex onwards.
352     *
353     * @param array[] the original array
354     * @param index the cut-point in array
355     * @param charIndex the cut-point in element of array
356     * @return the result array
357     */

358    private final String JavaDoc[] subArray(final String JavaDoc[] array,
359                                    final int index,
360                                    final int charIndex)
361    {
362       final int remaining = array.length - index;
363       final String JavaDoc[] result = new String JavaDoc[remaining];
364
365       if (remaining > 1)
366       {
367          System.arraycopy(array, index + 1, result, 1, remaining - 1);
368       }
369
370       result[0] = array[index].substring(charIndex - 1);
371
372       return result;
373    }
374
375    /**
376     * Actually parse arguments
377     *
378     * @param args[] arguments
379     */

380    private final void parse()
381            throws ParseException JavaDoc
382    {
383       if (0 == args.length)
384       {
385          return;
386       }
387
388       stringLength = args[argIndex].length();
389
390       //ch = peekAtChar();
391

392       while (true)
393       {
394          ch = peekAtChar();
395
396          //System.out.println( "Pre State=" + m_state );
397
//System.out.println( "Pre Char=" + (char)ch + "/" + (int)ch );
398

399          if (argIndex >= args.length)
400          {
401             break;
402          }
403
404          if (null != m_control && m_control.isFinished(m_lastOptionId))
405          {
406             //this may need mangling due to peeks
407
m_unparsedArgs = subArray(args, argIndex, stringIndex);
408             return;
409          }
410
411          //System.out.println( "State=" + m_state );
412
//System.out.println( "Char=" + (char)ch + "/" + (int)ch );
413

414          if (STATE_OPTION_MODE == m_state)
415          {
416             //if get to an arg barrier then return to normal mode
417
//else continue accumulating options
418
if (0 == ch)
419             {
420                getChar(); //strip the null
421
m_state = STATE_NORMAL;
422             }
423             else
424             {
425                parseShortOption();
426             }
427          }
428          else if (STATE_NORMAL == m_state)
429          {
430             parseNormal();
431          }
432          else if (STATE_NO_OPTIONS == m_state)
433          {
434             //should never get to here when stringIndex != 0
435
addOption(new CLOption(args[argIndex++]));
436          }
437          else if (STATE_OPTIONAL_ARG == m_state && '-' == ch)
438          {
439             m_state = STATE_NORMAL;
440             addOption(m_option);
441          }
442          else
443          {
444             parseArguments();
445          }
446       }
447
448       if (m_option != null)
449       {
450          if (STATE_OPTIONAL_ARG == m_state)
451          {
452             m_options.addElement(m_option);
453          }
454          else if (STATE_REQUIRE_ARG == m_state)
455          {
456             final CLOptionDescriptor descriptor = getDescriptorFor(m_option.getId());
457             final String JavaDoc message =
458                     "Missing argument to option " + getOptionDescription(descriptor);
459             throw new ParseException JavaDoc(message, 0);
460          }
461          else if (STATE_REQUIRE_2ARGS == m_state)
462          {
463             if (1 == m_option.getArgumentCount())
464             {
465                m_option.addArgument("");
466                m_options.addElement(m_option);
467             }
468             else
469             {
470                final CLOptionDescriptor descriptor = getDescriptorFor(m_option.getId());
471                final String JavaDoc message =
472                        "Missing argument to option " + getOptionDescription(descriptor);
473                throw new ParseException JavaDoc(message, 0);
474             }
475          }
476          else
477          {
478             throw new ParseException JavaDoc("IllegalState " + m_state + ": " + m_option, 0);
479          }
480       }
481    }
482
483    private final String JavaDoc getOptionDescription(final CLOptionDescriptor descriptor)
484    {
485       if (isLong)
486       {
487          return "--" + descriptor.getName();
488       }
489       else
490       {
491          return "-" + (char)descriptor.getId();
492       }
493    }
494
495    private final char peekAtChar()
496    {
497       if (INVALID == m_lastChar)
498       {
499          m_lastChar = readChar();
500       }
501       return (char)m_lastChar;
502    }
503
504    private final char getChar()
505    {
506       if (INVALID != m_lastChar)
507       {
508          final char result = (char)m_lastChar;
509          m_lastChar = INVALID;
510          return result;
511       }
512       else
513       {
514          return readChar();
515       }
516    }
517
518    private final char readChar()
519    {
520       if (stringIndex >= stringLength)
521       {
522          argIndex++;
523          stringIndex = 0;
524
525          if (argIndex < args.length)
526          {
527             stringLength = args[argIndex].length();
528          }
529          else
530          {
531             stringLength = 0;
532          }
533
534          return 0;
535       }
536
537       if (argIndex >= args.length)
538          return 0;
539
540       return args[argIndex].charAt(stringIndex++);
541    }
542
543    private final Token nextToken(final char[] separators)
544    {
545       ch = getChar();
546
547       if (isSeparator(ch, separators))
548       {
549          ch = getChar();
550          return new Token(TOKEN_SEPARATOR, null);
551       }
552
553       final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
554
555       do
556       {
557          sb.append(ch);
558          ch = getChar();
559       }
560       while (!isSeparator(ch, separators));
561
562       return new Token(TOKEN_STRING, sb.toString());
563    }
564
565    private final boolean isSeparator(final char ch, final char[] separators)
566    {
567       for (int i = 0; i < separators.length; i++)
568       {
569          if (ch == separators[i])
570          {
571             return true;
572          }
573       }
574
575       return false;
576    }
577
578    private final void addOption(final CLOption option)
579    {
580       m_options.addElement(option);
581       m_lastOptionId = option.getId();
582       m_option = null;
583    }
584
585    private final void parseOption(final CLOptionDescriptor descriptor,
586                                   final String JavaDoc optionString)
587            throws ParseException JavaDoc
588    {
589       if (null == descriptor)
590       {
591          throw new ParseException JavaDoc("Unknown option " + optionString, 0);
592       }
593
594       m_state = getStateFor(descriptor);
595       m_option = new CLOption(descriptor.getId());
596
597       if (STATE_NORMAL == m_state)
598       {
599          addOption(m_option);
600       }
601    }
602
603    private final void parseShortOption()
604            throws ParseException JavaDoc
605    {
606       ch = getChar();
607       final CLOptionDescriptor descriptor = getDescriptorFor(ch);
608       isLong = false;
609       parseOption(descriptor, "-" + ch);
610
611       if (STATE_NORMAL == m_state)
612       {
613          m_state = STATE_OPTION_MODE;
614       }
615    }
616
617    private final void parseArguments()
618            throws ParseException JavaDoc
619    {
620       if (STATE_REQUIRE_ARG == m_state)
621       {
622          if ('=' == ch || 0 == ch)
623          {
624             getChar();
625          }
626
627          final Token token = nextToken(NULL_SEPARATORS);
628          m_option.addArgument(token.getValue());
629
630          addOption(m_option);
631          m_state = STATE_NORMAL;
632       }
633       else if (STATE_OPTIONAL_ARG == m_state)
634       {
635          if ('-' == ch || 0 == ch)
636          {
637             getChar(); //consume stray character
638
addOption(m_option);
639             m_state = STATE_NORMAL;
640             return;
641          }
642
643          if ('=' == ch)
644          {
645             getChar();
646          }
647
648          final Token token = nextToken(NULL_SEPARATORS);
649          m_option.addArgument(token.getValue());
650
651          addOption(m_option);
652          m_state = STATE_NORMAL;
653       }
654       else if (STATE_REQUIRE_2ARGS == m_state)
655       {
656          if (0 == m_option.getArgumentCount())
657          {
658             final Token token = nextToken(ARG_SEPARATORS);
659
660             if (TOKEN_SEPARATOR == token.getType())
661             {
662                final CLOptionDescriptor descriptor = getDescriptorFor(m_option.getId());
663                final String JavaDoc message =
664                        "Unable to parse first argument for option " +
665                        getOptionDescription(descriptor);
666                throw new ParseException JavaDoc(message, 0);
667             }
668             else
669             {
670                m_option.addArgument(token.getValue());
671             }
672          }
673          else //2nd argument
674
{
675             final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
676
677             ch = getChar();
678             if ('-' == ch)
679             {
680                m_lastChar = ch;
681             }
682
683             while (!isSeparator(ch, ARG2_SEPARATORS))
684             {
685                sb.append(ch);
686                ch = getChar();
687             }
688
689             final String JavaDoc argument = sb.toString();
690
691             //System.out.println( "Arguement:" + argument );
692

693             m_option.addArgument(argument);
694             addOption(m_option);
695             m_option = null;
696             m_state = STATE_NORMAL;
697          }
698       }
699    }
700
701    /**
702     * Parse Options from Normal mode.
703     */

704    private final void parseNormal()
705            throws ParseException JavaDoc
706    {
707       if ('-' != ch)
708       {
709          //Parse the arguments that are not options
710
final String JavaDoc argument = nextToken(NULL_SEPARATORS).getValue();
711          addOption(new CLOption(argument));
712          m_state = STATE_NORMAL;
713       }
714       else
715       {
716          getChar(); // strip the -
717

718          if (0 == peekAtChar())
719          {
720             throw new ParseException JavaDoc("Malformed option -", 0);
721          }
722          else
723          {
724             ch = peekAtChar();
725
726             //if it is a short option then parse it else ...
727
if ('-' != ch)
728             {
729                parseShortOption();
730             }
731             else
732             {
733                getChar(); // strip the -
734
//-- sequence .. it can either mean a change of state
735
//to STATE_NO_OPTIONS or else a long option
736

737                if (0 == peekAtChar())
738                {
739                   getChar();
740                   m_state = STATE_NO_OPTIONS;
741                }
742                else
743                {
744                   //its a long option
745
final String JavaDoc optionName = nextToken(ARG_SEPARATORS).getValue();
746                   final CLOptionDescriptor descriptor = getDescriptorFor(optionName);
747                   isLong = true;
748                   parseOption(descriptor, "--" + optionName);
749                }
750             }
751          }
752       }
753    }
754
755    /**
756     * Build the m_optionIndex lookup map for the parsed options.
757     */

758    private final void buildOptionIndex()
759    {
760       m_optionIndex = new Hashtable JavaDoc(m_options.size() * 2);
761
762       for (int i = 0; i < m_options.size(); i++)
763       {
764          final CLOption option = (CLOption)m_options.get(i);
765          final CLOptionDescriptor optionDescriptor =
766                  getDescriptorFor(option.getId());
767
768          m_optionIndex.put(new Integer JavaDoc(option.getId()), option);
769
770          if (null != optionDescriptor)
771          {
772             m_optionIndex.put(optionDescriptor.getName(), option);
773          }
774       }
775    }
776 }
777
Popular Tags