1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  package org.jomc.jls;
32  
33  import java.io.Serializable;
34  import java.lang.ref.Reference;
35  import java.lang.ref.SoftReference;
36  import java.text.MessageFormat;
37  import java.text.ParseException;
38  import java.text.ParsePosition;
39  import java.util.ArrayList;
40  import java.util.Collections;
41  import java.util.HashMap;
42  import java.util.LinkedList;
43  import java.util.List;
44  import java.util.Locale;
45  import java.util.Map;
46  import java.util.ResourceBundle;
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  public final class JavaTypeName implements Serializable
61  {
62  
63      
64  
65  
66  
67  
68  
69      public static final class Argument implements Serializable
70      {
71  
72          
73  
74  
75  
76  
77          private boolean wildcard;
78  
79          
80  
81  
82  
83  
84          private String wildcardBounds;
85  
86          
87  
88  
89  
90  
91          private JavaTypeName typeName;
92  
93          
94  
95  
96          private transient String cachedString;
97  
98          
99  
100 
101         private static final long serialVersionUID = 3028173973749861191L;
102 
103         
104 
105 
106         private Argument()
107         {
108             super();
109         }
110 
111         
112 
113 
114 
115 
116         public boolean isWildcard()
117         {
118             return this.wildcard;
119         }
120 
121         
122 
123 
124 
125 
126         public String getWildcardBounds()
127         {
128             return this.wildcardBounds;
129         }
130 
131         
132 
133 
134 
135 
136         public JavaTypeName getTypeName()
137         {
138             return this.typeName;
139         }
140 
141         
142 
143 
144 
145 
146         @Override
147         public String toString()
148         {
149             if ( this.cachedString == null )
150             {
151                 final StringBuilder builder = new StringBuilder( 128 );
152 
153                 if ( this.isWildcard() )
154                 {
155                     builder.append( "?" );
156 
157                     if ( this.getWildcardBounds() != null && this.getTypeName() != null )
158                     {
159                         builder.append( " " ).append( this.getWildcardBounds() ).append( " " ).
160                             append( this.getTypeName() );
161 
162                     }
163                 }
164                 else
165                 {
166                     builder.append( this.getTypeName() );
167                 }
168 
169                 this.cachedString = builder.toString();
170             }
171 
172             return this.cachedString;
173         }
174 
175     }
176 
177     
178 
179 
180 
181 
182     public static final JavaTypeName BOOLEAN;
183 
184     
185 
186 
187 
188 
189     public static final JavaTypeName BOOLEAN_TYPE;
190 
191     
192 
193 
194 
195 
196     public static final JavaTypeName BYTE;
197 
198     
199 
200 
201 
202 
203     public static final JavaTypeName BYTE_TYPE;
204 
205     
206 
207 
208 
209 
210     public static final JavaTypeName CHARACTER;
211 
212     
213 
214 
215 
216 
217     public static final JavaTypeName CHARACTER_TYPE;
218 
219     
220 
221 
222 
223 
224     public static final JavaTypeName DOUBLE;
225 
226     
227 
228 
229 
230 
231     public static final JavaTypeName DOUBLE_TYPE;
232 
233     
234 
235 
236 
237 
238     public static final JavaTypeName FLOAT;
239 
240     
241 
242 
243 
244 
245     public static final JavaTypeName FLOAT_TYPE;
246 
247     
248 
249 
250 
251 
252     public static final JavaTypeName INTEGER;
253 
254     
255 
256 
257 
258 
259     public static final JavaTypeName INTEGER_TYPE;
260 
261     
262 
263 
264 
265 
266     public static final JavaTypeName LONG;
267 
268     
269 
270 
271 
272 
273     public static final JavaTypeName LONG_TYPE;
274 
275     
276 
277 
278 
279 
280     public static final JavaTypeName SHORT;
281 
282     
283 
284 
285 
286 
287     public static final JavaTypeName SHORT_TYPE;
288 
289     
290 
291 
292 
293 
294     private int dimension;
295 
296     
297 
298 
299 
300 
301     private boolean primitive;
302 
303     
304 
305 
306 
307 
308     private String className;
309 
310     
311 
312 
313 
314 
315     private String packageName;
316 
317     
318 
319 
320 
321 
322     private String qualifiedName;
323 
324     
325 
326 
327 
328 
329     private String simpleName;
330 
331     
332 
333 
334 
335 
336     private volatile List<Argument> arguments;
337 
338     
339 
340 
341     private transient String cachedString;
342 
343     
344 
345 
346     private static volatile Reference<Map<String, JavaTypeName>> cache;
347 
348     
349 
350 
351     private static final Map<String, String> CLASSNAME_ENCODINGS = new HashMap<String, String>( 8 );
352 
353     
354 
355 
356     private static final long serialVersionUID = 4185731817250549926L;
357 
358     static
359     {
360         CLASSNAME_ENCODINGS.put( "boolean", "Z" );
361         CLASSNAME_ENCODINGS.put( "byte", "B" );
362         CLASSNAME_ENCODINGS.put( "char", "C" );
363         CLASSNAME_ENCODINGS.put( "double", "D" );
364         CLASSNAME_ENCODINGS.put( "float", "F" );
365         CLASSNAME_ENCODINGS.put( "int", "I" );
366         CLASSNAME_ENCODINGS.put( "long", "J" );
367         CLASSNAME_ENCODINGS.put( "short", "S" );
368 
369         BOOLEAN = JavaTypeName.valueOf( Boolean.class.getName() );
370         BOOLEAN_TYPE = JavaTypeName.valueOf( Boolean.TYPE.getName() );
371         BYTE = JavaTypeName.valueOf( Byte.class.getName() );
372         BYTE_TYPE = JavaTypeName.valueOf( Byte.TYPE.getName() );
373         CHARACTER = JavaTypeName.valueOf( Character.class.getName() );
374         CHARACTER_TYPE = JavaTypeName.valueOf( Character.TYPE.getName() );
375         DOUBLE = JavaTypeName.valueOf( Double.class.getName() );
376         DOUBLE_TYPE = JavaTypeName.valueOf( Double.TYPE.getName() );
377         FLOAT = JavaTypeName.valueOf( Float.class.getName() );
378         FLOAT_TYPE = JavaTypeName.valueOf( Float.TYPE.getName() );
379         INTEGER = JavaTypeName.valueOf( Integer.class.getName() );
380         INTEGER_TYPE = JavaTypeName.valueOf( Integer.TYPE.getName() );
381         LONG = JavaTypeName.valueOf( Long.class.getName() );
382         LONG_TYPE = JavaTypeName.valueOf( Long.TYPE.getName() );
383         SHORT = JavaTypeName.valueOf( Short.class.getName() );
384         SHORT_TYPE = JavaTypeName.valueOf( Short.TYPE.getName() );
385     }
386 
387     
388 
389 
390     private JavaTypeName()
391     {
392         super();
393     }
394 
395     
396 
397 
398 
399 
400 
401 
402 
403 
404 
405 
406 
407 
408 
409     public Class<?> getClass( final ClassLoader classLoader, final boolean initialize ) throws ClassNotFoundException
410     {
411         Class<?> javaClass = null;
412 
413         if ( this.isArray() )
414         {
415             javaClass = Class.forName( this.getClassName(), initialize, classLoader );
416         }
417         else if ( this.isPrimitive() )
418         {
419             if ( BOOLEAN_TYPE.equals( this ) )
420             {
421                 javaClass = Boolean.TYPE;
422             }
423             else if ( BYTE_TYPE.equals( this ) )
424             {
425                 javaClass = Byte.TYPE;
426             }
427             else if ( CHARACTER_TYPE.equals( this ) )
428             {
429                 javaClass = Character.TYPE;
430             }
431             else if ( DOUBLE_TYPE.equals( this ) )
432             {
433                 javaClass = Double.TYPE;
434             }
435             else if ( FLOAT_TYPE.equals( this ) )
436             {
437                 javaClass = Float.TYPE;
438             }
439             else if ( INTEGER_TYPE.equals( this ) )
440             {
441                 javaClass = Integer.TYPE;
442             }
443             else if ( LONG_TYPE.equals( this ) )
444             {
445                 javaClass = Long.TYPE;
446             }
447             else if ( SHORT_TYPE.equals( this ) )
448             {
449                 javaClass = Short.TYPE;
450             }
451             else
452             {
453                 throw new AssertionError( this );
454             }
455         }
456         else
457         {
458             javaClass = Class.forName( this.getClassName(), initialize, classLoader );
459         }
460 
461         return javaClass;
462     }
463 
464     
465 
466 
467 
468 
469     public List<Argument> getArguments()
470     {
471         if ( this.arguments == null )
472         {
473             this.arguments = new ArrayList<Argument>();
474         }
475 
476         return this.arguments;
477     }
478 
479     
480 
481 
482 
483 
484 
485 
486     public boolean isArray()
487     {
488         return this.dimension > 0;
489     }
490 
491     
492 
493 
494 
495 
496 
497 
498     public boolean isPrimitive()
499     {
500         return this.primitive;
501     }
502 
503     
504 
505 
506 
507 
508     public boolean isUnboxable()
509     {
510         
511         return BOOLEAN.equals( this )
512                    || BYTE.equals( this )
513                    || SHORT.equals( this )
514                    || CHARACTER.equals( this )
515                    || INTEGER.equals( this )
516                    || LONG.equals( this )
517                    || FLOAT.equals( this )
518                    || DOUBLE.equals( this );
519 
520     }
521 
522     
523 
524 
525 
526 
527 
528 
529     public String getName( final boolean qualified )
530     {
531         return qualified
532                    ? this.toString()
533                    : this.getPackageName().length() > 0
534                          ? this.toString().substring( this.getPackageName().length() + 1 )
535                          : this.toString();
536 
537     }
538 
539     
540 
541 
542 
543 
544 
545 
546 
547     public String getClassName()
548     {
549         return this.className;
550     }
551 
552     
553 
554 
555 
556 
557 
558 
559 
560     public String getPackageName()
561     {
562         return this.packageName;
563     }
564 
565     
566 
567 
568 
569 
570 
571 
572     public boolean isUnnamedPackage()
573     {
574         return this.getPackageName().length() == 0;
575     }
576 
577     
578 
579 
580 
581 
582     public String getQualifiedName()
583     {
584         return this.qualifiedName;
585     }
586 
587     
588 
589 
590 
591 
592     public String getSimpleName()
593     {
594         return this.simpleName;
595     }
596 
597     
598 
599 
600 
601 
602 
603 
604 
605     public JavaTypeName getBoxedName()
606     {
607         JavaTypeName boxedName = null;
608 
609         
610         if ( BOOLEAN_TYPE.equals( this ) )
611         {
612             boxedName = BOOLEAN;
613         }
614         else if ( BYTE_TYPE.equals( this ) )
615         {
616             boxedName = BYTE;
617         }
618         else if ( SHORT_TYPE.equals( this ) )
619         {
620             boxedName = SHORT;
621         }
622         else if ( CHARACTER_TYPE.equals( this ) )
623         {
624             boxedName = CHARACTER;
625         }
626         else if ( INTEGER_TYPE.equals( this ) )
627         {
628             boxedName = INTEGER;
629         }
630         else if ( LONG_TYPE.equals( this ) )
631         {
632             boxedName = LONG;
633         }
634         else if ( FLOAT_TYPE.equals( this ) )
635         {
636             boxedName = FLOAT;
637         }
638         else if ( DOUBLE_TYPE.equals( this ) )
639         {
640             boxedName = DOUBLE;
641         }
642 
643         return boxedName;
644     }
645 
646     
647 
648 
649 
650 
651 
652 
653     public JavaTypeName getUnboxedName()
654     {
655         JavaTypeName unboxedName = null;
656 
657         
658         if ( BOOLEAN.equals( this ) )
659         {
660             unboxedName = BOOLEAN_TYPE;
661         }
662         else if ( BYTE.equals( this ) )
663         {
664             unboxedName = BYTE_TYPE;
665         }
666         else if ( SHORT.equals( this ) )
667         {
668             unboxedName = SHORT_TYPE;
669         }
670         else if ( CHARACTER.equals( this ) )
671         {
672             unboxedName = CHARACTER_TYPE;
673         }
674         else if ( INTEGER.equals( this ) )
675         {
676             unboxedName = INTEGER_TYPE;
677         }
678         else if ( LONG.equals( this ) )
679         {
680             unboxedName = LONG_TYPE;
681         }
682         else if ( FLOAT.equals( this ) )
683         {
684             unboxedName = FLOAT_TYPE;
685         }
686         else if ( DOUBLE.equals( this ) )
687         {
688             unboxedName = DOUBLE_TYPE;
689         }
690 
691         return unboxedName;
692     }
693 
694     
695 
696 
697 
698 
699     @Override
700     public String toString()
701     {
702         if ( this.cachedString == null )
703         {
704             final StringBuilder builder = new StringBuilder( this.getQualifiedName() );
705 
706             if ( !this.getArguments().isEmpty() )
707             {
708                 builder.append( "<" );
709 
710                 for ( int i = 0, s0 = this.getArguments().size(); i < s0; i++ )
711                 {
712                     builder.append( this.getArguments().get( i ) ).append( ", " );
713                 }
714 
715                 builder.setLength( builder.length() - 2 );
716                 builder.append( ">" );
717             }
718 
719             if ( this.isArray() )
720             {
721                 final int idx = this.getQualifiedName().length() - this.dimension * "[]".length();
722                 builder.append( builder.substring( idx, this.getQualifiedName().length() ) );
723                 builder.delete( idx, this.getQualifiedName().length() );
724             }
725 
726             this.cachedString = builder.toString();
727         }
728 
729         return this.cachedString;
730     }
731 
732     
733 
734 
735 
736 
737     @Override
738     public int hashCode()
739     {
740         return this.toString().hashCode();
741     }
742 
743     
744 
745 
746 
747 
748 
749 
750     @Override
751     public boolean equals( final Object o )
752     {
753         boolean equal = o == this;
754 
755         if ( !equal && o instanceof JavaTypeName )
756         {
757             equal = this.toString().equals( o.toString() );
758         }
759 
760         return equal;
761     }
762 
763     
764 
765 
766 
767 
768 
769 
770     public boolean runtimeEquals( final Object o )
771     {
772         boolean equal = o == this;
773 
774         if ( !equal && o instanceof JavaTypeName )
775         {
776             final JavaTypeName that = (JavaTypeName) o;
777             equal = this.getClassName().equals( that.getClassName() );
778         }
779 
780         return equal;
781     }
782 
783     
784 
785 
786 
787 
788 
789 
790 
791 
792 
793 
794 
795     public static JavaTypeName parse( final String text ) throws ParseException
796     {
797         if ( text == null )
798         {
799             throw new NullPointerException( "text" );
800         }
801 
802         return parse( text, false );
803     }
804 
805     
806 
807 
808 
809 
810 
811 
812 
813 
814 
815 
816 
817 
818 
819 
820 
821     public static JavaTypeName valueOf( final String text ) throws IllegalArgumentException
822     {
823         if ( text == null )
824         {
825             throw new NullPointerException( "text" );
826         }
827 
828         try
829         {
830             return parse( text, true );
831         }
832         catch ( final ParseException e )
833         {
834             throw new AssertionError( e );
835         }
836     }
837 
838     private static JavaTypeName parse( final String text, boolean runtimeException ) throws ParseException
839     {
840         Map<String, JavaTypeName> map = cache == null ? null : cache.get();
841 
842         if ( map == null )
843         {
844             map = new HashMap<String, JavaTypeName>( 128 );
845             cache = new SoftReference<Map<String, JavaTypeName>>( map );
846         }
847 
848         synchronized ( map )
849         {
850             JavaTypeName javaType = map.get( text );
851 
852             if ( javaType == null )
853             {
854                 javaType = new JavaTypeName();
855                 parseType( javaType, text, runtimeException );
856 
857                 javaType.arguments = javaType.arguments != null
858                                          ? Collections.unmodifiableList( javaType.arguments )
859                                          : Collections.<Argument>emptyList();
860 
861                 final String name = javaType.getName( true );
862                 final JavaTypeName existingInstance = map.get( name );
863 
864                 if ( existingInstance != null )
865                 {
866                     map.put( text, existingInstance );
867                     javaType = existingInstance;
868                 }
869                 else
870                 {
871                     map.put( text, javaType );
872                     map.put( name, javaType );
873                 }
874             }
875 
876             return javaType;
877         }
878     }
879 
880     
881 
882 
883 
884 
885 
886 
887 
888 
889 
890     private static void parseType( final JavaTypeName t, final String text, final boolean runtimeException )
891         throws ParseException
892     {
893         final Tokenizer tokenizer = new Tokenizer( text, runtimeException );
894         boolean basic_type_or_reference_type_seen = false;
895         boolean lpar_seen = false;
896         Token token;
897 
898         while ( ( token = tokenizer.next() ) != null )
899         {
900             switch ( token.getKind() )
901             {
902                 case Tokenizer.TK_BASIC_TYPE:
903                     if ( basic_type_or_reference_type_seen || !CLASSNAME_ENCODINGS.containsKey( token.getValue() ) )
904                     {
905                         if ( runtimeException )
906                         {
907                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
908                         }
909                         else
910                         {
911                             throw createInvalidTokenParseException( tokenizer.input(), token );
912                         }
913                     }
914                     basic_type_or_reference_type_seen = true;
915                     t.className = token.getValue();
916                     t.qualifiedName = token.getValue();
917                     t.simpleName = token.getValue();
918                     t.packageName = "";
919                     t.primitive = true;
920                     break;
921 
922                 case Tokenizer.TK_IDENTIFIER:
923                     if ( basic_type_or_reference_type_seen )
924                     {
925                         if ( runtimeException )
926                         {
927                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
928                         }
929                         else
930                         {
931                             throw createInvalidTokenParseException( tokenizer.input(), token );
932                         }
933                     }
934                     basic_type_or_reference_type_seen = true;
935                     tokenizer.back();
936                     parseReferenceType( tokenizer, t, false, runtimeException );
937                     break;
938 
939                 case Tokenizer.TK_LPAR:
940                     if ( !basic_type_or_reference_type_seen || lpar_seen )
941                     {
942                         if ( runtimeException )
943                         {
944                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
945                         }
946                         else
947                         {
948                             throw createInvalidTokenParseException( tokenizer.input(), token );
949                         }
950                     }
951                     lpar_seen = true;
952                     break;
953 
954                 case Tokenizer.TK_RPAR:
955                     if ( !( basic_type_or_reference_type_seen && lpar_seen ) )
956                     {
957                         if ( runtimeException )
958                         {
959                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
960                         }
961                         else
962                         {
963                             throw createInvalidTokenParseException( tokenizer.input(), token );
964                         }
965                     }
966                     lpar_seen = false;
967                     t.dimension++;
968                     t.className = "[" + t.className;
969                     t.qualifiedName += "[]";
970                     t.simpleName += "[]";
971                     break;
972 
973                 default:
974                     if ( runtimeException )
975                     {
976                         throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
977                     }
978                     else
979                     {
980                         throw createInvalidTokenParseException( tokenizer.input(), token );
981                     }
982 
983             }
984         }
985 
986         if ( !basic_type_or_reference_type_seen || lpar_seen )
987         {
988             if ( runtimeException )
989             {
990                 throw createUnexpectedEndOfInputIllegalArgumentException( tokenizer.input(), tokenizer.length() );
991             }
992             else
993             {
994                 throw createUnexpectedEndOfInputParseException( tokenizer.input(), tokenizer.length() );
995             }
996         }
997 
998         if ( t.dimension > 0 )
999         {
1000             if ( t.primitive )
1001             {
1002                 t.className = new StringBuilder( t.className.length() ).
1003                     append( t.className.substring( 0, t.dimension ) ).
1004                     append( CLASSNAME_ENCODINGS.get( t.className.substring( t.dimension ) ) ).toString();
1005 
1006             }
1007             else
1008             {
1009                 t.className = new StringBuilder( t.className.length() ).
1010                     append( t.className.substring( 0, t.dimension ) ).
1011                     append( "L" ).append( t.className.substring( t.dimension ) ).append( ";" ).toString();
1012 
1013             }
1014         }
1015 
1016         t.arguments = Collections.unmodifiableList( t.getArguments() );
1017     }
1018 
1019     
1020 
1021 
1022 
1023 
1024 
1025 
1026 
1027 
1028     private static void parseReferenceType( final Tokenizer tokenizer, final JavaTypeName t,
1029                                             final boolean in_type_arguments, final boolean runtimeException )
1030         throws ParseException
1031     {
1032         final StringBuilder classNameBuilder = new StringBuilder( tokenizer.input().length() );
1033         final StringBuilder typeNameBuilder = new StringBuilder( tokenizer.input().length() );
1034         boolean identifier_seen = false;
1035         boolean type_arguments_seen = false;
1036         Token token;
1037 
1038         while ( ( token = tokenizer.next() ) != null )
1039         {
1040             switch ( token.getKind() )
1041             {
1042                 case Tokenizer.TK_IDENTIFIER:
1043                     if ( identifier_seen || type_arguments_seen )
1044                     {
1045                         if ( runtimeException )
1046                         {
1047                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1048                         }
1049                         else
1050                         {
1051                             throw createInvalidTokenParseException( tokenizer.input(), token );
1052                         }
1053                     }
1054                     identifier_seen = true;
1055                     type_arguments_seen = false;
1056                     t.simpleName = token.getValue();
1057                     t.packageName = typeNameBuilder.length() > 0
1058                                         ? typeNameBuilder.substring( 0, typeNameBuilder.length() - 1 )
1059                                         : "";
1060 
1061                     classNameBuilder.append( token.getValue() );
1062                     typeNameBuilder.append( token.getValue() );
1063                     break;
1064 
1065                 case Tokenizer.TK_DOT:
1066                     if ( !( identifier_seen || type_arguments_seen ) )
1067                     {
1068                         if ( runtimeException )
1069                         {
1070                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1071                         }
1072                         else
1073                         {
1074                             throw createInvalidTokenParseException( tokenizer.input(), token );
1075                         }
1076                     }
1077                     identifier_seen = false;
1078                     type_arguments_seen = false;
1079                     classNameBuilder.append( token.getValue() );
1080                     typeNameBuilder.append( token.getValue() );
1081                     break;
1082 
1083                 case Tokenizer.TK_LT:
1084                     if ( !identifier_seen )
1085                     {
1086                         if ( runtimeException )
1087                         {
1088                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1089                         }
1090                         else
1091                         {
1092                             throw createInvalidTokenParseException( tokenizer.input(), token );
1093                         }
1094                     }
1095                     identifier_seen = false;
1096                     type_arguments_seen = true;
1097                     tokenizer.back();
1098                     parseTypeArguments( tokenizer, t, runtimeException );
1099                     break;
1100 
1101                 case Tokenizer.TK_LPAR:
1102                     if ( !( identifier_seen || type_arguments_seen ) || in_type_arguments )
1103                     {
1104                         if ( runtimeException )
1105                         {
1106                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1107                         }
1108                         else
1109                         {
1110                             throw createInvalidTokenParseException( tokenizer.input(), token );
1111                         }
1112                     }
1113                     tokenizer.back();
1114                     t.className = classNameBuilder.toString();
1115                     t.qualifiedName = typeNameBuilder.toString();
1116                     return;
1117 
1118                 case Tokenizer.TK_COMMA:
1119                 case Tokenizer.TK_GT:
1120                     if ( !( identifier_seen || type_arguments_seen ) || !in_type_arguments )
1121                     {
1122                         if ( runtimeException )
1123                         {
1124                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1125                         }
1126                         else
1127                         {
1128                             throw createInvalidTokenParseException( tokenizer.input(), token );
1129                         }
1130                     }
1131                     tokenizer.back();
1132                     t.className = classNameBuilder.toString();
1133                     t.qualifiedName = typeNameBuilder.toString();
1134                     return;
1135 
1136                 default:
1137                     if ( runtimeException )
1138                     {
1139                         throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1140                     }
1141                     else
1142                     {
1143                         throw createInvalidTokenParseException( tokenizer.input(), token );
1144                     }
1145 
1146             }
1147         }
1148 
1149         if ( !( identifier_seen || type_arguments_seen ) )
1150         {
1151             if ( runtimeException )
1152             {
1153                 throw createUnexpectedEndOfInputIllegalArgumentException( tokenizer.input(), tokenizer.length() );
1154             }
1155             else
1156             {
1157                 throw createUnexpectedEndOfInputParseException( tokenizer.input(), tokenizer.length() );
1158             }
1159         }
1160 
1161         t.className = classNameBuilder.toString();
1162         t.qualifiedName = typeNameBuilder.toString();
1163     }
1164 
1165     
1166 
1167 
1168 
1169 
1170 
1171 
1172 
1173 
1174     private static void parseTypeArguments( final Tokenizer tokenizer, final JavaTypeName t,
1175                                             final boolean runtimeException )
1176         throws ParseException
1177     {
1178         boolean lt_seen = false;
1179         boolean argument_seen = false;
1180         Token token;
1181 
1182         while ( ( token = tokenizer.next() ) != null )
1183         {
1184             switch ( token.getKind() )
1185             {
1186                 case Tokenizer.TK_LT:
1187                     if ( lt_seen || argument_seen )
1188                     {
1189                         if ( runtimeException )
1190                         {
1191                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1192                         }
1193                         else
1194                         {
1195                             throw createInvalidTokenParseException( tokenizer.input(), token );
1196                         }
1197                     }
1198                     lt_seen = true;
1199                     argument_seen = false;
1200                     break;
1201 
1202                 case Tokenizer.TK_GT:
1203                     if ( !argument_seen )
1204                     {
1205                         if ( runtimeException )
1206                         {
1207                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1208                         }
1209                         else
1210                         {
1211                             throw createInvalidTokenParseException( tokenizer.input(), token );
1212                         }
1213                     }
1214                     return;
1215 
1216                 case Tokenizer.TK_COMMA:
1217                     if ( !argument_seen )
1218                     {
1219                         if ( runtimeException )
1220                         {
1221                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1222                         }
1223                         else
1224                         {
1225                             throw createInvalidTokenParseException( tokenizer.input(), token );
1226                         }
1227                     }
1228                     argument_seen = false;
1229                     break;
1230 
1231                 case Tokenizer.TK_IDENTIFIER:
1232                     if ( !lt_seen || argument_seen )
1233                     {
1234                         if ( runtimeException )
1235                         {
1236                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1237                         }
1238                         else
1239                         {
1240                             throw createInvalidTokenParseException( tokenizer.input(), token );
1241                         }
1242                     }
1243                     argument_seen = true;
1244                     tokenizer.back();
1245                     parseTypeArgument( tokenizer, t, runtimeException );
1246                     break;
1247 
1248                 case Tokenizer.TK_QM:
1249                     if ( !lt_seen || argument_seen )
1250                     {
1251                         if ( runtimeException )
1252                         {
1253                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1254                         }
1255                         else
1256                         {
1257                             throw createInvalidTokenParseException( tokenizer.input(), token );
1258                         }
1259                     }
1260                     argument_seen = true;
1261                     tokenizer.back();
1262                     parseTypeArgument( tokenizer, t, runtimeException );
1263                     break;
1264 
1265                 default:
1266                     if ( runtimeException )
1267                     {
1268                         throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1269                     }
1270                     else
1271                     {
1272                         throw createInvalidTokenParseException( tokenizer.input(), token );
1273                     }
1274 
1275             }
1276         }
1277 
1278         if ( runtimeException )
1279         {
1280             throw createUnexpectedEndOfInputIllegalArgumentException( tokenizer.input(), tokenizer.length() );
1281         }
1282         else
1283         {
1284             throw createUnexpectedEndOfInputParseException( tokenizer.input(), tokenizer.length() );
1285         }
1286     }
1287 
1288     
1289 
1290 
1291 
1292 
1293 
1294 
1295 
1296 
1297 
1298 
1299 
1300 
1301 
1302 
1303 
1304 
1305 
1306 
1307 
1308 
1309     private static void parseTypeArgument( final Tokenizer tokenizer, final JavaTypeName t,
1310                                            final boolean runtimeException )
1311         throws ParseException
1312     {
1313         boolean qm_seen = false;
1314         boolean keyword_seen = false;
1315         Token token;
1316 
1317         final Argument argument = new Argument();
1318         t.getArguments().add( argument );
1319 
1320         while ( ( token = tokenizer.next() ) != null )
1321         {
1322             switch ( token.getKind() )
1323             {
1324                 case Tokenizer.TK_IDENTIFIER:
1325                     if ( qm_seen && !keyword_seen )
1326                     {
1327                         if ( runtimeException )
1328                         {
1329                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1330                         }
1331                         else
1332                         {
1333                             throw createInvalidTokenParseException( tokenizer.input(), token );
1334                         }
1335                     }
1336                     tokenizer.back();
1337                     argument.typeName = new JavaTypeName();
1338                     parseReferenceType( tokenizer, argument.getTypeName(), true, runtimeException );
1339                     return;
1340 
1341                 case Tokenizer.TK_QM:
1342                     if ( qm_seen )
1343                     {
1344                         if ( runtimeException )
1345                         {
1346                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1347                         }
1348                         else
1349                         {
1350                             throw createInvalidTokenParseException( tokenizer.input(), token );
1351                         }
1352                     }
1353                     qm_seen = true;
1354                     argument.wildcard = true;
1355                     break;
1356 
1357                 case Tokenizer.TK_KEYWORD:
1358                     if ( !qm_seen || keyword_seen
1359                              || !( "extends".equals( token.getValue() ) || "super".equals( token.getValue() ) ) )
1360                     {
1361                         if ( runtimeException )
1362                         {
1363                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1364                         }
1365                         else
1366                         {
1367                             throw createInvalidTokenParseException( tokenizer.input(), token );
1368                         }
1369                     }
1370                     keyword_seen = true;
1371                     argument.wildcardBounds = token.getValue();
1372                     break;
1373 
1374                 case Tokenizer.TK_COMMA:
1375                 case Tokenizer.TK_GT:
1376                     if ( !qm_seen || keyword_seen )
1377                     {
1378                         if ( runtimeException )
1379                         {
1380                             throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1381                         }
1382                         else
1383                         {
1384                             throw createInvalidTokenParseException( tokenizer.input(), token );
1385                         }
1386                     }
1387                     tokenizer.back();
1388                     return;
1389 
1390                 default:
1391                     if ( runtimeException )
1392                     {
1393                         throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token );
1394                     }
1395                     else
1396                     {
1397                         throw createInvalidTokenParseException( tokenizer.input(), token );
1398                     }
1399 
1400             }
1401         }
1402 
1403         if ( runtimeException )
1404         {
1405             throw createUnexpectedEndOfInputIllegalArgumentException( tokenizer.input(), tokenizer.length() );
1406         }
1407         else
1408         {
1409             throw createUnexpectedEndOfInputParseException( tokenizer.input(), tokenizer.length() );
1410         }
1411     }
1412 
1413     private static ParseException createInvalidTokenParseException( final String input, final Token token )
1414     {
1415         if ( token.getValue().length() > 1 )
1416         {
1417             return new ParseException( getMessage( "invalidWord", input, token.getValue(),
1418                                                    token.getPosition() ), token.getPosition() );
1419 
1420         }
1421         else
1422         {
1423             return new ParseException( getMessage( "invalidCharacter", input, token.getValue(),
1424                                                    token.getPosition() ), token.getPosition() );
1425 
1426         }
1427     }
1428 
1429     private static IllegalArgumentException createInvalidTokenIllegalArgumentException( final String input,
1430                                                                                         final Token token )
1431     {
1432         if ( token.getValue().length() > 1 )
1433         {
1434             return new IllegalArgumentException( getMessage( "invalidWord", input, token.getValue(),
1435                                                              token.getPosition() ) );
1436 
1437         }
1438         else
1439         {
1440             return new IllegalArgumentException( getMessage( "invalidCharacter", input, token.getValue(),
1441                                                              token.getPosition() ) );
1442 
1443         }
1444     }
1445 
1446     private static ParseException createUnexpectedEndOfInputParseException( final String input,
1447                                                                             final int length )
1448     {
1449         return new ParseException( getMessage( "unexpectedEndOfInput", input, length ), length );
1450     }
1451 
1452     private static IllegalArgumentException createUnexpectedEndOfInputIllegalArgumentException( final String input,
1453                                                                                                 final int length )
1454     {
1455         return new IllegalArgumentException( getMessage( "unexpectedEndOfInput", input, length ) );
1456     }
1457 
1458     private static String getMessage( final String key, final Object... args )
1459     {
1460         return MessageFormat.format( ResourceBundle.getBundle(
1461             JavaTypeName.class.getName().replace( '.', '/' ), Locale.getDefault() ).
1462             getString( key ), args );
1463 
1464     }
1465 
1466     private static final class Token
1467     {
1468 
1469         private int kind;
1470 
1471         private final int position;
1472 
1473         private final String value;
1474 
1475         private Token( final int kind, final int position, final String value )
1476         {
1477             super();
1478             this.kind = kind;
1479             this.position = position;
1480             this.value = value;
1481         }
1482 
1483         private int getKind()
1484         {
1485             return this.kind;
1486         }
1487 
1488         private int getPosition()
1489         {
1490             return this.position;
1491         }
1492 
1493         private String getValue()
1494         {
1495             return this.value;
1496         }
1497 
1498     }
1499 
1500     private static final class Tokenizer
1501     {
1502 
1503         private static final int TK_BASIC_TYPE = 1;
1504 
1505         private static final int TK_KEYWORD = 2;
1506 
1507         private static final int TK_LITERAL = 3;
1508 
1509         private static final int TK_IDENTIFIER = 4;
1510 
1511         private static final int TK_LPAR = 5;
1512 
1513         private static final int TK_RPAR = 6;
1514 
1515         private static final int TK_LT = 7;
1516 
1517         private static final int TK_GT = 8;
1518 
1519         private static final int TK_COMMA = 9;
1520 
1521         private static final int TK_DOT = 10;
1522 
1523         private static final int TK_QM = 11;
1524 
1525         private final String input;
1526 
1527         private int token;
1528 
1529         private final List<Token> tokens;
1530 
1531         private int length;
1532 
1533         private Tokenizer( final String input, final boolean runtimeException ) throws ParseException
1534         {
1535             super();
1536             this.input = input;
1537             this.token = 0;
1538             this.tokens = tokenize( input, runtimeException );
1539 
1540             if ( !this.tokens.isEmpty() )
1541             {
1542                 final Token last = this.tokens.get( this.tokens.size() - 1 );
1543                 this.length = last.getPosition() + last.getValue().length();
1544             }
1545         }
1546 
1547         private String input()
1548         {
1549             return this.input;
1550         }
1551 
1552         private Token next()
1553         {
1554             final int idx = this.token++;
1555             return idx < this.tokens.size() ? this.tokens.get( idx ) : null;
1556         }
1557 
1558         private void back()
1559         {
1560             this.token--;
1561         }
1562 
1563         private int length()
1564         {
1565             return this.length;
1566         }
1567 
1568         private static List<Token> tokenize( final String input, final boolean runtimeException )
1569             throws ParseException
1570         {
1571             final List<Token> list = new LinkedList<Token>();
1572             final ParsePosition pos = new ParsePosition( 0 );
1573 
1574             for ( Token t = nextToken( pos, input, runtimeException );
1575                   t != null;
1576                   t = nextToken( pos, input, runtimeException ) )
1577             {
1578                 list.add( t );
1579             }
1580 
1581             return Collections.unmodifiableList( list );
1582         }
1583 
1584         private static Token nextToken( final ParsePosition pos, final String str, final boolean runtimeException )
1585             throws ParseException
1586         {
1587             for ( final int s0 = str.length(); pos.getIndex() < s0; pos.setIndex( pos.getIndex() + 1 ) )
1588             {
1589                 if ( !Character.isWhitespace( str.charAt( pos.getIndex() ) ) )
1590                 {
1591                     break;
1592                 }
1593             }
1594 
1595             int idx = pos.getIndex();
1596             Token token = null;
1597 
1598             if ( idx < str.length() )
1599             {
1600                 
1601                 switch ( str.charAt( idx ) )
1602                 {
1603                     case ',':
1604                         token = new Token( TK_COMMA, idx, "," );
1605                         pos.setIndex( idx + 1 );
1606                         break;
1607                     case '.':
1608                         token = new Token( TK_DOT, idx, "." );
1609                         pos.setIndex( idx + 1 );
1610                         break;
1611                     case '<':
1612                         token = new Token( TK_LT, idx, "<" );
1613                         pos.setIndex( idx + 1 );
1614                         break;
1615                     case '>':
1616                         token = new Token( TK_GT, idx, ">" );
1617                         pos.setIndex( idx + 1 );
1618                         break;
1619                     case '[':
1620                         token = new Token( TK_LPAR, idx, "[" );
1621                         pos.setIndex( idx + 1 );
1622                         break;
1623                     case ']':
1624                         token = new Token( TK_RPAR, idx, "]" );
1625                         pos.setIndex( idx + 1 );
1626                         break;
1627                     case '?':
1628                         token = new Token( TK_QM, idx, "?" );
1629                         pos.setIndex( idx + 1 );
1630                         break;
1631 
1632                     default:
1633                         token = null;
1634 
1635                 }
1636 
1637                 
1638                 if ( token == null )
1639                 {
1640                     for ( final String basicType : JavaLanguage.BASIC_TYPES )
1641                     {
1642                         if ( str.substring( idx ).startsWith( basicType ) )
1643                         {
1644                             idx += basicType.length();
1645 
1646                             if ( idx >= str.length()
1647                                      || !Character.isJavaIdentifierPart( str.charAt( idx ) ) )
1648                             {
1649                                 token = new Token( TK_BASIC_TYPE, pos.getIndex(), basicType );
1650                                 pos.setIndex( idx );
1651                                 break;
1652                             }
1653 
1654                             idx -= basicType.length();
1655                         }
1656                     }
1657                 }
1658 
1659                 
1660                 if ( token == null )
1661                 {
1662                     for ( final String keyword : JavaLanguage.KEYWORDS )
1663                     {
1664                         if ( str.substring( idx ).startsWith( keyword ) )
1665                         {
1666                             idx += keyword.length();
1667 
1668                             if ( idx >= str.length()
1669                                      || !Character.isJavaIdentifierPart( str.charAt( idx ) ) )
1670                             {
1671                                 token = new Token( TK_KEYWORD, pos.getIndex(), keyword );
1672                                 pos.setIndex( idx );
1673                                 break;
1674                             }
1675 
1676                             idx -= keyword.length();
1677                         }
1678                     }
1679                 }
1680 
1681                 
1682                 if ( token == null )
1683                 {
1684                     for ( final String literal : JavaLanguage.BOOLEAN_LITERALS )
1685                     {
1686                         if ( str.substring( idx ).startsWith( literal ) )
1687                         {
1688                             idx += literal.length();
1689 
1690                             if ( idx >= str.length()
1691                                      || !Character.isJavaIdentifierPart( str.charAt( idx ) ) )
1692                             {
1693                                 token = new Token( TK_LITERAL, pos.getIndex(), literal );
1694                                 pos.setIndex( idx );
1695                                 break;
1696                             }
1697 
1698                             idx -= literal.length();
1699                         }
1700                     }
1701                 }
1702 
1703                 
1704                 if ( token == null )
1705                 {
1706                     if ( str.substring( idx ).startsWith( JavaLanguage.NULL_LITERAL ) )
1707                     {
1708                         idx += JavaLanguage.NULL_LITERAL.length();
1709 
1710                         if ( idx >= str.length()
1711                                  || !Character.isJavaIdentifierPart( str.charAt( idx ) ) )
1712                         {
1713                             token = new Token( TK_LITERAL, pos.getIndex(), JavaLanguage.NULL_LITERAL );
1714                             pos.setIndex( idx );
1715                         }
1716                         else
1717                         {
1718                             idx -= JavaLanguage.NULL_LITERAL.length();
1719                         }
1720                     }
1721                 }
1722 
1723                 
1724                 if ( token == null )
1725                 {
1726                     for ( final int s0 = str.length(); idx < s0; idx++ )
1727                     {
1728                         if ( !( idx == pos.getIndex()
1729                                 ? Character.isJavaIdentifierStart( str.charAt( idx ) )
1730                                 : Character.isJavaIdentifierPart( str.charAt( idx ) ) ) )
1731                         {
1732                             break;
1733                         }
1734                     }
1735 
1736                     if ( idx != pos.getIndex() )
1737                     {
1738                         token = new Token( TK_IDENTIFIER, pos.getIndex(), str.substring( pos.getIndex(), idx ) );
1739                         pos.setIndex( idx );
1740                     }
1741                 }
1742 
1743                 if ( token == null )
1744                 {
1745                     final Token invalidToken =
1746                         new Token( Integer.MIN_VALUE, idx, Character.toString( str.charAt( idx ) ) );
1747 
1748                     if ( runtimeException )
1749                     {
1750                         throw createInvalidTokenIllegalArgumentException( str, invalidToken );
1751                     }
1752                     else
1753                     {
1754                         throw createInvalidTokenParseException( str, invalidToken );
1755                     }
1756                 }
1757             }
1758 
1759             return token;
1760         }
1761 
1762     }
1763 
1764 }