KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > excalibur > cli > CLArgsParser


1 /*
2
3  ============================================================================
4                    The Apache Software License, Version 1.1
5  ============================================================================
6
7  Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
8
9  Redistribution and use in source and binary forms, with or without modifica-
10  tion, are permitted provided that the following conditions are met:
11
12  1. Redistributions of source code must retain the above copyright notice,
13     this list of conditions and the following disclaimer.
14
15  2. Redistributions in binary form must reproduce the above copyright notice,
16     this list of conditions and the following disclaimer in the documentation
17     and/or other materials provided with the distribution.
18
19  3. The end-user documentation included with the redistribution, if any, must
20     include the following acknowledgment: "This product includes software
21     developed by the Apache Software Foundation (http://www.apache.org/)."
22     Alternately, this acknowledgment may appear in the software itself, if
23     and wherever such third-party acknowledgments normally appear.
24
25  4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation"
26     must not be used to endorse or promote products derived from this software
27     without prior written permission. For written permission, please contact
28     apache@apache.org.
29
30  5. Products derived from this software may not be called "Apache", nor may
31     "Apache" appear in their name, without prior written permission of the
32     Apache Software Foundation.
33
34  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
35  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37  APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
39  DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
41  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
45  This software consists of voluntary contributions made by many individuals
46  on behalf of the Apache Software Foundation. For more information on the
47  Apache Software Foundation, please see <http://www.apache.org/>.
48
49 */

50 package org.apache.avalon.excalibur.cli;
51
52 import java.text.ParseException JavaDoc;
53 import java.util.Hashtable JavaDoc;
54 import java.util.Vector JavaDoc;
55
56 /**
57  * Parser for command line arguments.
58  *
59  * This parses command lines according to the standard (?) of
60  * GNU utilities.
61  *
62  * Note: This is still used in 1.1 libraries so do not add 1.2+ dependencies.
63  *
64  * Note that CLArgs uses a backing hashtable for the options index and so duplicate
65  * arguments are only returned by getArguments().
66  *
67  * @author <a HREF="mailto:peter at apache.org">Peter Donald</a>
68  * @version $Revision: 1.6 $ $Date: 2003/04/11 10:25:52 $
69  * @since 4.0
70  * @see ParserControl
71  * @see CLOption
72  * @see CLOptionDescriptor
73  * @deprecated Toolkit deprecated and replaced by http://spice.sourceforge.net/cli/
74  */

