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