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 }