75 public final class CLArgsParser
76 {
77     //cached character == Integer.MAX_VALUE when invalid
78
private static final int INVALID = Integer.MAX_VALUE;
79
80     private static final int STATE_NORMAL = 0;
81     private static final int STATE_REQUIRE_2ARGS = 1;
82     private static final int STATE_REQUIRE_ARG = 2;
83     private static final int STATE_OPTIONAL_ARG = 3;
84     private static final int STATE_NO_OPTIONS = 4;
85     private static final int STATE_OPTION_MODE = 5;
86
87     private static final int TOKEN_SEPARATOR = 0;
88     private static final int TOKEN_STRING = 1;
89
90     private static final char[] ARG2_SEPARATORS =
91         new char[]{(char)0, '=', '-'};
92
93     private static final char[] ARG_SEPARATORS =
94         new char[]{(char)0, '='};
95
96     private static final char[] NULL_SEPARATORS =
97         new char[]{(char)0};
98
99     private final CLOptionDescriptor[] m_optionDescriptors;
100     private final Vector JavaDoc m_options;
101     private Hashtable JavaDoc m_optionIndex;
102     private final ParserControl m_control;
103
104     private String JavaDoc m_errorMessage;
105     private String JavaDoc[] m_unparsedArgs = new String JavaDoc[]{};
106
107     //variables used while parsing options.
108
private char m_ch;
109     private String JavaDoc[] m_args;
110     private boolean m_isLong;
111     private int m_argIndex;
112     private int m_stringIndex;
113     private int m_stringLength;
114
115     private int m_lastChar = INVALID;
116
117     private int m_lastOptionId;
118     private CLOption m_option;
119     private int m_state = STATE_NORMAL;
120
121     /**
122      * Retrieve an array of arguments that have not been parsed
123      * due to the parser halting.
124      *
125      * @return an array of unparsed args
126      */

127     public final String JavaDoc[] getUnparsedArgs()
128     {
129         return m_unparsedArgs;
130     }
131
132     /**
133      * Retrieve a list of options that were parsed from command list.
134      *
135      * @return the list of options
136      */

137     public final Vector JavaDoc getArguments()
138     {
139         //System.out.println( "Arguments: " + m_options );
140
return m_options;
141     }
142
143     /**
144      * Retrieve the {@link CLOption} with specified id, or
145      * <code>null</code> if no command line option is found.
146      *
147      * @param id the command line option id
148      * @return the {@link CLOption} with the specified id, or
149      * <code>null</code> if no CLOption is found.
150      * @see CLOption
151      */

152     public final CLOption getArgumentById( final int id )
153     {
154         return (CLOption)m_optionIndex.get( new Integer JavaDoc( id ) );
155     }
156
157     /**
158      * Retrieve the {@link CLOption} with specified name, or
159      * <code>null</code> if no command line option is found.
160      *
161      * @param name the command line option name
162      * @return the {@link CLOption} with the specified name, or
163      * <code>null</code> if no CLOption is found.
164      * @see CLOption
165      */

166     public final CLOption getArgumentByName( final String JavaDoc name )
167     {
168         return (CLOption)m_optionIndex.get( name );
169     }
170
171     /**
172      * Get Descriptor for option id.
173      *
174      * @param id the id
175      * @return the descriptor
176      */

177     private final CLOptionDescriptor getDescriptorFor( final int id )
178     {
179         for( int i = 0; i < m_optionDescriptors.length; i++ )
180         {
181             if( m_optionDescriptors[ i ].getId() == id )
182             {
183                 return m_optionDescriptors[ i ];
184             }
185         }
186
187         return null;
188     }
189
190     /**
191      * Retrieve a descriptor by name.
192      *
193      * @param name the name
194      * @return the descriptor
195      */

196     private final CLOptionDescriptor getDescriptorFor( final String JavaDoc name )
197     {
198         for( int i = 0; i < m_optionDescriptors.length; i++ )
199         {
200             if( m_optionDescriptors[ i ].getName().equals( name ) )
201             {
202                 return m_optionDescriptors[ i ];
203             }
204         }
205
206         return null;
207     }
208
209     /**
210      * Retrieve an error message that occured during parsing if one existed.
211      *
212      * @return the error string
213      */

214     public final String JavaDoc getErrorString()
215     {
216         //System.out.println( "ErrorString: " + m_errorMessage );
217
return m_errorMessage;
218     }
219
220     /**
221      * Require state to be placed in for option.
222      *
223      * @param descriptor the Option Descriptor
224      * @return the state
225      */

226     private final int getStateFor( final CLOptionDescriptor descriptor )
227     {
228         final int flags = descriptor.getFlags();
229         if( ( flags & CLOptionDescriptor.ARGUMENTS_REQUIRED_2 ) ==
230             CLOptionDescriptor.ARGUMENTS_REQUIRED_2 )
231         {
232             return STATE_REQUIRE_2ARGS;
233         }
234         else if( ( flags & CLOptionDescriptor.ARGUMENT_REQUIRED ) ==
235             CLOptionDescriptor.ARGUMENT_REQUIRED )
236         {
237             return STATE_REQUIRE_ARG;
238         }
239         else if( ( flags & CLOptionDescriptor.ARGUMENT_OPTIONAL ) ==
240             CLOptionDescriptor.ARGUMENT_OPTIONAL )
241         {
242             return STATE_OPTIONAL_ARG;
243         }
244         else
245         {
246             return STATE_NORMAL;
247         }
248     }
249
250     /**
251      * Create a parser that can deal with options and parses certain args.
252      *
253      * @param args the args, typically that passed to the
254      * <code>public static void main(String[] args)</code> method.
255      * @param optionDescriptors the option descriptors
256      * @param control the parser control used determine behaviour of parser
257      */

258     public CLArgsParser( final String JavaDoc[] args,
259                          final CLOptionDescriptor[] optionDescriptors,
260                          final ParserControl control )
261     {
262         m_optionDescriptors = optionDescriptors;
263         m_control = control;
264         m_options = new Vector JavaDoc();
265         m_args = args;
266
267         try
268         {
269             parse();
270             checkIncompatibilities( m_options );
271             buildOptionIndex();
272         }
273         catch( final ParseException JavaDoc pe )
274         {
275             m_errorMessage = pe.getMessage();
276         }
277
278         //System.out.println( "Built : " + m_options );
279
//System.out.println( "From : " + Arrays.asList( args ) );
280
}
281
282     /**
283      * Check for duplicates of an option.
284      * It is an error to have duplicates unless appropriate flags is set in descriptor.
285      *
286      * @param arguments the arguments
287      */

288     private final void checkIncompatibilities( final Vector JavaDoc arguments )
289         throws ParseException JavaDoc
290     {
291         final int size = arguments.size();
292
293         for( int i = 0; i < size; i++ )
294         {
295             final CLOption option = (CLOption)arguments.elementAt( i );
296             final int id = option.getDescriptor().getId();
297             final CLOptionDescriptor descriptor = getDescriptorFor( id );
298
299             //this occurs when id == 0 and user has not supplied a descriptor
300
//for arguments
301
if( null == descriptor )
302             {
303                 continue;
304             }
305
306             final int[] incompatible = descriptor.getIncompatible();
307
308             checkIncompatible( arguments, incompatible, i );
309         }
310     }
311
312     private final void checkIncompatible( final Vector JavaDoc arguments,
313                                           final int[] incompatible,
314                                           final int original )
315         throws ParseException JavaDoc
316     {
317         final int size = arguments.size();
318
319         for( int i = 0; i < size; i++ )
320         {
321             if( original == i )
322             {
323                 continue;
324             }
325
326             final CLOption option = (CLOption)arguments.elementAt( i );
327             final int id = option.getDescriptor().getId();
328
329             for( int j = 0; j < incompatible.length; j++ )
330             {
331                 if( id == incompatible[ j ] )
332                 {
333                     final CLOption originalOption = (CLOption)arguments.elementAt( original );
334                     final int originalId = originalOption.getDescriptor().getId();
335
336                     String JavaDoc message = null;
337
338                     if( id == originalId )
339                     {
340                         message =
341                             "Duplicate options for " + describeDualOption( originalId ) +
342                             " found.";
343                     }
344                     else
345                     {
346                         message = "Incompatible options -" +
347                             describeDualOption( id ) + " and " +
348                             describeDualOption( originalId ) + " found.";
349                     }
350                     throw new ParseException JavaDoc( message, 0 );
351                 }
352             }
353         }
354     }
355
356     private final String JavaDoc describeDualOption( final int id )
357     {
358         final CLOptionDescriptor descriptor = getDescriptorFor( id );
359         if( null == descriptor )
360         {
361             return "<parameter>";
362         }
363         else
364         {
365             final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
366             boolean hasCharOption = false;
367
368             if( Character.isLetter( (char)id ) )
369             {
370                 sb.append( '-' );
371                 sb.append( (char)id );
372                 hasCharOption = true;
373             }
374
375             final String JavaDoc longOption = descriptor.getName();
376             if( null != longOption )
377             {
378                 if( hasCharOption )
379                 {
380                     sb.append( '/' );
381                 }
382                 sb.append( "--" );
383                 sb.append( longOption );
384             }
385
386             return sb.toString();
387         }
388     }
389
390     /**
391      * Create a parser that deals with options and parses certain args.
392      *
393      * @param args the args
394      * @param optionDescriptors the option descriptors
395      */

396     public CLArgsParser( final String JavaDoc[] args,
397                          final CLOptionDescriptor[] optionDescriptors )
398     {
399         this( args, optionDescriptors, null );
400     }
401
402     /**
403      * Create a string array that is subset of input array.
404      * The sub-array should start at array entry indicated by index. That array element
405      * should only include characters from charIndex onwards.
406      *
407      * @param array the original array
408      * @param index the cut-point in array
409      * @param charIndex the cut-point in element of array
410      * @return the result array
411      */

412     private final String JavaDoc[] subArray( final String JavaDoc[] array,
413                                      final int index,
414                                      final int charIndex )
415     {
416         final int remaining = array.length - index;
417         final String JavaDoc[] result = new String JavaDoc[ remaining ];
418
419         if( remaining > 1 )
420         {
421             System.arraycopy( array, index + 1, result, 1, remaining - 1 );
422         }
423
424         result[ 0 ] = array[ index ].substring( charIndex - 1 );
425
426         return result;
427     }
428
429     /**
430      * Actually parse arguments
431      */

432     private final void parse()
433         throws ParseException JavaDoc
434     {
435         if( 0 == m_args.length )
436         {
437             return;
438         }
439
440         m_stringLength = m_args[ m_argIndex ].length();
441
442         //ch = peekAtChar();
443

444         while( true )
445         {
446             m_ch = peekAtChar();
447
448             //System.out.println( "Pre State=" + m_state );
449
//System.out.println( "Pre Char=" + (char)ch + "/" + (int)ch );
450

451             if( m_argIndex >= m_args.length )
452             {
453                 break;
454             }
455
456             if( null != m_control && m_control.isFinished( m_lastOptionId ) )
457             {
458                 //this may need mangling due to peeks
459
m_unparsedArgs = subArray( m_args, m_argIndex, m_stringIndex );
460                 return;
461             }
462
463             //System.out.println( "State=" + m_state );
464
//System.out.println( "Char=" + (char)ch + "/" + (int)ch );
465

466             if( STATE_OPTION_MODE == m_state )
467             {
468                 //if get to an arg barrier then return to normal mode
469
//else continue accumulating options
470
if( 0 == m_ch )
471                 {
472                     getChar(); //strip the null
473
m_state = STATE_NORMAL;
474                 }
475                 else
476                 {
477                     parseShortOption();
478                 }
479             }
480             else if( STATE_NORMAL == m_state )
481             {
482                 parseNormal();
483             }
484             else if( STATE_NO_OPTIONS == m_state )
485             {
486                 //should never get to here when stringIndex != 0
487
addOption( new CLOption( m_args[ m_argIndex++ ] ) );
488             }
489             else if( STATE_OPTIONAL_ARG == m_state && '-' == m_ch )
490             {
491                 m_state = STATE_NORMAL;
492                 addOption( m_option );
493             }
494             else
495             {
496                 parseArguments();
497             }
498         }
499
500         if( m_option != null )
501         {
502             if( STATE_OPTIONAL_ARG == m_state )
503             {
504                 m_options.addElement( m_option );
505             }
506             else if( STATE_REQUIRE_ARG == m_state )
507             {
508                 final CLOptionDescriptor descriptor = getDescriptorFor( m_option.getDescriptor().getId() );
509                 final String JavaDoc message =
510                     "Missing argument to option " + getOptionDescription( descriptor );
511                 throw new ParseException JavaDoc( message, 0 );
512             }
513             else if( STATE_REQUIRE_2ARGS == m_state )
514             {
515                 if( 1 == m_option.getArgumentCount() )
516                 {
517                     m_option.addArgument( "" );
518                     m_options.addElement( m_option );
519                 }
520                 else
521                 {
522                     final CLOptionDescriptor descriptor = getDescriptorFor( m_option.getDescriptor().getId() );
523                     final String JavaDoc message =
524                         "Missing argument to option " + getOptionDescription( descriptor );
525                     throw new ParseException JavaDoc( message, 0 );
526                 }
527             }
528             else
529             {
530                 throw new ParseException JavaDoc( "IllegalState " + m_state + ": " + m_option, 0 );
531             }
532         }
533     }
534
535     private final String JavaDoc getOptionDescription( final CLOptionDescriptor descriptor )
536     {
537         if( m_isLong )
538         {
539             return "--" + descriptor.getName();
540         }
541         else
542         {
543             return "-" + (char)descriptor.getId();
544         }
545     }
546
547     private final char peekAtChar()
548     {
549         if( INVALID == m_lastChar )
550         {
551             m_lastChar = readChar();
552         }
553         return (char)m_lastChar;
554     }
555
556     private final char getChar()
557     {
558         if( INVALID != m_lastChar )
559         {
560             final char result = (char)m_lastChar;
561             m_lastChar = INVALID;
562             return result;
563         }
564         else
565         {
566             return readChar();
567         }
568     }
569
570     private final char readChar()
571     {
572         if( m_stringIndex >= m_stringLength )
573         {
574             m_argIndex++;
575             m_stringIndex = 0;
576
577             if( m_argIndex < m_args.length )
578             {
579                 m_stringLength = m_args[ m_argIndex ].length();
580             }
581             else
582             {
583                 m_stringLength = 0;
584             }
585
586             return 0;
587         }
588
589         if( m_argIndex >= m_args.length )
590         {
591             return 0;
592         }
593
594         return m_args[ m_argIndex ].charAt( m_stringIndex++ );
595     }
596
597     private final Token nextToken( final char[] separators )
598     {
599         m_ch = getChar();
600
601         if( isSeparator( m_ch, separators ) )
602         {
603             m_ch = getChar();
604             return new Token( TOKEN_SEPARATOR, null );
605         }
606
607         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
608
609         do
610         {
611             sb.append( m_ch );
612             m_ch = getChar();
613         } while( !isSeparator( m_ch, separators ) );
614
615         return new Token( TOKEN_STRING, sb.toString() );
616     }
617
618     private final boolean isSeparator( final char ch, final char[] separators )
619     {
620         for( int i = 0; i < separators.length; i++ )
621         {
622             if( ch == separators[ i ] )
623             {
624                 return true;
625             }
626         }
627
628         return false;
629     }
630
631     private final void addOption( final CLOption option )
632     {
633         m_options.addElement( option );
634         m_lastOptionId = option.getDescriptor().getId();
635         m_option = null;
636     }
637
638     private final void parseOption( final CLOptionDescriptor descriptor,
639                                     final String JavaDoc optionString )
640         throws ParseException JavaDoc
641     {
642         if( null == descriptor )
643         {
644             throw new ParseException JavaDoc( "Unknown option " + optionString, 0 );
645         }
646
647         m_state = getStateFor( descriptor );
648         m_option = new CLOption( descriptor );
649
650         if( STATE_NORMAL == m_state )
651         {
652             addOption( m_option );
653         }
654     }
655
656     private final void parseShortOption()
657         throws ParseException JavaDoc
658     {
659         m_ch = getChar();
660         final CLOptionDescriptor descriptor = getDescriptorFor( m_ch );
661         m_isLong = false;
662         parseOption( descriptor, "-" + m_ch );
663
664         if( STATE_NORMAL == m_state )
665         {
666             m_state = STATE_OPTION_MODE;
667         }
668     }
669
670     private final void parseArguments()
671         throws ParseException JavaDoc
672     {
673         if( STATE_REQUIRE_ARG == m_state )
674         {
675             if( '=' == m_ch || 0 == m_ch )
676             {
677                 getChar();
678             }
679
680             final Token token = nextToken( NULL_SEPARATORS );
681             m_option.addArgument( token.getValue() );
682
683             addOption( m_option );
684             m_state = STATE_NORMAL;
685         }
686         else if( STATE_OPTIONAL_ARG == m_state )
687         {
688             if( '-' == m_ch || 0 == m_ch )
689             {
690                 getChar(); //consume stray character
691
addOption( m_option );
692                 m_state = STATE_NORMAL;
693                 return;
694             }
695
696             if( '=' == m_ch )
697             {
698                 getChar();
699             }
700
701             final Token token = nextToken( NULL_SEPARATORS );
702             m_option.addArgument( token.getValue() );
703
704             addOption( m_option );
705             m_state = STATE_NORMAL;
706         }
707         else if( STATE_REQUIRE_2ARGS == m_state )
708         {
709             if( 0 == m_option.getArgumentCount() )
710             {
711                 final Token token = nextToken( ARG_SEPARATORS );
712
713                 if( TOKEN_SEPARATOR == token.getType() )
714                 {
715                     final CLOptionDescriptor descriptor = getDescriptorFor( m_option.getDescriptor().getId() );
716                     final String JavaDoc message =
717                         "Unable to parse first argument for option " +
718                         getOptionDescription( descriptor );
719                     throw new ParseException JavaDoc( message, 0 );
720                 }
721                 else
722                 {
723                     m_option.addArgument( token.getValue() );
724                 }
725             }
726             else //2nd argument
727
{
728                 final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
729
730                 m_ch = getChar();
731                 if( '-' == m_ch )
732                 {
733                     m_lastChar = m_ch;
734                 }
735
736                 while( !isSeparator( m_ch, ARG2_SEPARATORS ) )
737                 {
738                     sb.append( m_ch );
739                     m_ch = getChar();
740                 }
741
742                 final String JavaDoc argument = sb.toString();
743
744                 //System.out.println( "Arguement:" + argument );
745

746                 m_option.addArgument( argument );
747                 addOption( m_option );
748                 m_option = null;
749                 m_state = STATE_NORMAL;
750             }
751         }
752     }
753
754     /**
755      * Parse Options from Normal mode.
756      */

757     private final void parseNormal()
758         throws ParseException JavaDoc
759     {
760         if( '-' != m_ch )
761         {
762             //Parse the arguments that are not options
763
final String JavaDoc argument = nextToken( NULL_SEPARATORS ).getValue();
764             addOption( new CLOption( argument ) );
765             m_state = STATE_NORMAL;
766         }
767         else
768         {
769             getChar(); // strip the -
770

771             if( 0 == peekAtChar() )
772             {
773                 throw new ParseException JavaDoc( "Malformed option -", 0 );
774             }
775             else
776             {
777                 m_ch = peekAtChar();
778
779                 //if it is a short option then parse it else ...
780
if( '-' != m_ch )
781                 {
782                     parseShortOption();
783                 }
784                 else
785                 {
786                     getChar(); // strip the -
787
//-- sequence .. it can either mean a change of state
788
//to STATE_NO_OPTIONS or else a long option
789

790                     if( 0 == peekAtChar() )
791                     {
792                         getChar();
793                         m_state = STATE_NO_OPTIONS;
794                     }
795                     else
796                     {
797                         //its a long option
798
final String JavaDoc optionName = nextToken( ARG_SEPARATORS ).getValue();
799                         final CLOptionDescriptor descriptor = getDescriptorFor( optionName );
800                         m_isLong = true;
801                         parseOption( descriptor, "--" + optionName );
802                     }
803                 }
804             }
805         }
806     }
807
808     /**
809      * Build the m_optionIndex lookup map for the parsed options.
810      */

811     private final void buildOptionIndex()
812     {
813         final int size = m_options.size();
814         m_optionIndex = new Hashtable JavaDoc( size * 2 );
815
816         for( int i = 0; i < size; i++ )
817         {
818             final CLOption option = (CLOption)m_options.get( i );
819             final CLOptionDescriptor optionDescriptor =
820                 getDescriptorFor( option.getDescriptor().getId() );
821
822             m_optionIndex.put( new Integer JavaDoc( option.getDescriptor().getId() ), option );
823
824             if( null != optionDescriptor &&
825                 null != optionDescriptor.getName() )
826             {
827                 m_optionIndex.put( optionDescriptor.getName(), option );
828             }
829         }
830     }
831 }
832
Popular Tags