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