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