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