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 }