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   public ClassInfo asClassInfo() {
    102     return mClass;
    103   }
    104 
    105   public boolean isPrimitive() {
    106     return mIsPrimitive;
    107   }
    108 
    109   public String dimension() {
    110     return mDimension;
    111   }
    112 
    113   public void setDimension(String dimension) {
    114       mDimension = dimension;
    115   }
    116 
    117   public String simpleTypeName() {
    118     return mSimpleTypeName;
    119   }
    120 
    121   public String qualifiedTypeName() {
    122     return mQualifiedTypeName;
    123   }
    124 
    125   public String fullName() {
    126     if (mFullName != null) {
    127       return mFullName;
    128     } else {
    129       return fullName(new HashSet<String>());
    130     }
    131   }
    132 
    133   public static String typeArgumentsName(ArrayList<TypeInfo> args, HashSet<String> typeVars) {
    134     String result = "<";
    135 
    136     int i = 0;
    137     for (TypeInfo arg : args) {
    138       result += arg.fullName(typeVars);
    139       if (i != (args.size()-1)) {
    140         result += ", ";
    141       }
    142       i++;
    143     }
    144     result += ">";
    145     return result;
    146   }
    147 
    148   public String fullName(HashSet<String> typeVars) {
    149     mFullName = fullNameNoDimension(typeVars) + mDimension;
    150     return mFullName;
    151   }
    152 
    153   public String fullNameNoBounds(HashSet<String> typeVars) {
    154     return fullNameNoDimensionNoBounds(typeVars) + mDimension;
    155   }
    156 
    157   // don't recurse forever with the parameters. This handles
    158   // Enum<K extends Enum<K>>
    159   private boolean checkRecurringTypeVar(HashSet<String> typeVars) {
    160     if (mIsTypeVariable) {
    161       if (typeVars.contains(mQualifiedTypeName)) {
    162         return true;
    163       }
    164       typeVars.add(mQualifiedTypeName);
    165     }
    166     return false;
    167   }
    168 
    169   private String fullNameNoDimensionNoBounds(HashSet<String> typeVars) {
    170     String fullName = null;
    171     if (checkRecurringTypeVar(typeVars)) {
    172       return mQualifiedTypeName;
    173     }
    174     /*
    175      * if (fullName != null) { return fullName; }
    176      */
    177     fullName = mQualifiedTypeName;
    178     if (mTypeArguments != null && !mTypeArguments.isEmpty()) {
    179       fullName += typeArgumentsName(mTypeArguments, typeVars);
    180     }
    181     return fullName;
    182   }
    183 
    184   public String fullNameNoDimension(HashSet<String> typeVars) {
    185     String fullName = null;
    186     if (checkRecurringTypeVar(typeVars)) {
    187       return mQualifiedTypeName;
    188     }
    189     fullName = fullNameNoDimensionNoBounds(typeVars);
    190     if (mTypeArguments == null || mTypeArguments.isEmpty()) {
    191        if (mSuperBounds != null && !mSuperBounds.isEmpty()) {
    192         for (TypeInfo superBound : mSuperBounds) {
    193             if (superBound == mSuperBounds.get(0)) {
    194                 fullName += " super " + superBound.fullNameNoBounds(typeVars);
    195             } else {
    196                 fullName += " & " + superBound.fullNameNoBounds(typeVars);
    197             }
    198         }
    199       } else if (mExtendsBounds != null && !mExtendsBounds.isEmpty()) {
    200         for (TypeInfo extendsBound : mExtendsBounds) {
    201             if (extendsBound == mExtendsBounds.get(0)) {
    202                 fullName += " extends " + extendsBound.fullNameNoBounds(typeVars);
    203             } else {
    204                 fullName += " & " + extendsBound.fullNameNoBounds(typeVars);
    205             }
    206         }
    207       }
    208     }
    209     return fullName;
    210   }
    211 
    212   public ArrayList<TypeInfo> typeArguments() {
    213     return mTypeArguments;
    214   }
    215 
    216   public void makeHDF(Data data, String base) {
    217     makeHDFRecursive(data, base, false, false, new HashSet<String>());
    218   }
    219 
    220   public void makeQualifiedHDF(Data data, String base) {
    221     makeHDFRecursive(data, base, true, false, new HashSet<String>());
    222   }
    223 
    224   public void makeHDF(Data data, String base, boolean isLastVararg, HashSet<String> typeVariables) {
    225     makeHDFRecursive(data, base, false, isLastVararg, typeVariables);
    226   }
    227 
    228   public void makeQualifiedHDF(Data data, String base, HashSet<String> typeVariables) {
    229     makeHDFRecursive(data, base, true, false, typeVariables);
    230   }
    231 
    232   private void makeHDFRecursive(Data data, String base, boolean qualified, boolean isLastVararg,
    233       HashSet<String> typeVars) {
    234     String label = qualified ? qualifiedTypeName() : simpleTypeName();
    235     label += (isLastVararg) ? "..." : dimension();
    236     data.setValue(base + ".label", label);
    237     if (mIsTypeVariable || mIsWildcard) {
    238       // could link to an @param tag on the class to describe this
    239       // but for now, just don't make it a link
    240     } else if (!isPrimitive() && mClass != null) {
    241       if (mClass.isIncluded()) {
    242         data.setValue(base + ".link", mClass.htmlPage());
    243         data.setValue(base + ".since", mClass.getSince());
    244       } else {
    245         Doclava.federationTagger.tag(mClass);
    246         if (!mClass.getFederatedReferences().isEmpty()) {
    247           FederatedSite site = mClass.getFederatedReferences().iterator().next();
    248           data.setValue(base + ".link", site.linkFor(mClass.htmlPage()));
    249           data.setValue(base + ".federated", site.name());
    250         }
    251       }
    252     }
    253 
    254     if (mIsTypeVariable) {
    255       if (typeVars.contains(qualifiedTypeName())) {
    256         // don't recurse forever with the parameters. This handles
    257         // Enum<K extends Enum<K>>
    258         return;
    259       }
    260       typeVars.add(qualifiedTypeName());
    261     }
    262     if (mTypeArguments != null) {
    263       TypeInfo.makeHDF(data, base + ".typeArguments", mTypeArguments, qualified, typeVars);
    264     }
    265     if (mSuperBounds != null) {
    266       TypeInfo.makeHDF(data, base + ".superBounds", mSuperBounds, qualified, typeVars);
    267     }
    268     if (mExtendsBounds != null) {
    269       TypeInfo.makeHDF(data, base + ".extendsBounds", mExtendsBounds, qualified, typeVars);
    270     }
    271   }
    272 
    273   public static void makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified,
    274       HashSet<String> typeVariables) {
    275     int i = 0;
    276     for (TypeInfo type : types) {
    277       type.makeHDFRecursive(data, base + "." + i++, qualified, false, typeVariables);
    278     }
    279   }
    280 
    281   public static void makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified) {
    282     makeHDF(data, base, types, qualified, new HashSet<String>());
    283   }
    284 
    285   void setTypeArguments(ArrayList<TypeInfo> args) {
    286     mTypeArguments = args;
    287   }
    288 
    289   public void addTypeArgument(TypeInfo arg) {
    290       if (mTypeArguments == null) {
    291           mTypeArguments = new ArrayList<TypeInfo>();
    292       }
    293 
    294       mTypeArguments.add(arg);
    295   }
    296 
    297   void setBounds(ArrayList<TypeInfo> superBounds, ArrayList<TypeInfo> extendsBounds) {
    298     mSuperBounds = superBounds;
    299     mExtendsBounds = extendsBounds;
    300   }
    301 
    302   public ArrayList<TypeInfo> superBounds() {
    303       return mSuperBounds;
    304   }
    305 
    306   public ArrayList<TypeInfo> extendsBounds() {
    307       return mExtendsBounds;
    308   }
    309 
    310   void setIsTypeVariable(boolean b) {
    311     mIsTypeVariable = b;
    312   }
    313 
    314   void setIsWildcard(boolean b) {
    315     mIsWildcard = b;
    316   }
    317 
    318   public boolean isWildcard() {
    319       return mIsWildcard;
    320   }
    321 
    322   static HashSet<String> typeVariables(ArrayList<TypeInfo> params) {
    323     return typeVariables(params, new HashSet<String>());
    324   }
    325 
    326   static HashSet<String> typeVariables(ArrayList<TypeInfo> params, HashSet<String> result) {
    327     if (params != null) {
    328         for (TypeInfo t : params) {
    329             if (t.mIsTypeVariable) {
    330                 result.add(t.mQualifiedTypeName);
    331             }
    332         }
    333     }
    334     return result;
    335   }
    336 
    337 
    338   public boolean isTypeVariable() {
    339     return mIsTypeVariable;
    340   }
    341 
    342   public String defaultValue() {
    343     if (mIsPrimitive) {
    344       if ("boolean".equals(mSimpleTypeName)) {
    345         return "false";
    346       } else {
    347         return "0";
    348       }
    349     } else {
    350       return "null";
    351     }
    352   }
    353 
    354   @Override
    355   public String toString() {
    356     String returnString = "";
    357     returnString +=
    358         "Primitive?: " + mIsPrimitive + " TypeVariable?: " + mIsTypeVariable + " Wildcard?: "
    359             + mIsWildcard + " Dimension: " + mDimension + " QualifedTypeName: "
    360             + mQualifiedTypeName;
    361 
    362     if (mTypeArguments != null) {
    363       returnString += "\nTypeArguments: ";
    364       for (TypeInfo tA : mTypeArguments) {
    365         returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
    366       }
    367     }
    368     if (mSuperBounds != null) {
    369       returnString += "\nSuperBounds: ";
    370       for (TypeInfo tA : mSuperBounds) {
    371         returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
    372       }
    373     }
    374     if (mExtendsBounds != null) {
    375       returnString += "\nExtendsBounds: ";
    376       for (TypeInfo tA : mExtendsBounds) {
    377         returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
    378       }
    379     }
    380     return returnString;
    381   }
    382 
    383   public void addResolution(Resolution resolution) {
    384       if (mResolutions == null) {
    385           mResolutions = new ArrayList<Resolution>();
    386       }
    387 
    388       mResolutions.add(resolution);
    389   }
    390 
    391   public void printResolutions() {
    392       if (mResolutions == null || mResolutions.isEmpty()) {
    393           return;
    394       }
    395 
    396       System.out.println("Resolutions for Type " + mSimpleTypeName + ":");
    397       for (Resolution r : mResolutions) {
    398           System.out.println(r);
    399       }
    400   }
    401 
    402   public boolean resolveResolutions() {
    403       ArrayList<Resolution> resolutions = mResolutions;
    404       mResolutions = new ArrayList<Resolution>();
    405 
    406       boolean allResolved = true;
    407       for (Resolution resolution : resolutions) {
    408           if ("class".equals(resolution.getVariable())) {
    409               StringBuilder qualifiedClassName = new StringBuilder();
    410               InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
    411                       resolution.getInfoBuilder());
    412 
    413               // if we still couldn't resolve it, save it for the next pass
    414               if ("".equals(qualifiedClassName.toString())) {
    415                   mResolutions.add(resolution);
    416                   allResolved = false;
    417               } else {
    418                   mClass = InfoBuilder.Caches.obtainClass(qualifiedClassName.toString());
    419               }
    420           }
    421       }
    422 
    423       return allResolved;
    424   }
    425 
    426   private ArrayList<Resolution> mResolutions;
    427 
    428   private boolean mIsPrimitive;
    429   private boolean mIsTypeVariable;
    430   private boolean mIsWildcard;
    431   private String mDimension;
    432   private String mSimpleTypeName;
    433   private String mQualifiedTypeName;
    434   private ClassInfo mClass;
    435   private ArrayList<TypeInfo> mTypeArguments;
    436   private ArrayList<TypeInfo> mSuperBounds;
    437   private ArrayList<TypeInfo> mExtendsBounds;
    438   private String mFullName;
    439 }
    440