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.doclava.apicheck.ApiInfo; 20 import com.sun.javadoc.AnnotationDesc; 21 import com.sun.javadoc.AnnotationTypeDoc; 22 import com.sun.javadoc.AnnotationTypeElementDoc; 23 import com.sun.javadoc.AnnotationValue; 24 import com.sun.javadoc.ClassDoc; 25 import com.sun.javadoc.ConstructorDoc; 26 import com.sun.javadoc.ExecutableMemberDoc; 27 import com.sun.javadoc.FieldDoc; 28 import com.sun.javadoc.MemberDoc; 29 import com.sun.javadoc.MethodDoc; 30 import com.sun.javadoc.PackageDoc; 31 import com.sun.javadoc.ParamTag; 32 import com.sun.javadoc.Parameter; 33 import com.sun.javadoc.RootDoc; 34 import com.sun.javadoc.SeeTag; 35 import com.sun.javadoc.SourcePosition; 36 import com.sun.javadoc.Tag; 37 import com.sun.javadoc.ThrowsTag; 38 import com.sun.javadoc.Type; 39 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collection; 43 import java.util.HashMap; 44 import java.util.HashSet; 45 import java.util.List; 46 import java.util.Map; 47 48 public class Converter { 49 private static RootDoc root; 50 private static List<ApiInfo> apis; 51 52 public static void makeInfo(RootDoc r) { 53 root = r; 54 apis = new ArrayList<>(); 55 56 // create the objects 57 ClassDoc[] classes = getClasses(r); 58 for (ClassDoc c : classes) { 59 Converter.obtainClass(c); 60 } 61 62 ArrayList<ClassInfo> classesNeedingInit2 = new ArrayList<ClassInfo>(); 63 64 int i; 65 // fill in the fields that reference other classes 66 while (mClassesNeedingInit.size() > 0) { 67 i = mClassesNeedingInit.size() - 1; 68 ClassNeedingInit clni = mClassesNeedingInit.get(i); 69 mClassesNeedingInit.remove(i); 70 71 initClass(clni.c, clni.cl); 72 classesNeedingInit2.add(clni.cl); 73 } 74 mClassesNeedingInit = null; 75 for (ClassInfo cl : classesNeedingInit2) { 76 cl.init2(); 77 } 78 79 finishAnnotationValueInit(); 80 81 // fill in the "root" stuff 82 mRootClasses = Converter.convertClasses(classes); 83 } 84 85 /** 86 * Adds additional APIs to be available from calls to obtain() methods. 87 * 88 * @param api the APIs to add, must be non-{@code null} 89 */ 90 public static void addApiInfo(ApiInfo api) { 91 apis.add(api); 92 } 93 94 private static ClassDoc[] getClasses(RootDoc r) { 95 ClassDoc[] classDocs = r.classes(); 96 ArrayList<ClassDoc> filtered = new ArrayList<ClassDoc>(classDocs.length); 97 for (ClassDoc c : classDocs) { 98 if (c.position() != null) { 99 // Work around a javadoc bug in Java 7: We sometimes spuriously 100 // receive duplicate top level ClassDocs with null positions and no type 101 // information. Ignore them, since every ClassDoc must have a non null 102 // position. 103 104 filtered.add(c); 105 } 106 } 107 108 ClassDoc[] filteredArray = new ClassDoc[filtered.size()]; 109 filtered.toArray(filteredArray); 110 return filteredArray; 111 } 112 113 private static ClassInfo[] mRootClasses; 114 115 public static Collection<ClassInfo> rootClasses() { 116 return Arrays.asList(mRootClasses); 117 } 118 119 public static Collection<ClassInfo> allClasses() { 120 return (Collection<ClassInfo>) mClasses.all(); 121 } 122 123 private static final MethodDoc[] EMPTY_METHOD_DOC = new MethodDoc[0]; 124 125 private static void initClass(ClassDoc c, ClassInfo cl) { 126 MethodDoc[] annotationElements; 127 if (c instanceof AnnotationTypeDoc) { 128 annotationElements = ((AnnotationTypeDoc) c).elements(); 129 } else { 130 annotationElements = EMPTY_METHOD_DOC; 131 } 132 cl.init(Converter.obtainType(c), 133 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(c.interfaces()))), 134 new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(c.interfaceTypes()))), 135 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(c.innerClasses()))), 136 new ArrayList<MethodInfo>(Arrays.asList( 137 Converter.convertMethods(c.constructors(false)))), 138 new ArrayList<MethodInfo>(Arrays.asList(Converter.convertMethods(c.methods(false)))), 139 new ArrayList<MethodInfo>(Arrays.asList(Converter.convertMethods(annotationElements))), 140 new ArrayList<FieldInfo>(Arrays.asList(Converter.convertFields(c.fields(false)))), 141 new ArrayList<FieldInfo>(Arrays.asList(Converter.convertFields(c.enumConstants()))), 142 Converter.obtainPackage(c.containingPackage()), 143 Converter.obtainClass(c.containingClass()), 144 Converter.obtainClass(c.superclass()), Converter.obtainType(c.superclassType()), 145 new ArrayList<AnnotationInstanceInfo>(Arrays.asList( 146 Converter.convertAnnotationInstances(c.annotations())))); 147 148 cl.setHiddenMethods( 149 new ArrayList<MethodInfo>(Arrays.asList(Converter.getHiddenMethods(c.methods(false))))); 150 cl.setRemovedMethods( 151 new ArrayList<MethodInfo>(Arrays.asList(Converter.getRemovedMethods(c.methods(false))))); 152 153 cl.setExhaustiveConstructors( 154 new ArrayList<MethodInfo>(Converter.convertAllMethods(c.constructors(false)))); 155 cl.setExhaustiveMethods( 156 new ArrayList<MethodInfo>(Converter.convertAllMethods(c.methods(false)))); 157 cl.setExhaustiveEnumConstants( 158 new ArrayList<FieldInfo>(Converter.convertAllFields(c.enumConstants()))); 159 cl.setExhaustiveFields( 160 new ArrayList<FieldInfo>(Converter.convertAllFields(c.fields(false)))); 161 162 cl.setNonWrittenConstructors( 163 new ArrayList<MethodInfo>(Arrays.asList(Converter.convertNonWrittenConstructors( 164 c.constructors(false))))); 165 cl.init3( 166 new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(c.typeParameters()))), 167 new ArrayList<ClassInfo>(Arrays.asList( 168 Converter.convertClasses(c.innerClasses(false))))); 169 } 170 171 /** 172 * Obtains a {@link ClassInfo} describing the specified class. If the class 173 * was not specified on the source path, this method will attempt to locate 174 * the class within the list of federated APIs. 175 * 176 * @param className the fully-qualified class name to search for 177 * @return info for the specified class, or {@code null} if not available 178 * @see #addApiInfo(ApiInfo) 179 */ 180 public static ClassInfo obtainClass(String className) { 181 ClassInfo result = Converter.obtainClass(root.classNamed(className)); 182 if (result != null) { 183 return result; 184 } 185 186 for (ApiInfo api : apis) { 187 result = api.findClass(className); 188 if (result != null) { 189 return result; 190 } 191 } 192 193 return null; 194 } 195 196 /** 197 * Obtains a {@link PackageInfo} describing the specified package. If the 198 * package was not specified on the source path, this method will attempt to 199 * locate the package within the list of federated APIs. 200 * 201 * @param packageName the fully-qualified package name to search for 202 * @return info for the specified package, or {@code null} if not available 203 * @see #addApiInfo(ApiInfo) 204 */ 205 public static PackageInfo obtainPackage(String packageName) { 206 PackageInfo result = Converter.obtainPackage(root.packageNamed(packageName)); 207 if (result != null) { 208 return result; 209 } 210 211 for (ApiInfo api : apis) { 212 result = api.getPackages().get(packageName); 213 if (result != null) { 214 return result; 215 } 216 } 217 218 return null; 219 } 220 221 private static TagInfo convertTag(Tag tag) { 222 return new TextTagInfo(tag.name(), tag.kind(), tag.text(), 223 Converter.convertSourcePosition(tag.position())); 224 } 225 226 private static ThrowsTagInfo convertThrowsTag(ThrowsTag tag, ContainerInfo base) { 227 return new ThrowsTagInfo(tag.name(), tag.text(), tag.kind(), Converter.obtainClass(tag 228 .exception()), tag.exceptionComment(), base, Converter 229 .convertSourcePosition(tag.position())); 230 } 231 232 private static ParamTagInfo convertParamTag(ParamTag tag, ContainerInfo base) { 233 return new ParamTagInfo(tag.name(), tag.kind(), tag.text(), tag.isTypeParameter(), tag 234 .parameterComment(), tag.parameterName(), base, Converter.convertSourcePosition(tag 235 .position())); 236 } 237 238 private static SeeTagInfo convertSeeTag(SeeTag tag, ContainerInfo base) { 239 return new SeeTagInfo(tag.name(), tag.kind(), tag.text(), base, Converter 240 .convertSourcePosition(tag.position())); 241 } 242 243 private static SourcePositionInfo convertSourcePosition(SourcePosition sp) { 244 if (sp == null) { 245 return null; 246 } 247 return new SourcePositionInfo(sp.file().toString(), sp.line(), sp.column()); 248 } 249 250 public static TagInfo[] convertTags(Tag[] tags, ContainerInfo base) { 251 int len = tags.length; 252 TagInfo[] out = TagInfo.getArray(len); 253 for (int i = 0; i < len; i++) { 254 Tag t = tags[i]; 255 /* 256 * System.out.println("Tag name='" + t.name() + "' kind='" + t.kind() + "'"); 257 */ 258 if (t instanceof SeeTag) { 259 out[i] = Converter.convertSeeTag((SeeTag) t, base); 260 } else if (t instanceof ThrowsTag) { 261 out[i] = Converter.convertThrowsTag((ThrowsTag) t, base); 262 } else if (t instanceof ParamTag) { 263 out[i] = Converter.convertParamTag((ParamTag) t, base); 264 } else { 265 out[i] = Converter.convertTag(t); 266 } 267 } 268 return out; 269 } 270 271 public static ClassInfo[] convertClasses(ClassDoc[] classes) { 272 if (classes == null) return null; 273 int N = classes.length; 274 ClassInfo[] result = new ClassInfo[N]; 275 for (int i = 0; i < N; i++) { 276 result[i] = Converter.obtainClass(classes[i]); 277 } 278 return result; 279 } 280 281 private static ParameterInfo convertParameter(Parameter p, SourcePosition pos, boolean isVarArg) { 282 if (p == null) return null; 283 ParameterInfo pi = 284 new ParameterInfo(p.name(), p.typeName(), Converter.obtainType(p.type()), isVarArg, 285 Converter.convertSourcePosition(pos), 286 Arrays.asList(Converter.convertAnnotationInstances(p.annotations()))); 287 return pi; 288 } 289 290 private static ParameterInfo[] convertParameters(Parameter[] p, ExecutableMemberDoc m) { 291 SourcePosition pos = m.position(); 292 int len = p.length; 293 ParameterInfo[] q = new ParameterInfo[len]; 294 for (int i = 0; i < len; i++) { 295 boolean isVarArg = (m.isVarArgs() && i == len - 1); 296 q[i] = Converter.convertParameter(p[i], pos, isVarArg); 297 } 298 return q; 299 } 300 301 private static TypeInfo[] convertTypes(Type[] p) { 302 if (p == null) return null; 303 int len = p.length; 304 TypeInfo[] q = new TypeInfo[len]; 305 for (int i = 0; i < len; i++) { 306 q[i] = Converter.obtainType(p[i]); 307 } 308 return q; 309 } 310 311 private Converter() {} 312 313 private static class ClassNeedingInit { 314 ClassNeedingInit(ClassDoc c, ClassInfo cl) { 315 this.c = c; 316 this.cl = cl; 317 } 318 319 ClassDoc c; 320 ClassInfo cl; 321 } 322 323 private static ArrayList<ClassNeedingInit> mClassesNeedingInit = 324 new ArrayList<ClassNeedingInit>(); 325 326 static ClassInfo obtainClass(ClassDoc o) { 327 return (ClassInfo) mClasses.obtain(o); 328 } 329 330 private static Cache mClasses = new Cache() { 331 @Override 332 protected Object make(Object o) { 333 ClassDoc c = (ClassDoc) o; 334 ClassInfo cl = 335 new ClassInfo(c, c.getRawCommentText(), Converter.convertSourcePosition(c.position()), c 336 .isPublic(), c.isProtected(), c.isPackagePrivate(), c.isPrivate(), c.isStatic(), c 337 .isInterface(), c.isAbstract(), c.isOrdinaryClass(), c.isException(), c.isError(), c 338 .isEnum(), (c instanceof AnnotationTypeDoc), c.isFinal(), c.isIncluded(), c.name(), c 339 .qualifiedName(), c.qualifiedTypeName(), c.isPrimitive()); 340 if (mClassesNeedingInit != null) { 341 mClassesNeedingInit.add(new ClassNeedingInit(c, cl)); 342 } 343 return cl; 344 } 345 346 @Override 347 protected void made(Object o, Object r) { 348 if (mClassesNeedingInit == null) { 349 initClass((ClassDoc) o, (ClassInfo) r); 350 ((ClassInfo) r).init2(); 351 } 352 } 353 354 @Override 355 Collection<?> all() { 356 return mCache.values(); 357 } 358 }; 359 360 private static MethodInfo[] getHiddenMethods(MethodDoc[] methods) { 361 if (methods == null) return null; 362 ArrayList<MethodInfo> hiddenMethods = new ArrayList<MethodInfo>(); 363 for (MethodDoc method : methods) { 364 MethodInfo methodInfo = Converter.obtainMethod(method); 365 if (methodInfo.isHidden()) { 366 hiddenMethods.add(methodInfo); 367 } 368 } 369 370 return hiddenMethods.toArray(new MethodInfo[hiddenMethods.size()]); 371 } 372 373 // Gets the removed methods regardless of access levels 374 private static MethodInfo[] getRemovedMethods(MethodDoc[] methods) { 375 if (methods == null) return null; 376 ArrayList<MethodInfo> removedMethods = new ArrayList<MethodInfo>(); 377 for (MethodDoc method : methods) { 378 MethodInfo methodInfo = Converter.obtainMethod(method); 379 if (methodInfo.isRemoved()) { 380 removedMethods.add(methodInfo); 381 } 382 } 383 384 return removedMethods.toArray(new MethodInfo[removedMethods.size()]); 385 } 386 387 /** 388 * Converts FieldDoc[] into List<FieldInfo>. No filtering is done. 389 */ 390 private static List<FieldInfo> convertAllFields(FieldDoc[] fields) { 391 if (fields == null) return null; 392 List<FieldInfo> allFields = new ArrayList<FieldInfo>(); 393 394 for (FieldDoc field : fields) { 395 FieldInfo fieldInfo = Converter.obtainField(field); 396 allFields.add(fieldInfo); 397 } 398 399 return allFields; 400 } 401 402 /** 403 * Converts ExecutableMemberDoc[] into List<MethodInfo>. No filtering is done. 404 */ 405 private static List<MethodInfo> convertAllMethods(ExecutableMemberDoc[] methods) { 406 if (methods == null) return null; 407 List<MethodInfo> allMethods = new ArrayList<MethodInfo>(); 408 for (ExecutableMemberDoc method : methods) { 409 MethodInfo methodInfo = Converter.obtainMethod(method); 410 allMethods.add(methodInfo); 411 } 412 return allMethods; 413 } 414 415 /** 416 * Convert MethodDoc[] or ConstructorDoc[] into MethodInfo[]. 417 * Also filters according to the -private, -public option, 418 * because the filtering doesn't seem to be working in the ClassDoc.constructors(boolean) call. 419 */ 420 private static MethodInfo[] convertMethods(ExecutableMemberDoc[] methods) { 421 if (methods == null) return null; 422 List<MethodInfo> filteredMethods = new ArrayList<MethodInfo>(); 423 for (ExecutableMemberDoc method : methods) { 424 MethodInfo methodInfo = Converter.obtainMethod(method); 425 if (methodInfo.checkLevel()) { 426 filteredMethods.add(methodInfo); 427 } 428 } 429 430 return filteredMethods.toArray(new MethodInfo[filteredMethods.size()]); 431 } 432 433 private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods) { 434 if (methods == null) return null; 435 ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>(); 436 for (ConstructorDoc method : methods) { 437 MethodInfo methodInfo = Converter.obtainMethod(method); 438 if (!methodInfo.checkLevel()) { 439 ctors.add(methodInfo); 440 } 441 } 442 443 return ctors.toArray(new MethodInfo[ctors.size()]); 444 } 445 446 private static <E extends ExecutableMemberDoc> MethodInfo obtainMethod(E o) { 447 return (MethodInfo) mMethods.obtain(o); 448 } 449 450 private static Cache mMethods = new Cache() { 451 @Override 452 protected Object make(Object o) { 453 if (o instanceof AnnotationTypeElementDoc) { 454 AnnotationTypeElementDoc m = (AnnotationTypeElementDoc) o; 455 MethodInfo result = 456 new MethodInfo(m.getRawCommentText(), 457 new ArrayList<TypeInfo>(Arrays.asList( 458 Converter.convertTypes(m.typeParameters()))), 459 m.name(), m.signature(), Converter.obtainClass(m.containingClass()), 460 Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m 461 .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(), 462 m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), true, 463 "annotationElement", m.flatSignature(), 464 Converter.obtainMethod(m.overriddenMethod()), 465 Converter.obtainType(m.returnType()), 466 new ArrayList<ParameterInfo>(Arrays.asList( 467 Converter.convertParameters(m.parameters(), m))), 468 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses( 469 m.thrownExceptions()))), Converter.convertSourcePosition(m.position()), 470 new ArrayList<AnnotationInstanceInfo>(Arrays.asList( 471 Converter.convertAnnotationInstances(m.annotations())))); 472 result.setVarargs(m.isVarArgs()); 473 result.init(Converter.obtainAnnotationValue(m.defaultValue(), result)); 474 return result; 475 } else if (o instanceof MethodDoc) { 476 MethodDoc m = (MethodDoc) o; 477 final ClassInfo containingClass = Converter.obtainClass(m.containingClass()); 478 479 // The containing class is final, so it is implied that every method is final as well. 480 // No need to apply 'final' to each method. 481 final boolean isMethodFinal = m.isFinal() && !containingClass.isFinal(); 482 MethodInfo result = 483 new MethodInfo(m.getRawCommentText(), 484 new ArrayList<TypeInfo>(Arrays.asList( 485 Converter.convertTypes(m.typeParameters()))), m.name(), m.signature(), 486 containingClass, containingClass, m.isPublic(), m.isProtected(), 487 m.isPackagePrivate(), m.isPrivate(), isMethodFinal, m.isStatic(), 488 m.isSynthetic(), m.isAbstract(), m.isSynchronized(), m.isNative(), 489 m.isDefault(), false, "method", m.flatSignature(), 490 Converter.obtainMethod(m.overriddenMethod()), 491 Converter.obtainType(m.returnType()), 492 new ArrayList<ParameterInfo>(Arrays.asList( 493 Converter.convertParameters(m.parameters(), m))), 494 new ArrayList<ClassInfo>(Arrays.asList( 495 Converter.convertClasses(m.thrownExceptions()))), 496 Converter.convertSourcePosition(m.position()), 497 new ArrayList<AnnotationInstanceInfo>(Arrays.asList( 498 Converter.convertAnnotationInstances(m.annotations())))); 499 result.setVarargs(m.isVarArgs()); 500 result.init(null); 501 return result; 502 } else { 503 ConstructorDoc m = (ConstructorDoc) o; 504 // Workaround for a JavaDoc behavior change introduced in OpenJDK 8 that breaks 505 // links in documentation and the content of API files like current.txt. 506 // http://b/18051133. 507 String name = m.name(); 508 ClassDoc containingClass = m.containingClass(); 509 if (containingClass.containingClass() != null) { 510 // This should detect the new behavior and be bypassed otherwise. 511 if (!name.contains(".")) { 512 // Constructors of inner classes do not contain the name of the enclosing class 513 // with OpenJDK 8. This simulates the old behavior: 514 name = containingClass.name(); 515 } 516 } 517 // End of workaround. 518 MethodInfo result = 519 new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))), 520 name, m.signature(), Converter.obtainClass(m.containingClass()), Converter 521 .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m 522 .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(), 523 false, m.isSynchronized(), m.isNative(), false/*isDefault*/, false, "constructor", m.flatSignature(), 524 null, null, new ArrayList<ParameterInfo>(Arrays.asList(Converter.convertParameters(m.parameters(), m))), 525 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(m.thrownExceptions()))), Converter.convertSourcePosition(m 526 .position()), new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter.convertAnnotationInstances(m.annotations())))); 527 result.setVarargs(m.isVarArgs()); 528 result.init(null); 529 return result; 530 } 531 } 532 }; 533 534 535 private static FieldInfo[] convertFields(FieldDoc[] fields) { 536 if (fields == null) return null; 537 ArrayList<FieldInfo> out = new ArrayList<FieldInfo>(); 538 int N = fields.length; 539 for (int i = 0; i < N; i++) { 540 FieldInfo f = Converter.obtainField(fields[i]); 541 if (f.checkLevel()) { 542 out.add(f); 543 } 544 } 545 return out.toArray(new FieldInfo[out.size()]); 546 } 547 548 private static FieldInfo obtainField(FieldDoc o) { 549 return (FieldInfo) mFields.obtain(o); 550 } 551 552 private static FieldInfo obtainField(ConstructorDoc o) { 553 return (FieldInfo) mFields.obtain(o); 554 } 555 556 private static Cache mFields = new Cache() { 557 @Override 558 protected Object make(Object o) { 559 FieldDoc f = (FieldDoc) o; 560 return new FieldInfo(f.name(), Converter.obtainClass(f.containingClass()), Converter 561 .obtainClass(f.containingClass()), f.isPublic(), f.isProtected(), f.isPackagePrivate(), f 562 .isPrivate(), f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(), 563 f.isSynthetic(), Converter.obtainType(f.type()), f.getRawCommentText(), 564 f.constantValue(), Converter.convertSourcePosition(f.position()), 565 new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter 566 .convertAnnotationInstances(f.annotations())))); 567 } 568 }; 569 570 private static PackageInfo obtainPackage(PackageDoc o) { 571 return (PackageInfo) mPackagees.obtain(o); 572 } 573 574 private static Cache mPackagees = new Cache() { 575 @Override 576 protected Object make(Object o) { 577 PackageDoc p = (PackageDoc) o; 578 return new PackageInfo(p, p.name(), Converter.convertSourcePosition(p.position())); 579 } 580 }; 581 582 private static TypeInfo obtainType(Type o) { 583 return (TypeInfo) mTypes.obtain(o); 584 } 585 586 private static Cache mTypes = new Cache() { 587 @Override 588 protected Object make(Object o) { 589 Type t = (Type) o; 590 String simpleTypeName; 591 if (t instanceof ClassDoc) { 592 simpleTypeName = ((ClassDoc) t).name(); 593 } else { 594 simpleTypeName = t.simpleTypeName(); 595 } 596 TypeInfo ti = 597 new TypeInfo(t.isPrimitive(), t.dimension(), simpleTypeName, t.qualifiedTypeName(), 598 Converter.obtainClass(t.asClassDoc())); 599 return ti; 600 } 601 602 @Override 603 protected void made(Object o, Object r) { 604 Type t = (Type) o; 605 TypeInfo ti = (TypeInfo) r; 606 if (t.asParameterizedType() != null) { 607 ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asParameterizedType().typeArguments())))); 608 } else if (t instanceof ClassDoc) { 609 ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(((ClassDoc) t).typeParameters())))); 610 } else if (t.asTypeVariable() != null) { 611 ti.setBounds(null, new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes((t.asTypeVariable().bounds()))))); 612 ti.setIsTypeVariable(true); 613 } else if (t.asWildcardType() != null) { 614 ti.setIsWildcard(true); 615 ti.setBounds(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().superBounds()))), 616 new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().extendsBounds())))); 617 } 618 } 619 620 @Override 621 protected Object keyFor(Object o) { 622 Type t = (Type) o; 623 while (t.asAnnotatedType() != null) { 624 t = t.asAnnotatedType().underlyingType(); 625 } 626 String keyString = t.getClass().getName() + "/" + t.toString() + "/"; 627 if (t.asParameterizedType() != null) { 628 keyString += t.asParameterizedType().toString() + "/"; 629 if (t.asParameterizedType().typeArguments() != null) { 630 for (Type ty : t.asParameterizedType().typeArguments()) { 631 keyString += ty.toString() + "/"; 632 } 633 } 634 } else { 635 keyString += "NoParameterizedType//"; 636 } 637 if (t.asTypeVariable() != null) { 638 keyString += t.asTypeVariable().toString() + "/"; 639 if (t.asTypeVariable().bounds() != null) { 640 for (Type ty : t.asTypeVariable().bounds()) { 641 keyString += ty.toString() + "/"; 642 } 643 } 644 } else { 645 keyString += "NoTypeVariable//"; 646 } 647 if (t.asWildcardType() != null) { 648 keyString += t.asWildcardType().toString() + "/"; 649 if (t.asWildcardType().superBounds() != null) { 650 for (Type ty : t.asWildcardType().superBounds()) { 651 keyString += ty.toString() + "/"; 652 } 653 } 654 if (t.asWildcardType().extendsBounds() != null) { 655 for (Type ty : t.asWildcardType().extendsBounds()) { 656 keyString += ty.toString() + "/"; 657 } 658 } 659 } else { 660 keyString += "NoWildCardType//"; 661 } 662 663 return keyString; 664 } 665 }; 666 667 public static TypeInfo obtainTypeFromString(String type) { 668 return (TypeInfo) mTypesFromString.obtain(type); 669 } 670 671 private static final Cache mTypesFromString = new Cache() { 672 @Override 673 protected Object make(Object o) { 674 String name = (String) o; 675 return new TypeInfo(name); 676 } 677 678 @Override 679 protected void made(Object o, Object r) { 680 681 } 682 683 @Override 684 protected Object keyFor(Object o) { 685 return o; 686 } 687 }; 688 689 private static MemberInfo obtainMember(MemberDoc o) { 690 return (MemberInfo) mMembers.obtain(o); 691 } 692 693 private static Cache mMembers = new Cache() { 694 @Override 695 protected Object make(Object o) { 696 if (o instanceof MethodDoc) { 697 return Converter.obtainMethod((MethodDoc) o); 698 } else if (o instanceof ConstructorDoc) { 699 return Converter.obtainMethod((ConstructorDoc) o); 700 } else if (o instanceof FieldDoc) { 701 return Converter.obtainField((FieldDoc) o); 702 } else { 703 return null; 704 } 705 } 706 }; 707 708 private static AnnotationInstanceInfo[] convertAnnotationInstances(AnnotationDesc[] orig) { 709 int len = orig.length; 710 AnnotationInstanceInfo[] out = new AnnotationInstanceInfo[len]; 711 for (int i = 0; i < len; i++) { 712 out[i] = Converter.obtainAnnotationInstance(orig[i]); 713 } 714 return out; 715 } 716 717 718 private static AnnotationInstanceInfo obtainAnnotationInstance(AnnotationDesc o) { 719 return (AnnotationInstanceInfo) mAnnotationInstances.obtain(o); 720 } 721 722 private static Cache mAnnotationInstances = new Cache() { 723 @Override 724 protected Object make(Object o) { 725 AnnotationDesc a = (AnnotationDesc) o; 726 ClassInfo annotationType = Converter.obtainClass(a.annotationType()); 727 AnnotationDesc.ElementValuePair[] ev = a.elementValues(); 728 AnnotationValueInfo[] elementValues = new AnnotationValueInfo[ev.length]; 729 for (int i = 0; i < ev.length; i++) { 730 elementValues[i] = 731 obtainAnnotationValue(ev[i].value(), Converter.obtainMethod(ev[i].element())); 732 } 733 return new AnnotationInstanceInfo(annotationType, elementValues); 734 } 735 }; 736 737 738 private abstract static class Cache { 739 void put(Object key, Object value) { 740 mCache.put(key, value); 741 } 742 743 Object obtain(Object o) { 744 if (o == null) { 745 return null; 746 } 747 Object k = keyFor(o); 748 Object r = mCache.get(k); 749 if (r == null) { 750 r = make(o); 751 mCache.put(k, r); 752 made(o, r); 753 } 754 return r; 755 } 756 757 protected HashMap<Object, Object> mCache = new HashMap<Object, Object>(); 758 759 protected abstract Object make(Object o); 760 761 protected void made(Object o, Object r) {} 762 763 protected Object keyFor(Object o) { 764 return o; 765 } 766 767 Collection<?> all() { 768 return null; 769 } 770 } 771 772 // annotation values 773 private static HashMap<AnnotationValue, AnnotationValueInfo> mAnnotationValues = 774 new HashMap<AnnotationValue, AnnotationValueInfo>(); 775 private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit = 776 new HashSet<AnnotationValue>(); 777 778 private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element) { 779 if (o == null) { 780 return null; 781 } 782 AnnotationValueInfo v = mAnnotationValues.get(o); 783 if (v != null) return v; 784 v = new AnnotationValueInfo(element); 785 mAnnotationValues.put(o, v); 786 if (mAnnotationValuesNeedingInit != null) { 787 mAnnotationValuesNeedingInit.add(o); 788 } else { 789 initAnnotationValue(o, v); 790 } 791 return v; 792 } 793 794 private static void initAnnotationValue(AnnotationValue o, AnnotationValueInfo v) { 795 Object orig = o.value(); 796 Object converted; 797 if (orig instanceof Type) { 798 // class literal 799 converted = Converter.obtainType((Type) orig); 800 } else if (orig instanceof FieldDoc) { 801 // enum constant 802 converted = Converter.obtainField((FieldDoc) orig); 803 } else if (orig instanceof AnnotationDesc) { 804 // annotation instance 805 converted = Converter.obtainAnnotationInstance((AnnotationDesc) orig); 806 } else if (orig instanceof AnnotationValue[]) { 807 AnnotationValue[] old = (AnnotationValue[]) orig; 808 ArrayList<AnnotationValueInfo> values = new ArrayList<AnnotationValueInfo>(); 809 for (int i = 0; i < old.length; i++) { 810 values.add(Converter.obtainAnnotationValue(old[i], null)); 811 } 812 converted = values; 813 } else { 814 converted = orig; 815 } 816 v.init(converted); 817 } 818 819 private static void finishAnnotationValueInit() { 820 int depth = 0; 821 while (mAnnotationValuesNeedingInit.size() > 0) { 822 HashSet<AnnotationValue> set = mAnnotationValuesNeedingInit; 823 mAnnotationValuesNeedingInit = new HashSet<AnnotationValue>(); 824 for (AnnotationValue o : set) { 825 AnnotationValueInfo v = mAnnotationValues.get(o); 826 initAnnotationValue(o, v); 827 } 828 depth++; 829 } 830 mAnnotationValuesNeedingInit = null; 831 } 832 } 833