KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright 2001-2004 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 // This file is pulled from package org.apache.avalon.excalibur.cli Excalibur
17
// version 4.1 (Jan 30, 2002). Only the package name has been changed.
18
package org.apache.axis.utils;
19
20 import java.text.ParseException JavaDoc;
21 import java.util.Hashtable JavaDoc;
22 import java.util.Vector JavaDoc;
23
24 /**
25  * Parser for command line arguments.
26  *
27  * This parses command lines according to the standard (?) of
28  * GNU utilities.
29  *
30  * Note: This is still used in 1.1 libraries so do not add 1.2+ dependencies.
31  *
32  * @author <a HREF="mailto:peter@apache.org">Peter Donald</a>
33  * @since 4.0
34  */

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

91     public final Vector JavaDoc getArguments()
92     {
93         //System.out.println( "Arguments: " + m_options );
94
return m_options;
95     }
96
97     /**
98      * Retrieve the {@link CLOption} with specified id, or
99      * <code>null</code> if no command line option is found.
100      *
101      * @param id the command line option id
102      * @return the {@link CLOption} with the specified id, or
103      * <code>null</code> if no CLOption is found.
104      * @see CLOption
105      */

106     public final CLOption getArgumentById( final int id )
107     {
108         return (CLOption)m_optionIndex.get( new Integer JavaDoc( id ) );
109     }
110
111     /**
112      * Retrieve the {@link CLOption} with specified name, or
113      * <code>null</code> if no command line option is found.
114      *
115      * @param name the command line option name
116      * @return the {@link CLOption} with the specified name, or
117      * <code>null</code> if no CLOption is found.
118      * @see CLOption
119      */

120     public final CLOption getArgumentByName( final String JavaDoc name)
121     {
122         return (CLOption)m_optionIndex.get( name );
123     }
124
125     /**
126      * Get Descriptor for option id.
127      *
128      * @param id the id
129      * @return the descriptor
130      */

131     private final CLOptionDescriptor getDescriptorFor( final int id )
132     {
133         for( int i = 0; i < m_optionDescriptors.length; i++ )
134         {
135             if( m_optionDescriptors[i].getId() == id )
136             {
137                 return m_optionDescriptors[i];
138             }
139         }
140
141         return null;
142     }
143
144     /**
145      * Retrieve a descriptor by name.
146      *
147      * @param name the name
148      * @return the descriptor
149      */

150     private final CLOptionDescriptor getDescriptorFor( final String JavaDoc name )
151     {
152         for( int i = 0; i < m_optionDescriptors.length; i++ )
153         {
154             if( m_optionDescriptors[i].getName().equals( name ) )
155             {
156                 return m_optionDescriptors[i];
157             }
158         }
159
160         return null;
161     }
162
163     /**
164      * Retrieve an error message that occured during parsing if one existed.
165      *
166      * @return the error string
167      */

168     public final String JavaDoc getErrorString()
169     {
170         //System.out.println( "ErrorString: " + m_errorMessage );
171
return m_errorMessage;
172     }
173
174     /**
175      * Require state to be placed in for option.
176      *
177      * @param descriptor the Option Descriptor
178      * @return the state
179      */

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

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

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

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

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

366     private final String JavaDoc[] subArray( final String JavaDoc[] array,
367                                final int index,
368                                final int charIndex )
369     {
370         final int remaining = array.length - index;
371         final String JavaDoc[] result = new String JavaDoc[ remaining ];
372
373         if( remaining > 1 )
374         {
375             System.arraycopy( array, index + 1, result, 1, remaining - 1 );
376         }
377
378         result[0] = array[ index ].substring( charIndex - 1 );
379
380         return result;
381     }
382
383     /**
384      * Actually parse arguments
385      *
386      * @param args[] arguments
387      */

388     private final void parse()
389         throws ParseException JavaDoc
390     {
391         if( 0 == args.length )
392         {
393             return;
394         }
395
396         stringLength = args[ argIndex ].length();
397
398         //ch = peekAtChar();
399

400         while( true )
401         {
402             ch = peekAtChar();
403
404             //System.out.println( "Pre State=" + m_state );
405
//System.out.println( "Pre Char=" + (char)ch + "/" + (int)ch );
406

407             if( argIndex >= args.length )
408             {
409                 break;
410             }
411
412             if( null != m_control && m_control.isFinished( m_lastOptionId ) )
413             {
414                 //this may need mangling due to peeks
415
m_unparsedArgs = subArray( args, argIndex, stringIndex );
416                 return;
417             }
418
419             //System.out.println( "State=" + m_state );
420
//System.out.println( "Char=" + (char)ch + "/" + (int)ch );
421

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

701                 m_option.addArgument( argument );
702                 addOption( m_option );
703                 m_option = null;
704                 m_state = STATE_NORMAL;
705             }
706         }
707     }
708
709     /**
710      * Parse Options from Normal mode.
711      */

712     private final void parseNormal()
713         throws ParseException JavaDoc
714     {
715         if( '-' != ch )
716         {
717             //Parse the arguments that are not options
718
final String JavaDoc argument = nextToken( NULL_SEPARATORS ).getValue();
719             addOption( new CLOption( argument ) );
720             m_state = STATE_NORMAL;
721         }
722         else
723         {
724             getChar(); // strip the -
725

726             if( 0 == peekAtChar() )
727             {
728                 throw new ParseException JavaDoc( "Malformed option -", 0 );
729             }
730             else
731             {
732                 ch = peekAtChar();
733
734                 //if it is a short option then parse it else ...
735
if( '-' != ch )
736                 {
737                     parseShortOption();
738                 }
739                 else
740                 {
741                     getChar(); // strip the -
742
//-- sequence .. it can either mean a change of state
743
//to STATE_NO_OPTIONS or else a long option
744

745                     if( 0 == peekAtChar() )
746                     {
747                         getChar();
748                         m_state = STATE_NO_OPTIONS;
749                     }
750                     else
751                     {
752                         //its a long option
753
final String JavaDoc optionName = nextToken( ARG_SEPARATORS ).getValue();
754                         final CLOptionDescriptor descriptor = getDescriptorFor( optionName );
755                         isLong = true;
756                         parseOption( descriptor, "--" + optionName );
757                     }
758                 }
759             }
760         }
761     }
762
763     /**
764      * Build the m_optionIndex lookup map for the parsed options.
765      */

766     private final void buildOptionIndex()
767     {
768         m_optionIndex = new Hashtable JavaDoc( m_options.size() * 2 );
769
770         for( int i = 0; i < m_options.size(); i++ )
771         {
772             final CLOption option = (CLOption)m_options.get( i );
773             final CLOptionDescriptor optionDescriptor =
774                 getDescriptorFor( option.getId() );
775
776             m_optionIndex.put( new Integer JavaDoc( option.getId() ), option );
777
778             if( null != optionDescriptor )
779             {
780                 m_optionIndex.put( optionDescriptor.getName(), option );
781             }
782         }
783     }
784 }
785
Popular Tags