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.Doc; 27 import com.sun.javadoc.ExecutableMemberDoc; 28 import com.sun.javadoc.FieldDoc; 29 import com.sun.javadoc.MemberDoc; 30 import com.sun.javadoc.MethodDoc; 31 import com.sun.javadoc.PackageDoc; 32 import com.sun.javadoc.ParamTag; 33 import com.sun.javadoc.Parameter; 34 import com.sun.javadoc.RootDoc; 35 import com.sun.javadoc.SeeTag; 36 import com.sun.javadoc.SourcePosition; 37 import com.sun.javadoc.Tag; 38 import com.sun.javadoc.ThrowsTag; 39 import com.sun.javadoc.Type; 40 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.Collection; 44 import java.util.HashMap; 45 import java.util.HashSet; 46 import java.util.List; 47 48 public class Converter { 49 private static RootDoc root; 50 private static List<ApiInfo> apis; 51 makeInfo(RootDoc r)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 */ addApiInfo(ApiInfo api)90 public static void addApiInfo(ApiInfo api) { 91 apis.add(api); 92 } 93 getClasses(RootDoc r)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 rootClasses()115 public static Collection<ClassInfo> rootClasses() { 116 return Arrays.asList(mRootClasses); 117 } 118 allClasses()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 initClass(ClassDoc c, ClassInfo cl)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 */ obtainClass(String className)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 */ obtainPackage(String packageName)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 convertTag(Tag tag)221 private static TagInfo convertTag(Tag tag) { 222 return new TextTagInfo(tag.name(), tag.kind(), tag.text(), 223 Converter.convertSourcePosition(tag.position())); 224 } 225 convertThrowsTag(ThrowsTag tag, ContainerInfo base)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 convertParamTag(ParamTag tag, ContainerInfo base)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 convertSeeTag(SeeTag tag, ContainerInfo base)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 convertSourcePosition(SourcePosition sp)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 convertTags(Tag[] tags, ContainerInfo base)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 convertClasses(ClassDoc[] classes)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 convertParameter(Parameter p, SourcePosition pos, boolean isVarArg)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 convertParameters(Parameter[] p, ExecutableMemberDoc m)290 private static ParameterInfo[] convertParameters(Parameter[] p, ExecutableMemberDoc m) { 291 SourcePosition pos = getPositionSafe(m); 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 convertTypes(Type[] p)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 Converter()311 private Converter() {} 312 313 private static class ClassNeedingInit { ClassNeedingInit(ClassDoc c, ClassInfo cl)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 obtainClass(ClassDoc o)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 getHiddenMethods(MethodDoc[] methods)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 getRemovedMethods(MethodDoc[] methods)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 */ convertAllFields(FieldDoc[] fields)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 */ convertAllMethods(ExecutableMemberDoc[] methods)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 */ convertMethods(ExecutableMemberDoc[] methods)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 convertNonWrittenConstructors(ConstructorDoc[] methods)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 obtainMethod(E o)446 private static <E extends ExecutableMemberDoc> MethodInfo obtainMethod(E o) { 447 return (MethodInfo) mMethods.obtain(o); 448 } 449 450 /** 451 * Returns the result of a call to {@code f.position()} but avoids a {@link NullPointerException} 452 * if {@code f.position()} throws a {@link NullPointerException} due to the {@link Doc} element 453 * being related to an annotation. 454 * 455 * <p>This is consistent with the Java runtime where references to annotations that do not exist 456 * on the classpath are allowed. This method is used, for example, to allow Doclava to process 457 * JARs in the classpath that were generated by the Kotlin compiler, without requiring the Kotlin 458 * stdlib to exist on the classpath when Doclava is executed. 459 */ getPositionSafe(Doc f)460 private static SourcePosition getPositionSafe(Doc f) { 461 try { 462 return f.position(); 463 } catch (NullPointerException e) { 464 if (f.isAnnotationTypeElement() || mFieldDocElementsOnAnnotationClasses.contains(f)) { 465 return null; 466 } 467 throw e; 468 } 469 } 470 471 private static Cache mMethods = new Cache() { 472 @Override 473 protected Object make(Object o) { 474 if (o instanceof AnnotationTypeElementDoc) { 475 AnnotationTypeElementDoc m = (AnnotationTypeElementDoc) o; 476 MethodInfo result = 477 new MethodInfo(m.getRawCommentText(), 478 new ArrayList<TypeInfo>(Arrays.asList( 479 Converter.convertTypes(m.typeParameters()))), 480 m.name(), m.signature(), Converter.obtainClass(m.containingClass()), 481 Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m 482 .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(), 483 m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), true, 484 "annotationElement", m.flatSignature(), 485 Converter.obtainMethod(m.overriddenMethod()), 486 Converter.obtainType(m.returnType()), 487 new ArrayList<ParameterInfo>(Arrays.asList( 488 Converter.convertParameters(m.parameters(), m))), 489 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses( 490 m.thrownExceptions()))), Converter.convertSourcePosition(getPositionSafe(m)), 491 new ArrayList<AnnotationInstanceInfo>(Arrays.asList( 492 Converter.convertAnnotationInstances(m.annotations())))); 493 result.setVarargs(m.isVarArgs()); 494 result.init(Converter.obtainAnnotationValue(m.defaultValue(), result)); 495 return result; 496 } else if (o instanceof MethodDoc) { 497 MethodDoc m = (MethodDoc) o; 498 final ClassInfo containingClass = Converter.obtainClass(m.containingClass()); 499 500 // The containing class is final, so it is implied that every method is final as well. 501 // No need to apply 'final' to each method. 502 final boolean isMethodFinal = m.isFinal() && !containingClass.isFinal(); 503 MethodInfo result = 504 new MethodInfo(m.getRawCommentText(), 505 new ArrayList<TypeInfo>(Arrays.asList( 506 Converter.convertTypes(m.typeParameters()))), m.name(), m.signature(), 507 containingClass, containingClass, m.isPublic(), m.isProtected(), 508 m.isPackagePrivate(), m.isPrivate(), isMethodFinal, m.isStatic(), 509 m.isSynthetic(), m.isAbstract(), m.isSynchronized(), m.isNative(), 510 m.isDefault(), false, "method", m.flatSignature(), 511 Converter.obtainMethod(m.overriddenMethod()), 512 Converter.obtainType(m.returnType()), 513 new ArrayList<ParameterInfo>(Arrays.asList( 514 Converter.convertParameters(m.parameters(), m))), 515 new ArrayList<ClassInfo>(Arrays.asList( 516 Converter.convertClasses(m.thrownExceptions()))), 517 Converter.convertSourcePosition(m.position()), 518 new ArrayList<AnnotationInstanceInfo>(Arrays.asList( 519 Converter.convertAnnotationInstances(m.annotations())))); 520 result.setVarargs(m.isVarArgs()); 521 result.init(null); 522 return result; 523 } else { 524 ConstructorDoc m = (ConstructorDoc) o; 525 MethodInfo result = 526 new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))), 527 m.name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter 528 .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m 529 .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(), 530 false, m.isSynchronized(), m.isNative(), false/*isDefault*/, false, "constructor", m.flatSignature(), 531 null, null, new ArrayList<ParameterInfo>(Arrays.asList(Converter.convertParameters(m.parameters(), m))), 532 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(m.thrownExceptions()))), Converter.convertSourcePosition(m 533 .position()), new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter.convertAnnotationInstances(m.annotations())))); 534 result.setVarargs(m.isVarArgs()); 535 result.init(null); 536 return result; 537 } 538 } 539 }; 540 541 convertFields(FieldDoc[] fields)542 private static FieldInfo[] convertFields(FieldDoc[] fields) { 543 if (fields == null) return null; 544 ArrayList<FieldInfo> out = new ArrayList<FieldInfo>(); 545 int N = fields.length; 546 for (int i = 0; i < N; i++) { 547 FieldInfo f = Converter.obtainField(fields[i]); 548 if (f.checkLevel()) { 549 out.add(f); 550 } 551 } 552 return out.toArray(new FieldInfo[out.size()]); 553 } 554 obtainField(FieldDoc o)555 private static FieldInfo obtainField(FieldDoc o) { 556 return (FieldInfo) mFields.obtain(o); 557 } 558 obtainField(ConstructorDoc o)559 private static FieldInfo obtainField(ConstructorDoc o) { 560 return (FieldInfo) mFields.obtain(o); 561 } 562 563 private static Cache mFields = new Cache() { 564 @Override 565 protected Object make(Object o) { 566 FieldDoc f = (FieldDoc) o; 567 return new FieldInfo(f.name(), Converter.obtainClass(f.containingClass()), Converter 568 .obtainClass(f.containingClass()), f.isPublic(), f.isProtected(), f.isPackagePrivate(), f 569 .isPrivate(), f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(), 570 f.isSynthetic(), Converter.obtainType(f.type()), f.getRawCommentText(), 571 f.constantValue(), Converter.convertSourcePosition(getPositionSafe(f)), 572 new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter 573 .convertAnnotationInstances(f.annotations())))); 574 } 575 }; 576 obtainPackage(PackageDoc o)577 private static PackageInfo obtainPackage(PackageDoc o) { 578 return (PackageInfo) mPackagees.obtain(o); 579 } 580 581 private static Cache mPackagees = new Cache() { 582 @Override 583 protected Object make(Object o) { 584 PackageDoc p = (PackageDoc) o; 585 return new PackageInfo(p, p.name(), Converter.convertSourcePosition(p.position())); 586 } 587 }; 588 obtainType(Type o)589 private static TypeInfo obtainType(Type o) { 590 return (TypeInfo) mTypes.obtain(o); 591 } 592 593 private static Cache mTypes = new Cache() { 594 @Override 595 protected Object make(Object o) { 596 Type t = (Type) o; 597 String simpleTypeName; 598 if (t instanceof ClassDoc) { 599 simpleTypeName = ((ClassDoc) t).name(); 600 } else { 601 simpleTypeName = t.simpleTypeName(); 602 } 603 TypeInfo ti = 604 new TypeInfo(t.isPrimitive(), t.dimension(), simpleTypeName, t.qualifiedTypeName(), 605 Converter.obtainClass(t.asClassDoc())); 606 return ti; 607 } 608 609 @Override 610 protected void made(Object o, Object r) { 611 Type t = (Type) o; 612 TypeInfo ti = (TypeInfo) r; 613 if (t.asParameterizedType() != null) { 614 ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asParameterizedType().typeArguments())))); 615 } else if (t instanceof ClassDoc) { 616 ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(((ClassDoc) t).typeParameters())))); 617 } else if (t.asTypeVariable() != null) { 618 ti.setBounds(null, new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes((t.asTypeVariable().bounds()))))); 619 ti.setIsTypeVariable(true); 620 } else if (t.asWildcardType() != null) { 621 ti.setIsWildcard(true); 622 ti.setBounds(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().superBounds()))), 623 new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().extendsBounds())))); 624 } 625 } 626 627 @Override 628 protected Object keyFor(Object o) { 629 Type t = (Type) o; 630 while (t.asAnnotatedType() != null) { 631 t = t.asAnnotatedType().underlyingType(); 632 } 633 String keyString = t.getClass().getName() + "/" + t.toString() + "/"; 634 if (t.asParameterizedType() != null) { 635 keyString += t.asParameterizedType().toString() + "/"; 636 if (t.asParameterizedType().typeArguments() != null) { 637 for (Type ty : t.asParameterizedType().typeArguments()) { 638 keyString += ty.toString() + "/"; 639 } 640 } 641 } else { 642 keyString += "NoParameterizedType//"; 643 } 644 if (t.asTypeVariable() != null) { 645 keyString += t.asTypeVariable().toString() + "/"; 646 if (t.asTypeVariable().bounds() != null) { 647 for (Type ty : t.asTypeVariable().bounds()) { 648 keyString += ty.toString() + "/"; 649 } 650 } 651 } else { 652 keyString += "NoTypeVariable//"; 653 } 654 if (t.asWildcardType() != null) { 655 keyString += t.asWildcardType().toString() + "/"; 656 if (t.asWildcardType().superBounds() != null) { 657 for (Type ty : t.asWildcardType().superBounds()) { 658 keyString += ty.toString() + "/"; 659 } 660 } 661 if (t.asWildcardType().extendsBounds() != null) { 662 for (Type ty : t.asWildcardType().extendsBounds()) { 663 keyString += ty.toString() + "/"; 664 } 665 } 666 } else { 667 keyString += "NoWildCardType//"; 668 } 669 670 return keyString; 671 } 672 }; 673 obtainTypeFromString(String type)674 public static TypeInfo obtainTypeFromString(String type) { 675 return (TypeInfo) mTypesFromString.obtain(type); 676 } 677 678 private static final Cache mTypesFromString = new Cache() { 679 @Override 680 protected Object make(Object o) { 681 String name = (String) o; 682 return new TypeInfo(name); 683 } 684 685 @Override 686 protected void made(Object o, Object r) { 687 688 } 689 690 @Override 691 protected Object keyFor(Object o) { 692 return o; 693 } 694 }; 695 obtainMember(MemberDoc o)696 private static MemberInfo obtainMember(MemberDoc o) { 697 return (MemberInfo) mMembers.obtain(o); 698 } 699 700 private static Cache mMembers = new Cache() { 701 @Override 702 protected Object make(Object o) { 703 if (o instanceof MethodDoc) { 704 return Converter.obtainMethod((MethodDoc) o); 705 } else if (o instanceof ConstructorDoc) { 706 return Converter.obtainMethod((ConstructorDoc) o); 707 } else if (o instanceof FieldDoc) { 708 return Converter.obtainField((FieldDoc) o); 709 } else { 710 return null; 711 } 712 } 713 }; 714 convertAnnotationInstances(AnnotationDesc[] orig)715 private static AnnotationInstanceInfo[] convertAnnotationInstances(AnnotationDesc[] orig) { 716 int len = orig.length; 717 AnnotationInstanceInfo[] out = new AnnotationInstanceInfo[len]; 718 for (int i = 0; i < len; i++) { 719 out[i] = Converter.obtainAnnotationInstance(orig[i]); 720 } 721 return out; 722 } 723 724 obtainAnnotationInstance(AnnotationDesc o)725 private static AnnotationInstanceInfo obtainAnnotationInstance(AnnotationDesc o) { 726 return (AnnotationInstanceInfo) mAnnotationInstances.obtain(o); 727 } 728 729 private static Cache mAnnotationInstances = new Cache() { 730 @Override 731 protected Object make(Object o) { 732 AnnotationDesc a = (AnnotationDesc) o; 733 ClassInfo annotationType = Converter.obtainClass(a.annotationType()); 734 AnnotationDesc.ElementValuePair[] ev = a.elementValues(); 735 AnnotationValueInfo[] elementValues = new AnnotationValueInfo[ev.length]; 736 for (int i = 0; i < ev.length; i++) { 737 elementValues[i] = 738 obtainAnnotationValue(ev[i].value(), Converter.obtainMethod(ev[i].element())); 739 } 740 return new AnnotationInstanceInfo(annotationType, elementValues); 741 } 742 }; 743 744 745 private abstract static class Cache { put(Object key, Object value)746 void put(Object key, Object value) { 747 mCache.put(key, value); 748 } 749 obtain(Object o)750 Object obtain(Object o) { 751 if (o == null) { 752 return null; 753 } 754 Object k = keyFor(o); 755 Object r = mCache.get(k); 756 if (r == null) { 757 r = make(o); 758 mCache.put(k, r); 759 made(o, r); 760 } 761 return r; 762 } 763 764 protected HashMap<Object, Object> mCache = new HashMap<Object, Object>(); 765 make(Object o)766 protected abstract Object make(Object o); 767 made(Object o, Object r)768 protected void made(Object o, Object r) {} 769 keyFor(Object o)770 protected Object keyFor(Object o) { 771 return o; 772 } 773 all()774 Collection<?> all() { 775 return null; 776 } 777 } 778 779 // annotation values 780 private static HashMap<AnnotationValue, AnnotationValueInfo> mAnnotationValues = 781 new HashMap<AnnotationValue, AnnotationValueInfo>(); 782 private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit = 783 new HashSet<AnnotationValue>(); 784 /** 785 * Stores the {@link FieldDoc} instances the are contained in annotation classes. This is used by 786 * {@link #getPositionSafe(Doc)} to allow {@link NullPointerException} for annotation classes to 787 * be ignored. 788 */ 789 private static HashSet<FieldDoc> mFieldDocElementsOnAnnotationClasses = new HashSet<FieldDoc>(); 790 obtainAnnotationValue(AnnotationValue o, MethodInfo element)791 private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element) { 792 if (o == null) { 793 return null; 794 } 795 AnnotationValueInfo v = mAnnotationValues.get(o); 796 if (v != null) return v; 797 v = new AnnotationValueInfo(element); 798 mAnnotationValues.put(o, v); 799 if (o.value() instanceof FieldDoc) { 800 mFieldDocElementsOnAnnotationClasses.add((FieldDoc) o.value()); 801 } 802 if (mAnnotationValuesNeedingInit != null) { 803 mAnnotationValuesNeedingInit.add(o); 804 } else { 805 initAnnotationValue(o, v); 806 } 807 return v; 808 } 809 initAnnotationValue(AnnotationValue o, AnnotationValueInfo v)810 private static void initAnnotationValue(AnnotationValue o, AnnotationValueInfo v) { 811 Object orig = o.value(); 812 Object converted; 813 if (orig instanceof Type) { 814 // class literal 815 converted = Converter.obtainType((Type) orig); 816 } else if (orig instanceof FieldDoc) { 817 // enum constant 818 converted = Converter.obtainField((FieldDoc) orig); 819 } else if (orig instanceof AnnotationDesc) { 820 // annotation instance 821 converted = Converter.obtainAnnotationInstance((AnnotationDesc) orig); 822 } else if (orig instanceof AnnotationValue[]) { 823 AnnotationValue[] old = (AnnotationValue[]) orig; 824 ArrayList<AnnotationValueInfo> values = new ArrayList<AnnotationValueInfo>(); 825 for (int i = 0; i < old.length; i++) { 826 values.add(Converter.obtainAnnotationValue(old[i], null)); 827 } 828 converted = values; 829 } else { 830 converted = orig; 831 } 832 v.init(converted); 833 } 834 finishAnnotationValueInit()835 private static void finishAnnotationValueInit() { 836 int depth = 0; 837 while (mAnnotationValuesNeedingInit.size() > 0) { 838 HashSet<AnnotationValue> set = mAnnotationValuesNeedingInit; 839 mAnnotationValuesNeedingInit = new HashSet<AnnotationValue>(); 840 for (AnnotationValue o : set) { 841 AnnotationValueInfo v = mAnnotationValues.get(o); 842 initAnnotationValue(o, v); 843 } 844 depth++; 845 } 846 mAnnotationValuesNeedingInit = null; 847 } 848 } 849