• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.doclava;
18 
19 import com.google.clearsilver.jsilver.data.Data;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.Set;
27 
28 public class TypeInfo implements Resolvable {
29   public static final Set<String> PRIMITIVE_TYPES = Collections.unmodifiableSet(
30       new HashSet<String>(Arrays.asList("boolean", "byte", "char", "double", "float", "int",
31       "long", "short", "void")));
32 
TypeInfo(boolean isPrimitive, String dimension, String simpleTypeName, String qualifiedTypeName, ClassInfo cl)33   public TypeInfo(boolean isPrimitive, String dimension, String simpleTypeName,
34       String qualifiedTypeName, ClassInfo cl) {
35     mIsPrimitive = isPrimitive;
36     mDimension = dimension;
37     mSimpleTypeName = simpleTypeName;
38     mQualifiedTypeName = qualifiedTypeName;
39     if (mQualifiedTypeName == null) {
40       int x = 5;
41     }
42     mClass = cl;
43   }
44 
TypeInfo(String typeString)45   public TypeInfo(String typeString) {
46     // VarArgs
47     if (typeString.endsWith("...")) {
48       typeString = typeString.substring(0, typeString.length() - 3);
49     }
50 
51     // Generic parameters
52     int extendsPos = typeString.indexOf(" extends ");
53     int paramStartPos = typeString.indexOf('<');
54     if (paramStartPos > -1 && (extendsPos == -1 || paramStartPos < extendsPos)) {
55       ArrayList<TypeInfo> generics = new ArrayList<TypeInfo>();
56       int paramEndPos = 0;
57 
58       int entryStartPos = paramStartPos + 1;
59       int bracketNesting = 0;
60       for (int i = entryStartPos; i < typeString.length(); i++) {
61         char c = typeString.charAt(i);
62         if (c == ',' && bracketNesting == 0) {
63           String entry = typeString.substring(entryStartPos, i).trim();
64           TypeInfo info = new TypeInfo(entry);
65           generics.add(info);
66           entryStartPos = i + 1;
67         } else if (c == '<') {
68           bracketNesting++;
69         } else if (c == '>') {
70           bracketNesting--;
71           // Once bracketNesting goes negative, we've found the closing angle bracket
72           if (bracketNesting < 0) {
73             paramEndPos = i;
74             break;
75           }
76         }
77       }
78 
79       TypeInfo info = new TypeInfo(typeString.substring(entryStartPos, paramEndPos).trim());
80       generics.add(info);
81       addResolution(new Resolution("variability", "", null));
82 
83       mTypeArguments = generics;
84 
85       if (paramEndPos < typeString.length() - 1) {
86         typeString = typeString.substring(0,paramStartPos) + typeString.substring(paramEndPos + 1);
87       } else {
88         typeString = typeString.substring(0,paramStartPos);
89       }
90     }
91 
92     // The previous extends may have been within the generic type parameters which we don't
93     // actually care about and were removed from the type string above
94     extendsPos = typeString.indexOf(" extends ");
95     if (extendsPos > -1) {
96       ArrayList<TypeInfo> extendsBounds = new ArrayList<>();
97       int entryStartPos = extendsPos + 9;
98       int bracketNesting = 0;
99       for (int i = entryStartPos; i < typeString.length(); i++) {
100         char c = typeString.charAt(i);
101         if (c == '&' && bracketNesting == 0) {
102           String entry = typeString.substring(entryStartPos, i).trim();
103           TypeInfo info = new TypeInfo(entry);
104           extendsBounds.add(info);
105           entryStartPos = i + 1;
106         } else if (c == '<') {
107           bracketNesting++;
108         } else if (c == '>') {
109           bracketNesting--;
110         }
111       }
112       TypeInfo info = new TypeInfo(typeString.substring(entryStartPos, typeString.length()).trim());
113       extendsBounds.add(info);
114       mExtendsBounds = extendsBounds;
115       typeString = typeString.substring(0, extendsPos);
116     }
117 
118     int pos = typeString.indexOf('[');
119     if (pos > -1) {
120       mDimension = typeString.substring(pos);
121       typeString = typeString.substring(0, pos);
122     } else {
123       mDimension = "";
124     }
125 
126     if (PRIMITIVE_TYPES.contains(typeString)) {
127       mIsPrimitive = true;
128       mSimpleTypeName = typeString;
129       mQualifiedTypeName = typeString;
130     } else {
131       mQualifiedTypeName = typeString;
132       if (mQualifiedTypeName == null) {
133         int x = 5;
134       }
135       pos = typeString.lastIndexOf('.');
136       if (pos > -1) {
137         mSimpleTypeName = typeString.substring(pos + 1);
138       } else {
139         mSimpleTypeName = typeString;
140       }
141     }
142   }
143 
144   /**
145    * Copy Constructor.
146    */
TypeInfo(TypeInfo other)147   private TypeInfo(TypeInfo other) {
148     mIsPrimitive = other.isPrimitive();
149     mIsTypeVariable = other.isTypeVariable();
150     mIsWildcard = other.isWildcard();
151     mDimension = other.dimension();
152     mSimpleTypeName = other.simpleTypeName();
153     mQualifiedTypeName = other.qualifiedTypeName();
154     if (mQualifiedTypeName == null) {
155       int x = 5;
156     }
157     mClass = other.asClassInfo();
158     if (other.typeArguments() != null) {
159       mTypeArguments = new ArrayList<TypeInfo>(other.typeArguments());
160     }
161     if (other.superBounds() != null) {
162       mSuperBounds = new ArrayList<TypeInfo>(other.superBounds());
163     }
164     if (other.extendsBounds() != null) {
165       mExtendsBounds = new ArrayList<TypeInfo>(other.extendsBounds());
166     }
167     mFullName = other.fullName();
168   }
169 
170   /**
171    * Returns this type as a {@link ClassInfo} if it represents a class or
172    * interface.
173    */
asClassInfo()174   public ClassInfo asClassInfo() {
175     if (!mResolvedClass) {
176       mResolvedClass = true;
177       if (mClass == null && !mIsPrimitive && !mIsTypeVariable && !mIsWildcard) {
178         mClass = Converter.obtainClass(qualifiedTypeName());
179         if (mClass == null) {
180           int x = 5;
181           mClass = Converter.obtainClass(qualifiedTypeName());
182         }
183       }
184     }
185     return mClass;
186   }
187 
isPrimitive()188   public boolean isPrimitive() {
189     return mIsPrimitive;
190   }
191 
dimension()192   public String dimension() {
193     return mDimension;
194   }
195 
setDimension(String dimension)196   public void setDimension(String dimension) {
197       mDimension = dimension;
198   }
199 
simpleTypeName()200   public String simpleTypeName() {
201     return mSimpleTypeName;
202   }
203 
qualifiedTypeName()204   public String qualifiedTypeName() {
205     return mQualifiedTypeName;
206   }
207 
fullName()208   public String fullName() {
209     if (mFullName != null) {
210       return mFullName;
211     } else {
212       return fullName(new HashSet<String>());
213     }
214   }
215 
typeArgumentsName(ArrayList<TypeInfo> args, HashSet<String> typeVars)216   public static String typeArgumentsName(ArrayList<TypeInfo> args, HashSet<String> typeVars) {
217     String result = "<";
218 
219     int i = 0;
220     for (TypeInfo arg : args) {
221       result += arg.fullName(typeVars);
222       if (i != (args.size()-1)) {
223         result += ", ";
224       }
225       i++;
226     }
227     result += ">";
228     return result;
229   }
230 
fullName(HashSet<String> typeVars)231   public String fullName(HashSet<String> typeVars) {
232     mFullName = fullNameNoDimension(typeVars) + mDimension;
233     return mFullName;
234   }
235 
fullNameNoBounds(HashSet<String> typeVars)236   public String fullNameNoBounds(HashSet<String> typeVars) {
237     return fullNameNoDimensionNoBounds(typeVars) + mDimension;
238   }
239 
240   // don't recurse forever with the parameters. This handles
241   // Enum<K extends Enum<K>>
checkRecurringTypeVar(HashSet<String> typeVars)242   private boolean checkRecurringTypeVar(HashSet<String> typeVars) {
243     if (mIsTypeVariable) {
244       if (typeVars.contains(mQualifiedTypeName)) {
245         return true;
246       }
247       typeVars.add(mQualifiedTypeName);
248     }
249     return false;
250   }
251 
fullNameNoDimensionNoBounds(HashSet<String> typeVars)252   private String fullNameNoDimensionNoBounds(HashSet<String> typeVars) {
253     String fullName = null;
254     if (checkRecurringTypeVar(typeVars)) {
255       return mQualifiedTypeName;
256     }
257     /*
258      * if (fullName != null) { return fullName; }
259      */
260     fullName = mQualifiedTypeName;
261     if (mTypeArguments != null && !mTypeArguments.isEmpty()) {
262       fullName += typeArgumentsName(mTypeArguments, typeVars);
263     }
264     return fullName;
265   }
266 
fullNameNoDimension(HashSet<String> typeVars)267   public String fullNameNoDimension(HashSet<String> typeVars) {
268     String fullName = null;
269     if (checkRecurringTypeVar(typeVars)) {
270       return mQualifiedTypeName;
271     }
272     fullName = fullNameNoDimensionNoBounds(typeVars);
273     if (mTypeArguments == null || mTypeArguments.isEmpty()) {
274        if (mSuperBounds != null && !mSuperBounds.isEmpty()) {
275         for (TypeInfo superBound : mSuperBounds) {
276             if (superBound == mSuperBounds.get(0)) {
277                 fullName += " super " + superBound.fullNameNoBounds(typeVars);
278             } else {
279                 fullName += " & " + superBound.fullNameNoBounds(typeVars);
280             }
281         }
282       } else if (mExtendsBounds != null && !mExtendsBounds.isEmpty()) {
283         for (TypeInfo extendsBound : mExtendsBounds) {
284             if (extendsBound == mExtendsBounds.get(0)) {
285                 fullName += " extends " + extendsBound.fullNameNoBounds(typeVars);
286             } else {
287                 fullName += " & " + extendsBound.fullNameNoBounds(typeVars);
288             }
289         }
290       }
291     }
292     return fullName;
293   }
294 
dexName()295   public String dexName() {
296     if (mIsTypeVariable || mIsWildcard) {
297       if (mExtendsBounds != null && !mExtendsBounds.isEmpty()) {
298         return mExtendsBounds.get(0).dexName() + mDimension;
299       } else {
300         return "java.lang.Object" + mDimension;
301       }
302     }
303     return mQualifiedTypeName + mDimension;
304   }
305 
typeArguments()306   public ArrayList<TypeInfo> typeArguments() {
307     return mTypeArguments;
308   }
309 
makeHDF(Data data, String base)310   public void makeHDF(Data data, String base) {
311     makeHDFRecursive(data, base, false, false, new HashSet<String>());
312   }
313 
makeQualifiedHDF(Data data, String base)314   public void makeQualifiedHDF(Data data, String base) {
315     makeHDFRecursive(data, base, true, false, new HashSet<String>());
316   }
317 
makeHDF(Data data, String base, boolean isLastVararg, HashSet<String> typeVariables)318   public void makeHDF(Data data, String base, boolean isLastVararg, HashSet<String> typeVariables) {
319     makeHDFRecursive(data, base, false, isLastVararg, typeVariables);
320   }
321 
makeQualifiedHDF(Data data, String base, HashSet<String> typeVariables)322   public void makeQualifiedHDF(Data data, String base, HashSet<String> typeVariables) {
323     makeHDFRecursive(data, base, true, false, typeVariables);
324   }
325 
makeHDFRecursive(Data data, String base, boolean qualified, boolean isLastVararg, HashSet<String> typeVars)326   private void makeHDFRecursive(Data data, String base, boolean qualified, boolean isLastVararg,
327       HashSet<String> typeVars) {
328     String label = qualified ? qualifiedTypeName() : simpleTypeName();
329     label += (isLastVararg) ? "..." : dimension();
330     data.setValue(base + ".label", label);
331     if (mIsTypeVariable || mIsWildcard) {
332       // could link to an @param tag on the class to describe this
333       // but for now, just don't make it a link
334     } else if (!isPrimitive() && mClass != null) {
335       if (mClass.isIncluded()) {
336         data.setValue(base + ".link", mClass.htmlPage());
337         data.setValue(base + ".since", mClass.getSince());
338         data.setValue(base + ".sdkextsince", mClass.getSdkExtSince());
339       } else {
340         Doclava.federationTagger.tag(mClass);
341         if (!mClass.getFederatedReferences().isEmpty()) {
342           FederatedSite site = mClass.getFederatedReferences().iterator().next();
343           data.setValue(base + ".link", site.linkFor(mClass.htmlPage()));
344           data.setValue(base + ".federated", site.name());
345         }
346       }
347     }
348 
349     if (mIsTypeVariable) {
350       if (typeVars.contains(qualifiedTypeName())) {
351         // don't recurse forever with the parameters. This handles
352         // Enum<K extends Enum<K>>
353         return;
354       }
355       typeVars.add(qualifiedTypeName());
356     }
357     if (mTypeArguments != null) {
358       TypeInfo.makeHDF(data, base + ".typeArguments", mTypeArguments, qualified, typeVars);
359     }
360     if (mSuperBounds != null) {
361       TypeInfo.makeHDF(data, base + ".superBounds", mSuperBounds, qualified, typeVars);
362     }
363     if (mExtendsBounds != null) {
364       TypeInfo.makeHDF(data, base + ".extendsBounds", mExtendsBounds, qualified, typeVars);
365     }
366   }
367 
makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified, HashSet<String> typeVariables)368   public static void makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified,
369       HashSet<String> typeVariables) {
370     int i = 0;
371     for (TypeInfo type : types) {
372       type.makeHDFRecursive(data, base + "." + i++, qualified, false, typeVariables);
373     }
374   }
375 
makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified)376   public static void makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified) {
377     makeHDF(data, base, types, qualified, new HashSet<String>());
378   }
379 
setTypeArguments(ArrayList<TypeInfo> args)380   void setTypeArguments(ArrayList<TypeInfo> args) {
381     mTypeArguments = args;
382   }
383 
addTypeArgument(TypeInfo arg)384   public void addTypeArgument(TypeInfo arg) {
385       if (mTypeArguments == null) {
386           mTypeArguments = new ArrayList<TypeInfo>();
387       }
388 
389       mTypeArguments.add(arg);
390   }
391 
setBounds(ArrayList<TypeInfo> superBounds, ArrayList<TypeInfo> extendsBounds)392   public void setBounds(ArrayList<TypeInfo> superBounds, ArrayList<TypeInfo> extendsBounds) {
393     mSuperBounds = superBounds;
394     mExtendsBounds = extendsBounds;
395   }
396 
superBounds()397   public ArrayList<TypeInfo> superBounds() {
398       return mSuperBounds;
399   }
400 
extendsBounds()401   public ArrayList<TypeInfo> extendsBounds() {
402       return mExtendsBounds;
403   }
404 
setIsTypeVariable(boolean b)405   public void setIsTypeVariable(boolean b) {
406     mIsTypeVariable = b;
407   }
408 
setIsWildcard(boolean b)409   void setIsWildcard(boolean b) {
410     mIsWildcard = b;
411   }
412 
isWildcard()413   public boolean isWildcard() {
414       return mIsWildcard;
415   }
416 
typeVariables(ArrayList<TypeInfo> params)417   public static HashSet<String> typeVariables(ArrayList<TypeInfo> params) {
418     return typeVariables(params, new HashSet<String>());
419   }
420 
typeVariables(ArrayList<TypeInfo> params, HashSet<String> result)421   static HashSet<String> typeVariables(ArrayList<TypeInfo> params, HashSet<String> result) {
422     if (params != null) {
423         for (TypeInfo t : params) {
424             if (t.mIsTypeVariable) {
425                 result.add(t.mQualifiedTypeName);
426             }
427         }
428     }
429     return result;
430   }
431 
432 
isTypeVariable()433   public boolean isTypeVariable() {
434     return mIsTypeVariable;
435   }
436 
resolveTypeVariables(HashSet<String> variables)437   public void resolveTypeVariables(HashSet<String> variables) {
438     if (mExtendsBounds != null) {
439       for (TypeInfo bound : mExtendsBounds) {
440         if (variables.contains(bound.qualifiedTypeName())) {
441           bound.setIsTypeVariable(true);
442         }
443       }
444     }
445   }
446 
defaultValue()447   public String defaultValue() {
448     if (mIsPrimitive) {
449       if ("boolean".equals(mSimpleTypeName)) {
450         return "false";
451       } else {
452         return "0";
453       }
454     } else {
455       return "null";
456     }
457   }
458 
459   @Override
toString()460   public String toString() {
461     String returnString = "";
462     returnString +=
463         "Primitive?: " + mIsPrimitive + " TypeVariable?: " + mIsTypeVariable + " Wildcard?: "
464             + mIsWildcard + " Dimension: " + mDimension + " QualifedTypeName: "
465             + mQualifiedTypeName;
466 
467     if (mTypeArguments != null) {
468       returnString += "\nTypeArguments: ";
469       for (TypeInfo tA : mTypeArguments) {
470         returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
471       }
472     }
473     if (mSuperBounds != null) {
474       returnString += "\nSuperBounds: ";
475       for (TypeInfo tA : mSuperBounds) {
476         returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
477       }
478     }
479     if (mExtendsBounds != null) {
480       returnString += "\nExtendsBounds: ";
481       for (TypeInfo tA : mExtendsBounds) {
482         returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
483       }
484     }
485     return returnString;
486   }
487 
addResolution(Resolution resolution)488   public void addResolution(Resolution resolution) {
489       if (mResolutions == null) {
490           mResolutions = new ArrayList<Resolution>();
491       }
492 
493       mResolutions.add(resolution);
494   }
495 
printResolutions()496   public void printResolutions() {
497       if (mResolutions == null || mResolutions.isEmpty()) {
498           return;
499       }
500 
501       System.out.println("Resolutions for Type " + mSimpleTypeName + ":");
502       for (Resolution r : mResolutions) {
503           System.out.println(r);
504       }
505   }
506 
resolveResolutions()507   public boolean resolveResolutions() {
508       ArrayList<Resolution> resolutions = mResolutions;
509       mResolutions = new ArrayList<Resolution>();
510 
511       boolean allResolved = true;
512       for (Resolution resolution : resolutions) {
513           if ("class".equals(resolution.getVariable())) {
514               StringBuilder qualifiedClassName = new StringBuilder();
515               InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
516                       resolution.getInfoBuilder());
517 
518               // if we still couldn't resolve it, save it for the next pass
519               if ("".equals(qualifiedClassName.toString())) {
520                   mResolutions.add(resolution);
521                   allResolved = false;
522               } else {
523                 mClass = InfoBuilder.Caches.obtainClass(qualifiedClassName.toString());
524                 if (mClass == null) {
525                   int x = 5;
526                 }
527               }
528           } else if ("variability".equals(resolution.getVariable())) {
529               StringBuilder qualifiedClassName = new StringBuilder();
530               for (TypeInfo arg : mTypeArguments) {
531                 InfoBuilder.resolveQualifiedName(arg.simpleTypeName(), qualifiedClassName,
532                         resolution.getInfoBuilder());
533                 arg.setIsTypeVariable(!("".equals(qualifiedClassName.toString())));
534               }
535           }
536       }
537 
538       return allResolved;
539   }
540 
541   /**
542    * Copy this TypeInfo, but replace type arguments with those defined in the
543    * typeArguments mapping.
544    * <p>
545    * If the current type is one of the base types in the mapping (i.e. a parameter itself)
546    * then this returns the mapped type.
547    */
getTypeWithArguments(Map<String, TypeInfo> typeArguments)548   public TypeInfo getTypeWithArguments(Map<String, TypeInfo> typeArguments) {
549     if (typeArguments.containsKey(fullName())) {
550       return typeArguments.get(fullName());
551     }
552 
553     TypeInfo ti = new TypeInfo(this);
554     if (typeArguments() != null) {
555       ArrayList<TypeInfo> newArgs = new ArrayList<TypeInfo>();
556       for (TypeInfo t : typeArguments()) {
557         newArgs.add(t.getTypeWithArguments(typeArguments));
558       }
559       ti.setTypeArguments(newArgs);
560     }
561     return ti;
562   }
563 
564   /**
565    * Given two TypeInfos that reference the same type, take the first one's type parameters
566    * and generate a mapping from their names to the type parameters defined in the second.
567    */
getTypeArgumentMapping(TypeInfo generic, TypeInfo typed)568   public static Map<String, TypeInfo> getTypeArgumentMapping(TypeInfo generic, TypeInfo typed) {
569     Map<String, TypeInfo> map = new HashMap<String, TypeInfo>();
570     if (generic != null && generic.typeArguments() != null) {
571       for (int i = 0; i < generic.typeArguments().size(); i++) {
572         if (typed.typeArguments() != null && typed.typeArguments().size() > i) {
573           map.put(generic.typeArguments().get(i).simpleTypeName(), typed.typeArguments().get(i));
574         }
575       }
576     }
577     return map;
578   }
579 
580   /**
581    * Given a ClassInfo and a parameterized TypeInfo, take the class's raw type's type parameters
582    * and generate a mapping from their names to the type parameters defined in the TypeInfo.
583    */
getTypeArgumentMapping(ClassInfo cls, TypeInfo typed)584   public static Map<String, TypeInfo> getTypeArgumentMapping(ClassInfo cls, TypeInfo typed) {
585     return getTypeArgumentMapping(cls.asTypeInfo(), typed);
586   }
587 
588   private ArrayList<Resolution> mResolutions;
589 
590   /** Whether the value of {@code mClass} has been resolved. */
591   private boolean mResolvedClass;
592 
593   private boolean mIsPrimitive;
594   private boolean mIsTypeVariable;
595   private boolean mIsWildcard;
596   private String mDimension;
597   private String mSimpleTypeName;
598   private String mQualifiedTypeName;
599   private ClassInfo mClass;
600   private ArrayList<TypeInfo> mTypeArguments;
601   private ArrayList<TypeInfo> mSuperBounds;
602   private ArrayList<TypeInfo> mExtendsBounds;
603   private String mFullName;
604 }
605