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