KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > core > ext > typeinfo > TypeOracle


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.core.ext.typeinfo;
17
18 import com.google.gwt.core.ext.TreeLogger;
19 import com.google.gwt.core.ext.UnableToCompleteException;
20
21 import java.util.ArrayList JavaDoc;
22 import java.util.Arrays JavaDoc;
23 import java.util.Comparator JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.IdentityHashMap JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Set JavaDoc;
31
32 /**
33  * Provides type-related information about a set of source files, including doc
34  * comment metadata.
35  * <p>
36  * All type objects exposed, such as
37  * {@link com.google.gwt.core.ext.typeinfo.JClassType} and others, have a stable
38  * identity relative to this type oracle instance. Consequently, you can
39  * reliably compare object identity of any objects this type oracle produces.
40  * For example, the following code relies on this stable identity guarantee:
41  *
42  * <pre>
43  * JClassType o = typeOracle.getJavaLangObject();
44  * JClassType s1 = typeOracle.getType(&quot;java.lang.String&quot;);
45  * JClassType s2 = typeOracle.getType(&quot;java.lang.String&quot;);
46  * assert(s1 == s2);
47  * assert(o == s1.getSuperclass());
48  * JParameterizedType ls = typeOracle.parse(&quot;java.util.List&lt;java.lang.String&gt;&quot;);
49  * assert(ls.getTypeArgs()[0] == s1);
50  * </pre>
51  *
52  * </p>
53  */

