KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > cli > jmx > support > ArgParserImpl


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23  
24 /*
25  * $Header: /cvs/glassfish/admin-cli/cli-api/src/java/com/sun/cli/jmx/support/ArgParserImpl.java,v 1.3 2005/12/25 03:45:45 tcfujii Exp $
26  * $Revision: 1.3 $
27  * $Date: 2005/12/25 03:45:45 $
28  */

29  
30
31  
32 package com.sun.cli.jmx.support;
33
34 import java.lang.reflect.Array JavaDoc;
35 import java.util.ArrayList JavaDoc;
36
37
38
39 public final class ArgParserImpl implements ArgParser
40 {
41     public static class ParseChars
42     {
43         public final static ParseChars DEFAULT = new ParseChars();
44         
45         public char mArgDelim;
46         
47         public char mEscapeChar;
48         public String JavaDoc mEscapableChars;
49         public String JavaDoc mEscapableCharsWithinLiteralString;
50         public char mArrayLeft;
51         public char mArrayRight;
52         
53         // non-configurable
54
final static char TYPECAST_BEGIN = '(';
55         final static char TYPECAST_END = ')';
56         final static char LITERAL_STRING_DELIM = '\"';
57         final static char ARG_VALUE_DELIM = '=';
58         
59             public void
60         validate()
61         {
62             // escapable chars must contain the escape char
63
assert( mEscapableChars.indexOf( mEscapeChar ) >= 0 );
64             assert( mEscapableChars.indexOf( DEFAULT_ARRAY_LEFT ) >= 0 );
65             
66             // we do not allow configuration of type cast chars
67
assert( mEscapableChars.indexOf( '(' ) >= 0 );
68         }
69         
70             public
71         ParseChars()
72         {
73             mArgDelim = DEFAULT_ARG_DELIM;
74             mEscapeChar = DEFAULT_ESCAPE_CHAR;
75             mEscapableChars = DEFAULT_ESCAPABLE_CHARS;
76             mEscapableCharsWithinLiteralString = DEFAULT_ESCAPABLE_CHARS_WITHIN_LITERAL_STRING;
77             mArrayLeft = DEFAULT_ARRAY_LEFT;
78             mArrayRight = DEFAULT_ARRAY_RIGHT;
79             
80             validate();
81         }
82     };
83     
84     
85     boolean mNamedArgs = false;
86     String JavaDoc mInput;
87     int mCurIndex;
88     ParseChars mParseChars;
89     
90     
91         public
92     ArgParserImpl( )
93     {
94         mParseChars = ParseChars.DEFAULT;
95     }
96     
97         public
98     ArgParserImpl( final ParseChars parseChars )
99     {
100         mParseChars = parseChars;
101     }
102     
103     
104         private int
105     inputRemainingCount()
106     {
107         return( mInput.length() - mCurIndex );
108     }
109     
110         private static boolean
111     isDigit( int theChar )
112     {
113         return( (theChar >= '0' && theChar <= '9') );
114     }
115     
116         private static boolean
117     isHexDigit( int theChar )
118     {
119         return( isDigit( theChar ) || (theChar >= 'a' && theChar <= 'f') ||
120             (theChar >= 'A' && theChar <= 'F') );
121     }
122     
123         private boolean
124     IsEscapableChar( int theChar, String JavaDoc escapableChars )
125     {
126         return( escapableChars.indexOf( theChar ) >= 0 || theChar == mParseChars.mEscapeChar );
127     }
128     
129         private int
130     escapeCharToChar( int theChar )
131     {
132         int result = -1;
133         
134         if ( theChar == 'n' )
135             result = '\n';
136         else if ( theChar == 'r' )
137             result = '\r';
138         else if ( theChar == 't' )
139             result = '\t';
140         else
141             result = theChar;
142         
143         return( result );
144     }
145     
146     
147     /*
148         Get the next logical character, unescaping the input as necessary.
149         
150         @returns the next character, or -1 if no more input
151      */

152         private int
153     nextChar( int escapeChar, String JavaDoc escapableChars )
154     {
155         int result = -1;
156         
157         final int theChar = nextLiteralChar();
158         if ( theChar == escapeChar )
159         {
160             final int theNextChar = nextLiteralChar();
161             if ( IsEscapableChar( theNextChar, escapableChars ) )
162             {
163                 result = escapeCharToChar( theNextChar );
164             }
165             else
166             {
167                 // if valid hexadecimal, convert two hex digits to a number
168
// otherwise emit the escape char and restart
169
// should we allow unicode?
170
result = escapeChar;
171                 if ( isHexDigit( theNextChar ) && peekNextLiteralChar() > 0 )
172                 {
173                     final int theNextNextChar = nextLiteralChar();
174                     if ( isHexDigit( theNextNextChar ) )
175                     {
176                         result = (((int)theNextChar) << 4) + (int)theNextNextChar;
177                     }
178                 }
179                 else
180                 {
181                     backup( 1 );
182                 }
183             }
184         }
185         else
186         {
187             result = theChar;
188         }
189         
190         return( result );
191     }
192     
193         private int
194     nextChar()
195     {
196         return( nextChar( mParseChars.mEscapeChar, mParseChars.mEscapableChars ) );
197     }
198     
199         private int
200     nextLiteralChar()
201     {
202         final int result = peekNextLiteralChar();
203         if ( result > 0 )
204         {
205             advance( 1 );
206         }
207         
208         return( result );
209     }
210     
211         private String JavaDoc
212     peekRemaining()
213     {
214         if ( inputRemainingCount() <= 0 )
215             return( "" );
216         
217         return( mInput.substring( mCurIndex, mInput.length() ) );
218     }
219     
220         private int
221     peekNextLiteralChar()
222     {
223         if ( inputRemainingCount() <= 0 )
224             return( -1 );
225             
226         final char result = mInput.charAt( mCurIndex );
227         
228         return( result );
229     }
230     
231         private void
232     backup( int count )
233     {
234         assert( count <= mCurIndex );
235         
236         mCurIndex -= count;
237     }
238     
239         private void
240     advance( int count )
241     {
242         assert( count <= inputRemainingCount() );
243         
244         mCurIndex += count;
245     }
246     
247         private void
248     checkInputAvailable()
249         throws ArgParserException
250     {
251         if ( inputRemainingCount() == 0 )
252         {
253             throw new ArgParserException( "expecting additional input" );
254         }
255     }
256     
257     
258     /*
259         Parse a named argument name. Named argument names are *not* escaped. Everything
260         up to the '=' is the name. The name and the '=' are consumed.
261         
262         Named arguments may not have the "," character in them and usually should be
263         
264         @returns a ParseResult for the cast
265      */

266         private String JavaDoc
267     ParseNamedArgName()
268         throws ArgParserException
269     {
270         checkInputAvailable();
271         
272         final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
273         
274         boolean foundDelim = false;
275         
276         int theChar = 0;
277         while ( (theChar = nextLiteralChar()) > 0 )
278         {
279             if ( theChar == mParseChars.ARG_VALUE_DELIM )
280             {
281                 foundDelim = true;
282                 break;
283             }
284             
285             if ( ! Character.isJavaIdentifierPart( (char)theChar ) )
286             {
287                 break;
288             }
289             
290             buf.append( (char)theChar );
291         }
292         
293         if ( ! foundDelim )
294         {
295             throw new ArgParserException(
296                 "named arguments must be of the form name=value: " + buf.toString() );
297         }
298         
299         return( new String JavaDoc( buf ) );
300     }
301     
302     /*
303         Parse a named argument. The name argument must be of the form:
304         <name>=<value>
305         
306         @returns a ParseResult for the cast
307      */

308         private ParseResult
309     ParseNamedArg()
310         throws ArgParserException
311     {
312         checkInputAvailable();
313         
314         final String JavaDoc name = ParseNamedArgName();
315         
316         ParseResult result = null;
317         
318         // special case--no more input
319
if ( inputRemainingCount() > 0 )
320         {
321             result = ParseNonNamedArg( "" + mParseChars.mArgDelim );
322         }
323         else
324         {
325             result = new ParseResult( ParseResult.OTHER, "");
326         }
327         result.setName( name );
328         
329         return( result );
330     }
331     
332         boolean
333     isStringTypecast( String JavaDoc typecast )
334     {
335         return( typecast != null &&
336             (typecast.equals( "String" ) || typecast.equals( "java.lang.String" ) ) );
337     }
338     
339     /*
340         Parse a non-named argument.
341         
342         @returns a ParseResult for the cast
343      */

344         private ParseResult
345     ParseNonNamedArg( String JavaDoc delimiters )
346         throws ArgParserException
347     {
348         ParseResult result = null;
349         
350         String JavaDoc typeCast = ParseTypeCast(); // may be null--that's OK
351

352         if ( isStringTypecast( typeCast ) )
353         {
354             if ( inputRemainingCount() == 0 )
355             {
356                 // special case--a type cast followed by nothing is OK as an empty string
357
result = new ParseResult( ParseResult.LITERAL_STRING, "" );
358                 result.setTypeCast( typeCast );
359                 return( result );
360             }
361         }
362         
363         boolean quotedString = false;
364         
365         result = ParseLiteralString();
366         if ( result != null )
367         {
368             // if a string X is quoted as "X" , this is equivalent (shorthand) for (String)X
369
typeCast = "String";
370             quotedString = true;
371         }
372         else
373         {
374             result = ParseArray();
375             if ( result == null )
376             {
377                 result = ParseRegular( delimiters );
378             }
379         }
380         
381         result.setTypeCast( typeCast );
382         if ( isStringTypecast( typeCast ) && result.getType() != ParseResult.ARRAY )
383         {
384             result.setType( ParseResult.LITERAL_STRING );
385             
386             if ( (! quotedString) &&
387                 result.getData() instanceof String JavaDoc &&
388                 "null".equalsIgnoreCase( (String JavaDoc)result.getData() ) )
389             {
390                 result.setData( null );
391             }
392         }
393         
394         // eat the delimiter
395
final int theChar = nextLiteralChar();
396         
397         boolean validDelimiter = delimiters.indexOf( theChar ) >= 0 || theChar < 0;
398         if ( ! validDelimiter )
399         {
400             throw new ArgParserException( "Syntax error: parsed this so far: " + mInput.substring( 1, mCurIndex ) );
401         }
402         
403         return( result );
404     }
405     
406     
407     /*
408         Parse a type-cast. The type-cast must be properly formed with the the begin
409         and end array type-cast characters ( and ).
410         
411         @returns a ParseResult for the cast of null if not a type cast.
412      */

413         private String JavaDoc
414     ParseTypeCast( )
415         throws ArgParserException
416     {
417         int theChar = nextLiteralChar();
418         if ( theChar != mParseChars.TYPECAST_BEGIN )
419         {
420             backup( 1 );
421             return( null );
422         }
423         
424         final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
425         
426         theChar = nextLiteralChar();
427         if ( ! Character.isJavaIdentifierStart( (char)theChar ) )
428         {
429             final String JavaDoc msg = "type cast must contain a legal Java " +
430             "identifier start character: " + peekRemaining();
431             throw new ArgParserException( msg );
432         }
433         buf.append( (char)theChar );
434         
435         boolean foundDelim = false;
436         while ( (theChar = nextLiteralChar( )) > 0 )
437         {
438             if ( theChar == mParseChars.TYPECAST_END )
439             {
440                 foundDelim = true;
441                 break;
442             }
443             
444             if ( theChar != '.' && ! Character.isJavaIdentifierPart( (char)theChar ) )
445             {
446                 throw new ArgParserException( "type cast must contain a legal Java identifier part: " +
447                             (char)theChar );
448             }
449             
450             buf.append( (char)theChar );
451         }
452         
453         if ( ! foundDelim )
454         {
455             throw new ArgParserException( "type cast must be terminated by the ) character" );
456         }
457         
458         final String JavaDoc result = new String JavaDoc( buf );
459         
460         if ( result.length() == 0 )
461         {
462             throw new ArgParserException( "type cast must contain a type" );
463         }
464         
465         return( result );
466     }
467     
468     
469     /*
470         Parse characters that are not (1) array or (2) quote-delimited string.
471         Type-cast (if any) has already been parsed.
472         
473         Note that the argument might be a string or it might be any of a variety of
474         numbers.
475         
476         @returns a ParseResult
477      */

478         private ParseResult
479     ParseRegular( String JavaDoc delimiters )
480         throws ArgParserException
481     {
482         checkInputAvailable();
483         
484         final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
485
486         int theChar;
487         while ( (theChar = peekNextLiteralChar() ) > 0 )
488         {
489             if ( delimiters.indexOf( theChar ) >= 0 )
490             {
491                 break;
492             }
493             
494             theChar = nextChar();
495             
496             buf.append( (char)theChar );
497         }
498         
499         final String JavaDoc resultString = new String JavaDoc( buf );
500         
501         final ParseResult result = new ParseResult( ParseResult.OTHER, resultString );
502         
503         return( result );
504     
505     }
506     
507     
508     /*
509         Parse an array. The array must be properly formed with the the begin
510         and end array characters { and }.
511         
512         @returns a ParseResult for the array or null if not an array
513      */

514         private ParseResult
515     ParseArray( )
516         throws ArgParserException
517     {
518         checkInputAvailable();
519         
520         // verify that it's an array
521
int theChar = nextLiteralChar();
522         if ( theChar != mParseChars.mArrayLeft )
523         {
524             backup( 1 );
525             return( null );
526         }
527         
528         final ArrayList JavaDoc elems = new ArrayList JavaDoc();
529         
530         boolean endOfArrayFound = false;
531             
532         if ( peekNextLiteralChar() == mParseChars.mArrayRight )
533         {
534             // empty array
535
endOfArrayFound = true;
536             advance( 1 );
537         }
538         else
539         {
540             while ( (theChar = peekNextLiteralChar( )) > 0 )
541             {
542                 final ParseResult elem = ParseNonNamedArg( "" +
543                         mParseChars.mArrayRight + mParseChars.mArgDelim );
544                 elems.add( elem );
545                 
546                 backup( 1 );
547                 if ( nextLiteralChar() == mParseChars.mArrayRight)
548                 {
549                     endOfArrayFound = true;
550                     break;
551                 }
552             }
553         }
554         
555         if ( ! endOfArrayFound )
556         {
557             throw new ArgParserException( "end of array not found" );
558         }
559         
560         final ParseResult [] resultsArray = new ParseResult[ elems.size() ];
561         elems.toArray( resultsArray );
562         
563         final ParseResult result = new ParseResult( ParseResult.ARRAY, resultsArray );
564         
565         return( result );
566     
567     }
568     
569     
570     /*
571         Parse a literal String. The String must be properly formed with the the begin
572         and end denoted by the double-quote character ". The only escapable character
573         within a literal string is the double-quote itself.
574         
575         @returns a ParseResult for the String or null if not a literal String
576      */

577         private ParseResult
578     ParseLiteralString( )
579         throws ArgParserException
580     {
581         checkInputAvailable();
582         
583         // verify that it's a literal string
584
int theChar = nextLiteralChar();
585         if ( theChar != mParseChars.LITERAL_STRING_DELIM )
586         {
587             backup( 1 );
588             return( null );
589         }
590         
591         final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
592         
593         boolean endOfStringFound = false;
594         while ( (theChar = peekNextLiteralChar( )) > 0 )
595         {
596             if ( theChar == mParseChars.LITERAL_STRING_DELIM )
597             {
598                 endOfStringFound = true;
599                 advance( 1 );
600                 break;
601             }
602             
603             theChar = nextChar( mParseChars.mEscapeChar, mParseChars.mEscapableCharsWithinLiteralString );
604             buf.append( (char)theChar );
605         }
606         
607         if ( ! endOfStringFound )
608         {
609             throw new ArgParserException( "literal string must be terminated by double quote \"" );
610         }
611         
612         
613         final String JavaDoc resultString = new String JavaDoc( buf );
614         
615         final ParseResult result = new ParseResult( ParseResult.LITERAL_STRING, resultString );
616         
617         return( result );
618     }
619     
620     /*
621         Parse any argument--named or not
622         
623         @returns a ParseResult for the argument
624      */

625         private ParseResult
626     ParseArg()
627         throws ArgParserException
628     {
629
630         checkInputAvailable();
631         
632         ParseResult result = null;
633         
634         if ( mNamedArgs )
635         {
636             result = ParseNamedArg();
637         }
638         else
639         {
640             result = ParseNonNamedArg( "" + mParseChars.mArgDelim );
641         }
642         
643         return( result );
644     }
645     
646     
647         public String JavaDoc []
648     ParseNames( String JavaDoc input )
649         throws ArgParserException
650     {
651         mInput = input;
652         mCurIndex = 0;
653         
654         final ArrayList JavaDoc list = new ArrayList JavaDoc();
655         
656         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
657         while ( true )
658         {
659             final int theChar = nextLiteralChar();
660             
661             if ( theChar < 0 || theChar == mParseChars.mArgDelim )
662             {
663                 list.add( new String JavaDoc( buf ) );
664                 buf.setLength( 0 );
665                 
666                 if ( theChar < 0 )
667                     break;
668             }
669             else
670             {
671                 buf.append( (char)theChar );
672             }
673         }
674         
675         
676         final String JavaDoc [] results = (String JavaDoc [])list.toArray( new String JavaDoc [ list.size() ]);
677         
678         return( results );
679     }
680     
681      
682         public ParseResult []
683     Parse( String JavaDoc input, boolean namedArgs )
684         throws ArgParserException
685     {
686         mNamedArgs = namedArgs;
687         mInput = input;
688         mCurIndex = 0;
689         
690         final ArrayList JavaDoc results = new ArrayList JavaDoc();
691         
692         while ( inputRemainingCount() > 0 )
693         {
694             final ParseResult nextArg = ParseArg( );
695             assert( nextArg != null );
696             
697             results.add( nextArg );
698         }
699         
700         
701         final ParseResult [] arrayResults = new ParseResult [ results.size( ) ];
702         results.toArray( arrayResults );
703         
704         return( arrayResults );
705     }
706     
707         private static void
708     p( Object JavaDoc o )
709     {
710         System.out.println( o.toString() );
711     }
712 }
713
714
Popular Tags