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 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 asClassInfo()101 public ClassInfo asClassInfo() { 102 return mClass; 103 } 104 isPrimitive()105 public boolean isPrimitive() { 106 return mIsPrimitive; 107 } 108 dimension()109 public String dimension() { 110 return mDimension; 111 } 112 setDimension(String dimension)113 public void setDimension(String dimension) { 114 mDimension = dimension; 115 } 116 simpleTypeName()117 public String simpleTypeName() { 118 return mSimpleTypeName; 119 } 120 qualifiedTypeName()121 public String qualifiedTypeName() { 122 return mQualifiedTypeName; 123 } 124 fullName()125 public String fullName() { 126 if (mFullName != null) { 127 return mFullName; 128 } else { 129 return fullName(new HashSet<String>()); 130 } 131 } 132 typeArgumentsName(ArrayList<TypeInfo> args, HashSet<String> typeVars)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 fullName(HashSet<String> typeVars)148 public String fullName(HashSet<String> typeVars) { 149 mFullName = fullNameNoDimension(typeVars) + mDimension; 150 return mFullName; 151 } 152 fullNameNoBounds(HashSet<String> typeVars)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>> checkRecurringTypeVar(HashSet<String> typeVars)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 fullNameNoDimensionNoBounds(HashSet<String> typeVars)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 fullNameNoDimension(HashSet<String> typeVars)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 typeArguments()212 public ArrayList<TypeInfo> typeArguments() { 213 return mTypeArguments; 214 } 215 makeHDF(Data data, String base)216 public void makeHDF(Data data, String base) { 217 makeHDFRecursive(data, base, false, false, new HashSet<String>()); 218 } 219 makeQualifiedHDF(Data data, String base)220 public void makeQualifiedHDF(Data data, String base) { 221 makeHDFRecursive(data, base, true, false, new HashSet<String>()); 222 } 223 makeHDF(Data data, String base, boolean isLastVararg, HashSet<String> typeVariables)224 public void makeHDF(Data data, String base, boolean isLastVararg, HashSet<String> typeVariables) { 225 makeHDFRecursive(data, base, false, isLastVararg, typeVariables); 226 } 227 makeQualifiedHDF(Data data, String base, HashSet<String> typeVariables)228 public void makeQualifiedHDF(Data data, String base, HashSet<String> typeVariables) { 229 makeHDFRecursive(data, base, true, false, typeVariables); 230 } 231 makeHDFRecursive(Data data, String base, boolean qualified, boolean isLastVararg, HashSet<String> typeVars)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 makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified, HashSet<String> typeVariables)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 makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified)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 setTypeArguments(ArrayList<TypeInfo> args)285 void setTypeArguments(ArrayList<TypeInfo> args) { 286 mTypeArguments = args; 287 } 288 addTypeArgument(TypeInfo arg)289 public void addTypeArgument(TypeInfo arg) { 290 if (mTypeArguments == null) { 291 mTypeArguments = new ArrayList<TypeInfo>(); 292 } 293 294 mTypeArguments.add(arg); 295 } 296 setBounds(ArrayList<TypeInfo> superBounds, ArrayList<TypeInfo> extendsBounds)297 void setBounds(ArrayList<TypeInfo> superBounds, ArrayList<TypeInfo> extendsBounds) { 298 mSuperBounds = superBounds; 299 mExtendsBounds = extendsBounds; 300 } 301 superBounds()302 public ArrayList<TypeInfo> superBounds() { 303 return mSuperBounds; 304 } 305 extendsBounds()306 public ArrayList<TypeInfo> extendsBounds() { 307 return mExtendsBounds; 308 } 309 setIsTypeVariable(boolean b)310 void setIsTypeVariable(boolean b) { 311 mIsTypeVariable = b; 312 } 313 setIsWildcard(boolean b)314 void setIsWildcard(boolean b) { 315 mIsWildcard = b; 316 } 317 isWildcard()318 public boolean isWildcard() { 319 return mIsWildcard; 320 } 321 typeVariables(ArrayList<TypeInfo> params)322 static HashSet<String> typeVariables(ArrayList<TypeInfo> params) { 323 return typeVariables(params, new HashSet<String>()); 324 } 325 typeVariables(ArrayList<TypeInfo> params, HashSet<String> result)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 isTypeVariable()338 public boolean isTypeVariable() { 339 return mIsTypeVariable; 340 } 341 defaultValue()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 toString()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 addResolution(Resolution resolution)383 public void addResolution(Resolution resolution) { 384 if (mResolutions == null) { 385 mResolutions = new ArrayList<Resolution>(); 386 } 387 388 mResolutions.add(resolution); 389 } 390 printResolutions()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 resolveResolutions()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