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