54 public class TypeOracle {
55
56   /**
57    * A reserved metadata tag to indicates that a field type, method return type
58    * or method parameter type is intended to be parameterized. Note that
59    * constructor type parameters are not supported at present.
60    */

61   public static final String JavaDoc TAG_TYPEARGS = "gwt.typeArgs";
62
63   static final int MOD_ABSTRACT = 0x00000001;
64   static final int MOD_FINAL = 0x00000002;
65   static final int MOD_NATIVE = 0x00000004;
66   static final int MOD_PRIVATE = 0x00000008;
67   static final int MOD_PROTECTED = 0x00000010;
68   static final int MOD_PUBLIC = 0x00000020;
69   static final int MOD_STATIC = 0x00000040;
70   static final int MOD_TRANSIENT = 0x00000080;
71   static final int MOD_VOLATILE = 0x00000100;
72
73   static final JClassType[] NO_JCLASSES = new JClassType[0];
74   static final JConstructor[] NO_JCTORS = new JConstructor[0];
75   static final JField[] NO_JFIELDS = new JField[0];
76   static final JMethod[] NO_JMETHODS = new JMethod[0];
77   static final JPackage[] NO_JPACKAGES = new JPackage[0];
78   static final JParameter[] NO_JPARAMS = new JParameter[0];
79   static final JType[] NO_JTYPES = new JType[0];
80   static final String JavaDoc[][] NO_STRING_ARR_ARR = new String JavaDoc[0][];
81   static final String JavaDoc[] NO_STRINGS = new String JavaDoc[0];
82
83   static String JavaDoc combine(String JavaDoc[] strings, int startIndex) {
84     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
85     for (int i = startIndex; i < strings.length; i++) {
86       String JavaDoc s = strings[i];
87       sb.append(s);
88     }
89     return sb.toString();
90   }
91
92   static String JavaDoc[] modifierBitsToNames(int bits) {
93     List JavaDoc strings = new ArrayList JavaDoc();
94
95     // The order is based on the order in which we want them to appear.
96
//
97
if (0 != (bits & MOD_PUBLIC)) {
98       strings.add("public");
99     }
100
101     if (0 != (bits & MOD_PRIVATE)) {
102       strings.add("private");
103     }
104
105     if (0 != (bits & MOD_PROTECTED)) {
106       strings.add("protected");
107     }
108
109     if (0 != (bits & MOD_STATIC)) {
110       strings.add("static");
111     }
112
113     if (0 != (bits & MOD_ABSTRACT)) {
114       strings.add("abstract");
115     }
116
117     if (0 != (bits & MOD_FINAL)) {
118       strings.add("final");
119     }
120
121     if (0 != (bits & MOD_NATIVE)) {
122       strings.add("native");
123     }
124
125     if (0 != (bits & MOD_TRANSIENT)) {
126       strings.add("transient");
127     }
128
129     if (0 != (bits & MOD_VOLATILE)) {
130       strings.add("volatile");
131     }
132
133     return (String JavaDoc[]) strings.toArray(NO_STRINGS);
134   }
135
136   /**
137    * Returns true if the type has been invalidated because it is in the set of
138    * invalid types or if it is a parameterized type and either it raw type or
139    * any one of its type args has been invalidated.
140    *
141    * @param type type to check
142    * @param invalidTypes set of type known to be invalid
143    * @return true if the type has been invalidated
144    */

145   private static boolean isInvalidatedTypeRecursive(JType type, Set JavaDoc invalidTypes) {
146     if (type instanceof JParameterizedType) {
147       JParameterizedType parameterizedType = (JParameterizedType) type;
148       if (isInvalidatedTypeRecursive(parameterizedType.getRawType(),
149           invalidTypes)) {
150         return true;
151       }
152
153       JType[] typeArgs = parameterizedType.getTypeArgs();
154       for (int i = 0; i < typeArgs.length; ++i) {
155         JType typeArg = typeArgs[i];
156
157         if (isInvalidatedTypeRecursive(typeArg, invalidTypes)) {
158           return true;
159         }
160       }
161
162       return false;
163     } else {
164       return invalidTypes.contains(type);
165     }
166   }
167
168   private final Map JavaDoc arrayTypes = new IdentityHashMap JavaDoc();
169
170   private JClassType javaLangObject;
171
172   private final Map JavaDoc packages = new HashMap JavaDoc();
173
174   private final Map JavaDoc parameterizedTypes = new HashMap JavaDoc();
175
176   private int reloadCount = 0;
177
178   private final Map JavaDoc typesByCup = new IdentityHashMap JavaDoc();
179
180   public TypeOracle() {
181     // Always create the default package.
182
//
183
getOrCreatePackage("");
184   }
185
186   /**
187    * Attempts to find a package by name. All requests for the same package
188    * return the same package object.
189    *
190    * @return <code>null</code> if the package could not be found
191    */

192   public JPackage findPackage(String JavaDoc pkgName) {
193     return (JPackage) packages.get(pkgName);
194   }
195
196   /**
197    * Finds a class or interface given its fully-qualified name. For nested
198    * classes, use its source name rather than its binary name (that is, use a
199    * "." rather than a "$").
200    *
201    * @return <code>null</code> if the type is not found
202    */

203   public JClassType findType(String JavaDoc name) {
204     // Try the dotted pieces, right to left.
205
//
206
int i = name.length() - 1;
207     while (i >= 0) {
208       int dot = name.lastIndexOf('.', i);
209       String JavaDoc pkgName = "";
210       String JavaDoc typeName = name;
211       if (dot != -1) {
212         pkgName = name.substring(0, dot);
213         typeName = name.substring(dot + 1);
214         i = dot - 1;
215       } else {
216         i = -1;
217       }
218       JClassType result = findType(pkgName, typeName);
219       if (result != null) {
220         return result;
221       }
222     }
223     return null;
224   }
225
226   /**
227    * Finds a type given its package-relative name. For nested classes, use its
228    * source name rather than its binary name (that is, use a "." rather than a
229    * "$").
230    *
231    * @return <code>null</code> if the type is not found
232    */

233   public JClassType findType(String JavaDoc pkgName, String JavaDoc typeName) {
234     JPackage pkg = findPackage(pkgName);
235     if (pkg != null) {
236       JClassType type = pkg.findType(typeName);
237       if (type != null) {
238         return type;
239       }
240     }
241     return null;
242   }
243
244   /**
245    * Gets the type object that represents an array of the specified type. The
246    * returned type always has a stable identity so as to guarantee that all
247    * calls to this method with the same argument return the same object.
248    *
249    * @param componentType the component type of the array, which can itself be
250    * an array type
251    * @return a type object representing an array of the component type
252    */

253   public JArrayType getArrayType(JType componentType) {
254     JArrayType arrayType = (JArrayType) arrayTypes.get(componentType);
255     if (arrayType == null) {
256       arrayType = new JArrayType(componentType);
257       arrayTypes.put(componentType, arrayType);
258     }
259     return arrayType;
260   }
261
262   /**
263    * Gets a reference to the type object representing
264    * <code>java.lang.Object</code>.
265    */

266   public JClassType getJavaLangObject() {
267     return javaLangObject;
268   }
269
270   /**
271    * Ensure that a package with the specified name exists as well as its parent
272    * packages.
273    */

274   public JPackage getOrCreatePackage(String JavaDoc name) {
275     int i = name.lastIndexOf('.');
276     if (i != -1) {
277       // Ensure the parent package is also created.
278
//
279
getOrCreatePackage(name.substring(0, i));
280     }
281
282     JPackage pkg = (JPackage) packages.get(name);
283     if (pkg == null) {
284       pkg = new JPackage(name);
285       packages.put(name, pkg);
286     }
287     return pkg;
288   }
289
290   /**
291    * Gets a package by name. All requests for the same package return the same
292    * package object.
293    *
294    * @return the package object associated with the specified name
295    */

296   public JPackage getPackage(String JavaDoc pkgName) throws NotFoundException {
297     JPackage result = findPackage(pkgName);
298     if (result == null) {
299       throw new NotFoundException(pkgName);
300     }
301     return result;
302   }
303
304   /**
305    * Gets an array of all packages known to this type oracle.
306    *
307    * @return an array of packages, possibly of zero-length
308    */

309   public JPackage[] getPackages() {
310     return (JPackage[]) packages.values().toArray(NO_JPACKAGES);
311   }
312
313   /**
314    * Gets the parameterized type object that represents the combination of a
315    * specified raw type and a set of type arguments. The returned type always
316    * has a stable identity so as to guarantee that all calls to this method with
317    * the same arguments return the same object.
318    *
319    * @param rawType the raw type of the array, which must be a class or
320    * interface type and cannot be a primitive, array, or another
321    * parameterized type
322    * @param typeArgs the type arguments bound to the specified raw type
323    * @return a type object representing this particular binding of type
324    * arguments to the specified raw type
325    */

326   public JType getParameterizedType(JClassType rawType, JType[] typeArgs) {
327     // Uses the generated string signature to intern parameterized types.
328
//
329
JParameterizedType parameterized = new JParameterizedType(rawType);
330     for (int i = 0; i < typeArgs.length; i++) {
331       parameterized.addTypeArg(typeArgs[i]);
332     }
333     String JavaDoc sig = parameterized.getParameterizedQualifiedSourceName();
334     JParameterizedType existing = (JParameterizedType) parameterizedTypes.get(sig);
335     if (existing == null) {
336       parameterizedTypes.put(sig, parameterized);
337       existing = parameterized;
338     }
339     return existing;
340   }
341
342   public long getReloadCount() {
343     return reloadCount;
344   }
345
346   /**
347    * Finds a type given its fully qualified name. For nested classes, use its
348    * source name rather than its binary name (that is, use a "." rather than a
349    * "$").
350    *
351    * @return the specified type
352    */

353   public JClassType getType(String JavaDoc name) throws NotFoundException {
354     JClassType type = findType(name);
355     if (type == null) {
356       throw new NotFoundException(name);
357     }
358     return type;
359   }
360
361   /**
362    * Finds a type given its package-relative name. For nested classes, use its
363    * source name rather than its binary name (that is, use a "." rather than a
364    * "$").
365    *
366    * @return the specified type
367    */

368   public JClassType getType(String JavaDoc pkgName, String JavaDoc topLevelTypeSimpleName)
369       throws NotFoundException {
370     JClassType type = findType(pkgName, topLevelTypeSimpleName);
371     if (type == null) {
372       throw new NotFoundException(pkgName + "." + topLevelTypeSimpleName);
373     }
374     return type;
375   }
376
377   /**
378    * Gets all types, both top-level and nested.
379    *
380    * @return an array of types, possibly of zero length
381    */

382   public JClassType[] getTypes() {
383     Set JavaDoc allTypes = new HashSet JavaDoc();
384     JPackage[] pkgs = getPackages();
385     for (int i = 0; i < pkgs.length; i++) {
386       JPackage pkg = pkgs[i];
387       JClassType[] types = pkg.getTypes();
388       for (int j = 0; j < types.length; j++) {
389         JClassType type = types[j];
390         buildAllTypesImpl(allTypes, type);
391       }
392     }
393     return (JClassType[]) allTypes.toArray(NO_JCLASSES);
394   }
395
396   public JClassType[] getTypesInCompilationUnit(CompilationUnitProvider cup) {
397     JClassType[] types = (JClassType[]) typesByCup.get(cup);
398     if (types != null) {
399       return types;
400     } else {
401       return NO_JCLASSES;
402     }
403   }
404
405   /**
406    * Parses the string form of a type to produce the corresponding type object.
407    * The types that can be parsed include primitives, class and interface names,
408    * simple parameterized types (those without wildcards or bounds), and arrays
409    * of the preceding.
410    * <p>
411    * Examples of types that can be parsed by this method.
412    * <ul>
413    * <li><code>int</code></li>
414    * <li><code>java.lang.Object</code></li>
415    * <li><code>java.lang.String[]</code></li>
416    * <li><code>char[][]</code></li>
417    * <li><code>void</code></li>
418    * <li><code>List&lt;Shape&gt;</code></li>
419    * <li><code>List&lt;List&lt;Shape&gt;&gt;</code></li>
420    * </ul>
421    * </p>
422    *
423    * @param type a type signature to be parsed
424    * @return the type object corresponding to the parse type
425    */

426   public JType parse(String JavaDoc type) throws TypeOracleException {
427     // Remove all internal and external whitespace.
428
//
429
type = type.replaceAll("\\\\s", "");
430
431     // Recursively parse.
432
//
433
return parseImpl(type);
434   }
435
436   /**
437    * Convenience method to sort class types in a consistent way. Note that the
438    * order is subject to change and is intended to generate an "aesthetically
439    * pleasing" order rather than a computationally reliable order.
440    */

441   public void sort(JClassType[] types) {
442     Arrays.sort(types, new Comparator JavaDoc() {
443       public int compare(Object JavaDoc type1, Object JavaDoc type2) {
444         String JavaDoc name1 = ((JClassType) type1).getQualifiedSourceName();
445         String JavaDoc name2 = ((JClassType) type2).getQualifiedSourceName();
446         return name1.compareTo(name2);
447       }
448     });
449   }
450
451   /**
452    * Convenience method to sort constructors in a consistent way. Note that the
453    * order is subject to change and is intended to generate an "aesthetically
454    * pleasing" order rather than a computationally reliable order.
455    */

456   public void sort(JConstructor[] ctors) {
457     Arrays.sort(ctors, new Comparator JavaDoc() {
458       public int compare(Object JavaDoc o1, Object JavaDoc o2) {
459         // Nothing for now; could enhance to sort based on parameter list
460
return 0;
461       }
462     });
463   }
464
465   /**
466    * Convenience method to sort fields in a consistent way. Note that the order
467    * is subject to change and is intended to generate an "aesthetically
468    * pleasing" order rather than a computationally reliable order.
469    */

470   public void sort(JField[] fields) {
471     Arrays.sort(fields, new Comparator JavaDoc() {
472       public int compare(Object JavaDoc o1, Object JavaDoc o2) {
473         final JField f1 = ((JField) o1);
474         final JField f2 = ((JField) o2);
475         String JavaDoc name1 = f1.getName();
476         String JavaDoc name2 = f2.getName();
477         return name1.compareTo(name2);
478       }
479     });
480   }
481
482   /**
483    * Convenience method to sort methods in a consistent way. Note that the order
484    * is subject to change and is intended to generate an "aesthetically
485    * pleasing" order rather than a computationally reliable order.
486    */

487   public void sort(JMethod[] methods) {
488     Arrays.sort(methods, new Comparator JavaDoc() {
489       public int compare(Object JavaDoc o1, Object JavaDoc o2) {
490         final JMethod m1 = ((JMethod) o1);
491         final JMethod m2 = ((JMethod) o2);
492         String JavaDoc name1 = m1.getName();
493         String JavaDoc name2 = m2.getName();
494         return name1.compareTo(name2);
495       }
496     });
497   }
498
499   void incrementReloadCount() {
500     reloadCount++;
501   }
502
503   /**
504    * Note, this method is called reflectively from the
505    * {@link CacheManager#invalidateOnRefresh(TypeOracle)}
506    *
507    * @param cup compilation unit whose types will be invalidated
508    */

509   void invalidateTypesInCompilationUnit(CompilationUnitProvider cup) {
510     Set JavaDoc invalidTypes = new HashSet JavaDoc();
511     JClassType[] types = (JClassType[]) typesByCup.get(cup);
512     if (types == null) {
513       return;
514     }
515
516     for (int i = 0; i < types.length; i++) {
517       JClassType classTypeToInvalidate = types[i];
518       invalidTypes.add(classTypeToInvalidate);
519     }
520
521     typesByCup.remove(cup);
522
523     removeInvalidatedArrayTypes(invalidTypes);
524
525     removeInvalidatedParameterizedTypes(invalidTypes);
526
527     removeTypes(invalidTypes);
528   }
529
530   void recordTypeInCompilationUnit(CompilationUnitProvider cup, JClassType type) {
531     JClassType[] types = (JClassType[]) typesByCup.get(cup);
532     if (types == null) {
533       types = new JClassType[] {type};
534     } else {
535       JClassType[] temp = new JClassType[types.length + 1];
536       System.arraycopy(types, 0, temp, 0, types.length);
537       temp[types.length] = type;
538       types = temp;
539     }
540     typesByCup.put(cup, types);
541   }
542
543   /**
544    * Updates relationships within this type oracle. Should be called after any
545    * changes are made.
546    *
547    * <p>
548    * Throws <code>TypeOracleException</code> thrown if fundamental baseline
549    * correctness criteria are violated, most notably the absence of
550    * "java.lang.Object"
551    * </p>
552    */

553   void refresh(TreeLogger logger) throws NotFoundException {
554     if (javaLangObject == null) {
555       javaLangObject = findType("java.lang.Object");
556       if (javaLangObject == null) {
557         throw new NotFoundException("java.lang.Object");
558       }
559     }
560     computeHierarchyRelationships();
561     consumeTypeArgMetaData(logger);
562   }
563
564   private void buildAllTypesImpl(Set JavaDoc allTypes, JClassType type) {
565     boolean didAdd = allTypes.add(type);
566     assert (didAdd);
567     JClassType[] nestedTypes = type.getNestedTypes();
568     for (int i = 0; i < nestedTypes.length; i++) {
569       JClassType nestedType = nestedTypes[i];
570       buildAllTypesImpl(allTypes, nestedType);
571     }
572   }
573
574   private void computeHierarchyRelationships() {
575     // For each type, walk up its hierarchy chain and tell each supertype
576
// about its subtype.
577
//
578
JClassType[] allTypes = getTypes();
579     for (int i = 0; i < allTypes.length; i++) {
580       JClassType type = allTypes[i];
581       type.notifySuperTypes();
582     }
583   }
584
585   private void consumeTypeArgMetaData(TreeLogger logger) {
586     logger = logger.branch(TreeLogger.DEBUG, "Examining " + TAG_TYPEARGS
587         + " tags", null);
588     consumeTypeArgMetaData(logger, getTypes());
589   }
590
591   private void consumeTypeArgMetaData(TreeLogger logger, JClassType[] types) {
592     for (int i = 0; i < types.length; i++) {
593       JClassType type = types[i];
594       // CTORS not supported yet
595

596       TreeLogger branch = logger.branch(TreeLogger.DEBUG, "Type "
597           + type.getQualifiedSourceName(), null);
598
599       consumeTypeArgMetaData(branch, type.getMethods());
600       consumeTypeArgMetaData(branch, type.getFields());
601     }
602   }
603
604   private void consumeTypeArgMetaData(TreeLogger logger, JField[] fields) {
605     TreeLogger branch;
606     for (int i = 0; i < fields.length; i++) {
607       JField field = fields[i];
608
609       String JavaDoc[][] tokensArray = field.getMetaData(TAG_TYPEARGS);
610       if (tokensArray.length == 0) {
611         // No tag.
612
continue;
613       }
614
615       try {
616         String JavaDoc msg = "Field " + field.getName();
617         branch = logger.branch(TreeLogger.TRACE, msg, null);
618
619         if (tokensArray.length > 1) {
620           // Too many.
621
branch.log(TreeLogger.WARN, "Metadata error on field '"
622               + field.getName() + "' in type '" + field.getEnclosingType()
623               + "': expecting at most one " + TAG_TYPEARGS
624               + " (the last one will be used)", null);
625         }
626
627         // (1) Parse it.
628
// (2) Update the field's type.
629
// If it wasn't a valid parameterized type, parse() would've thrown.
630
//
631
JType fieldType = field.getType();
632         String JavaDoc[] token = tokensArray[tokensArray.length - 1];
633         JType resultingType = determineActualType(branch, fieldType, token, 0);
634         field.setType(resultingType);
635       } catch (UnableToCompleteException e) {
636         // Continue; the problem will have been logged.
637
//
638
}
639     }
640   }
641
642   private void consumeTypeArgMetaData(TreeLogger logger, JMethod[] methods) {
643     TreeLogger branch;
644     for (int i = 0; i < methods.length; i++) {
645       JMethod method = methods[i];
646
647       String JavaDoc[][] tokensArray = method.getMetaData(TAG_TYPEARGS);
648       if (tokensArray.length == 0) {
649         // No tag.
650
continue;
651       }
652       try {
653         String JavaDoc msg = "Method " + method.getReadableDeclaration();
654         branch = logger.branch(TreeLogger.TRACE, msg, null);
655
656         // Okay, parse each one and correlate it to a part of the decl.
657
//
658
boolean returnTypeHandled = false;
659         Set JavaDoc paramsAlreadySet = new HashSet JavaDoc();
660         for (int j = 0; j < tokensArray.length; j++) {
661           String JavaDoc[] tokens = tokensArray[j];
662           // It is either referring to the return type or a parameter type.
663
//
664
if (tokens.length == 0) {
665             // Expecting at least something.
666
//
667
branch.log(TreeLogger.WARN,
668                 "Metadata error: expecting tokens after " + TAG_TYPEARGS, null);
669             throw new UnableToCompleteException();
670           }
671
672           // See if the first token is a parameter name.
673
//
674
JParameter param = method.findParameter(tokens[0]);
675           if (param != null) {
676             if (!paramsAlreadySet.contains(param)) {
677               // These are type args for a param.
678
//
679
JType resultingType = determineActualType(branch,
680                   param.getType(), tokens, 1);
681               param.setType(resultingType);
682               paramsAlreadySet.add(param);
683             } else {
684               // This parameter type has already been set.
685
//
686
msg = "Metadata error: duplicate attempt to specify type args for parameter '"
687                   + param.getName() + "'";
688               branch.log(TreeLogger.WARN, msg, null);
689               throw new UnableToCompleteException();
690             }
691           } else {
692             // It's either referring to the return type or a bad param name.
693
//
694
if (!returnTypeHandled) {
695               JType resultingType = determineActualType(branch,
696                   method.getReturnType(), tokens, 0);
697               method.setReturnType(resultingType);
698               returnTypeHandled = true;
699             } else {
700               // The return type has already been set.
701
//
702
msg = "Metadata error: duplicate attempt to specify type args for the return type";
703               branch.log(TreeLogger.WARN, msg, null);
704             }
705           }
706         }
707       } catch (UnableToCompleteException e) {
708         // Continue; will already have been logged.
709
//
710
}
711     }
712   }
713
714   /*
715    * Given a declared type and some number of type arguments determine what the
716    * actual type should be.
717    */

718   private JType determineActualType(TreeLogger logger, JType declType,
719       String JavaDoc[] tokens, int startIndex) throws UnableToCompleteException {
720     // These are type args for a param.
721
//
722
JType leafType = declType.getLeafType();
723     String JavaDoc typeName = leafType.getQualifiedSourceName();
724     JType resultingType = parseTypeArgTokens(logger, typeName, tokens,
725         startIndex);
726     JArrayType arrayType = declType.isArray();
727     if (arrayType != null) {
728       arrayType.setLeafType(resultingType);
729
730       return declType;
731     }
732
733     return resultingType;
734   }
735
736   private JType parseImpl(String JavaDoc type) throws NotFoundException,
737       ParseException, BadTypeArgsException {
738     if (type.endsWith("[]")) {
739       String JavaDoc remainder = type.substring(0, type.length() - 2);
740       JType componentType = parseImpl(remainder);
741       return getArrayType(componentType);
742     }
743
744     if (type.endsWith(">")) {
745       int bracket = type.indexOf('<');
746       if (bracket == -1) {
747         throw new ParseException(
748             "Mismatched brackets; expected '<' to match subsequent '>'");
749       }
750
751       // Resolve the raw type.
752
//
753
String JavaDoc rawTypeName = type.substring(0, bracket);
754       JType rawType = parseImpl(rawTypeName);
755       if (rawType.isParameterized() != null) {
756         // The raw type cannot itself be parmeterized.
757
//
758
throw new BadTypeArgsException(
759             "Only non-parameterized classes and interface can be parameterized");
760       } else if (rawType.isClassOrInterface() == null) {
761         // The raw type must be a class or interface
762
// (not an array or primitive).
763
//
764
throw new BadTypeArgsException(
765             "Only classes and interface can be parameterized, so "
766                 + rawType.getQualifiedSourceName()
767                 + " cannot be used in this context");
768       }
769
770       // Resolve each type argument.
771
//
772
String JavaDoc typeArgContents = type.substring(bracket + 1, type.length() - 1);
773       JType[] typeArgs = parseTypeArgContents(typeArgContents);
774
775       // Intern this type.
776
//
777
return getParameterizedType(rawType.isClassOrInterface(), typeArgs);
778     }
779
780     JType result = JPrimitiveType.valueOf(type);
781     if (result != null) {
782       return result;
783     }
784
785     result = findType(type);
786     if (result != null) {
787       return result;
788     }
789
790     throw new NotFoundException(type);
791   }
792
793   private void parseTypeArgComponent(List JavaDoc typeArgList, String JavaDoc typeArgComponent)
794       throws NotFoundException, ParseException, BadTypeArgsException {
795     JType typeArg = parseImpl(typeArgComponent);
796     if (typeArg.isPrimitive() != null) {
797       // Cannot be primitive.
798
//
799
throw new BadTypeArgsException("Type arguments cannot be primitive, so "
800           + typeArg.getQualifiedSourceName()
801           + " cannot be used in this context");
802     }
803
804     typeArgList.add(typeArg);
805   }
806
807   /**
808    * Returns an array of types specified inside of a gwt.typeArgs javadoc
809    * annotation.
810    */

811   private JType[] parseTypeArgContents(String JavaDoc typeArgContents)
812       throws ParseException, NotFoundException, BadTypeArgsException {
813     List JavaDoc typeArgList = new ArrayList JavaDoc();
814
815     int start = 0;
816     for (int offset = 0, length = typeArgContents.length(); offset < length; ++offset) {
817       char ch = typeArgContents.charAt(offset);
818       switch (ch) {
819         case '<':
820           // scan for closing '>' while ignoring commas
821
for (int depth = 1; depth > 0; ) {
822             if (++offset == length) {
823               throw new ParseException(
824               "Mismatched brackets; expected '<' to match subsequent '>'");
825             }
826             
827             char ich = typeArgContents.charAt(offset);
828             if (ich == '<') {
829               ++depth;
830             } else if (ich == '>') {
831               --depth;
832             }
833           }
834           break;
835         case '>':
836           throw new ParseException("No matching '<' for '>'");
837         case ',':
838           String JavaDoc typeArgComponent = typeArgContents.substring(start, offset);
839           parseTypeArgComponent(typeArgList, typeArgComponent);
840           start = offset + 1;
841           break;
842         default:
843           break;
844       }
845     }
846
847     String JavaDoc typeArgComponent = typeArgContents.substring(start);
848     parseTypeArgComponent(typeArgList, typeArgComponent);
849
850     JType[] typeArgs = (JType[]) typeArgList.toArray(new JType[typeArgList.size()]);
851     return typeArgs;
852   }
853
854   private JType parseTypeArgTokens(TreeLogger logger, String JavaDoc maybeRawType,
855       String JavaDoc[] tokens, int startIndex) throws UnableToCompleteException {
856     String JavaDoc munged = combine(tokens, startIndex).trim();
857     String JavaDoc toParse = maybeRawType + munged;
858     JType parameterizedType;
859     try {
860       parameterizedType = parse(toParse);
861     } catch (TypeOracleException e) {
862       String JavaDoc msg = "Unable to recognize '" + toParse
863           + "' as a type name (is it fully qualified?)";
864       logger.log(TreeLogger.WARN, msg, null);
865       throw new UnableToCompleteException();
866     }
867     return parameterizedType;
868   }
869
870   /**
871    * Remove any array type whose leaf type has been invalidated.
872    *
873    * @param invalidTypes set of types that have been invalidated.
874    */

875   private void removeInvalidatedArrayTypes(Set JavaDoc invalidTypes) {
876     arrayTypes.keySet().removeAll(invalidTypes);
877   }
878
879   /**
880    * Remove any parameterized type that was invalidated because either its raw
881    * type or any one of its type arguements was invalidated.
882    *
883    * @param invalidTypes set of types known to have been invalidated
884    */

885   private void removeInvalidatedParameterizedTypes(Set JavaDoc invalidTypes) {
886     Iterator JavaDoc iter = parameterizedTypes.values().iterator();
887
888     while (iter.hasNext()) {
889       JType type = (JType) iter.next();
890
891       if (isInvalidatedTypeRecursive(type, invalidTypes)) {
892         iter.remove();
893       }
894     }
895   }
896
897   /**
898    * Removes the specified types from the type oracle.
899    *
900    * @param invalidTypes set of types to remove
901    */

902   private void removeTypes(Set JavaDoc invalidTypes) {
903     Iterator JavaDoc iter = invalidTypes.iterator();
904
905     while (iter.hasNext()) {
906       JClassType classType = (JClassType) iter.next();
907       JPackage pkg = classType.getPackage();
908       if (pkg != null) {
909         pkg.remove(classType);
910       }
911
912       classType.removeFromSupertypes();
913     }
914   }
915 }
916
Popular Tags