1 /* 2 * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.google.doclava.javadoc; 27 28 import com.google.doclava.annotation.Unused; 29 import com.google.doclava.annotation.Used; 30 import com.sun.javadoc.AnnotatedType; 31 import com.sun.javadoc.AnnotationTypeDoc; 32 import com.sun.javadoc.ClassDoc; 33 import com.sun.javadoc.ConstructorDoc; 34 import com.sun.javadoc.FieldDoc; 35 import com.sun.javadoc.MethodDoc; 36 import com.sun.javadoc.PackageDoc; 37 import com.sun.javadoc.ParamTag; 38 import com.sun.javadoc.ParameterizedType; 39 import com.sun.javadoc.Type; 40 import com.sun.javadoc.TypeVariable; 41 import com.sun.javadoc.WildcardType; 42 import com.sun.tools.javac.code.Symbol.ClassSymbol; 43 import com.sun.tools.javac.comp.AttrContext; 44 import com.sun.tools.javac.comp.Env; 45 import com.sun.tools.javac.code.Scope; 46 import com.sun.tools.javac.code.Symbol; 47 import com.sun.tools.javac.util.Names; 48 import javax.lang.model.element.ElementKind; 49 import javax.lang.model.element.ExecutableElement; 50 import javax.lang.model.element.NestingKind; 51 import javax.lang.model.element.TypeElement; 52 import javax.lang.model.element.VariableElement; 53 import javax.lang.model.type.TypeKind; 54 import javax.lang.model.type.TypeMirror; 55 import javax.lang.model.util.ElementFilter; 56 import jdk.javadoc.internal.tool.DocEnvImpl; 57 import jdk.javadoc.internal.tool.ToolEnvironment; 58 59 import static com.sun.tools.javac.code.Kinds.Kind.*; 60 61 class ClassDocImpl extends ProgramElementDocImpl<TypeElement> implements ClassDoc { 62 63 protected final TypeElement typeElement; 64 65 // Cached fields 66 private ConstructorDoc[] constructorsFiltered; 67 private ConstructorDoc[] constructorsAll; 68 private Type[] interfaceTypes; 69 private ClassDoc[] interfaces; 70 private TypeVariable[] typeParameters; 71 private MethodDoc[] methodsFiltered; 72 private MethodDoc[] methodsAll; 73 private FieldDoc[] fieldsFiltered; 74 private FieldDoc[] fieldsAll; 75 private FieldDoc[] enumConstants; 76 private ClassDoc[] innerClassesFiltered; 77 private ClassDoc[] innerClassesAll; 78 ClassDocImpl(TypeElement c, Context context)79 protected ClassDocImpl(TypeElement c, Context context) { 80 super(c, context); 81 typeElement = c; 82 83 if (c.getKind().isInterface()) { 84 reflectModifiers |= java.lang.reflect.Modifier.INTERFACE; 85 } 86 } 87 create(TypeElement e, Context context)88 static ClassDocImpl create(TypeElement e, Context context) { 89 return context.caches.classes.computeIfAbsent(e, el -> new ClassDocImpl(el, context)); 90 } 91 92 @Override 93 @Unused(implemented = true) modifiers()94 public String modifiers() { 95 return java.lang.reflect.Modifier.toString(modifierSpecifier()); 96 } 97 98 @Override 99 @Unused(implemented = true) modifierSpecifier()100 public int modifierSpecifier() { 101 if (isInterface() || isAnnotationType()) { 102 return reflectModifiers & ~java.lang.reflect.Modifier.ABSTRACT; 103 } 104 return reflectModifiers; 105 } 106 107 private Boolean isClass; 108 109 @Override 110 @Unused(implemented = true) isClass()111 public boolean isClass() { 112 if (isClass == null) { 113 isClass = typeElement.getKind().isClass(); 114 } 115 return isClass; 116 } 117 118 private Boolean isOrdinaryClass; 119 120 @Override 121 @Used(implemented = true) isOrdinaryClass()122 public boolean isOrdinaryClass() { 123 if (isOrdinaryClass == null) { 124 isOrdinaryClass = (!isEnum() && 125 !isInterface() && 126 !isAnnotationType() && 127 !isError() && 128 !isException() 129 ); 130 } 131 return isOrdinaryClass; 132 } 133 134 private Boolean isEnum; 135 136 @Override 137 @Used(implemented = true) isEnum()138 public boolean isEnum() { 139 if (isEnum == null) { 140 isEnum = (typeElement.getKind() == ElementKind.ENUM); 141 } 142 return isEnum; 143 } 144 145 private Boolean isInterface; 146 147 @Override 148 @Used(implemented = true) isInterface()149 public boolean isInterface() { 150 if (isInterface == null) { 151 isInterface = (typeElement.getKind() == ElementKind.INTERFACE); 152 } 153 return isInterface; 154 } 155 156 private Boolean isException; 157 158 @Override 159 @Used(implemented = true) isException()160 public boolean isException() { 161 if (isException == null) { 162 isException = context.docletElementUtils.isException(typeElement); 163 } 164 return isException; 165 } 166 167 private Boolean isError; 168 169 @Override 170 @Used(implemented = true) isError()171 public boolean isError() { 172 if (isError == null) { 173 isError = context.docletElementUtils.isError(typeElement); 174 } 175 return isError; 176 } 177 178 private String name; 179 180 @Override 181 @Used(implemented = true) name()182 public String name() { 183 if (name == null) { 184 name = context.docletElementUtils.getClassNameUntilNotNested(typeElement); 185 } 186 return name; 187 } 188 189 private String qualifiedName; 190 191 @Override 192 @Used(implemented = true) qualifiedName()193 public String qualifiedName() { 194 if (qualifiedName == null) { 195 qualifiedName = typeElement.getQualifiedName().toString(); 196 } 197 return qualifiedName; 198 } 199 200 @Override 201 @Used(implemented = true) isIncluded()202 public boolean isIncluded() { 203 return context.environment.isIncluded(typeElement); 204 } 205 206 @Override 207 @Used(implemented = true) isAbstract()208 public boolean isAbstract() { 209 return java.lang.reflect.Modifier.isAbstract(reflectModifiers); 210 } 211 212 private Boolean isSerializable; 213 214 @Override 215 @Used(implemented = true) isSerializable()216 public boolean isSerializable() { 217 if (isSerializable == null) { 218 var serializable = context.environment.getElementUtils() 219 .getTypeElement("java.io.Serializable").asType(); 220 isSerializable = context.environment.getTypeUtils() 221 .isSubtype(typeElement.asType(), serializable); 222 } 223 return isSerializable; 224 } 225 226 private Boolean isExternalizable; 227 228 @Override 229 @Unused(implemented = true) isExternalizable()230 public boolean isExternalizable() { 231 if (isExternalizable == null) { 232 var externalizable = context.environment.getElementUtils() 233 .getTypeElement("java.io.Externalizable").asType(); 234 isExternalizable = context.environment.getTypeUtils() 235 .isSubtype(typeElement.asType(), externalizable); 236 } 237 return isExternalizable; 238 } 239 240 @Override 241 @Unused serializationMethods()242 public MethodDoc[] serializationMethods() { 243 throw new UnsupportedOperationException("not yet implemented"); 244 } 245 246 @Override 247 @Unused serializableFields()248 public FieldDoc[] serializableFields() { 249 throw new UnsupportedOperationException("not yet implemented"); 250 } 251 252 @Override 253 @Unused definesSerializableFields()254 public boolean definesSerializableFields() { 255 throw new UnsupportedOperationException("not yet implemented"); 256 } 257 258 @Override 259 @Used(implemented = true) superclass()260 public ClassDoc superclass() { 261 if (isInterface()) { 262 return null; 263 } 264 TypeMirror superclassMirror = typeElement.getSuperclass(); 265 if (superclassMirror.getKind() == TypeKind.NONE) { 266 return null; 267 } 268 return TypeImpl.create(superclassMirror, context).asClassDoc(); 269 } 270 271 @Override 272 @Used(implemented = true) superclassType()273 public Type superclassType() { 274 if (isInterface()) { 275 return null; 276 } 277 TypeMirror superclassMirror = typeElement.getSuperclass(); 278 if (superclassMirror.getKind() == TypeKind.NONE) { 279 return null; 280 } 281 return TypeImpl.create(superclassMirror, context); 282 } 283 284 @Override 285 @Unused(implemented = true) subclassOf(ClassDoc cd)286 public boolean subclassOf(ClassDoc cd) { 287 TypeElement other = context.environment.getElementUtils() 288 .getTypeElement(cd.qualifiedName()); 289 if (isInterface()) { 290 return other.getQualifiedName().contentEquals("java.lang.Object"); 291 } 292 return context.environment.getTypeUtils().isSubtype(typeElement.asType(), other.asType()); 293 } 294 295 @Override 296 @Used(implemented = true) interfaces()297 public ClassDoc[] interfaces() { 298 if (interfaces == null) { 299 interfaces = typeElement.getInterfaces() 300 .stream() 301 .map(typeMirror -> { 302 TypeElement asElement = (TypeElement) context.environment.getTypeUtils() 303 .asElement(typeMirror); 304 return ClassDocImpl.create(asElement, context); 305 }) 306 .toArray(ClassDoc[]::new); 307 } 308 return interfaces; 309 } 310 311 @Override 312 @Used(implemented = true) interfaceTypes()313 public Type[] interfaceTypes() { 314 if (interfaceTypes == null) { 315 interfaceTypes = typeElement.getInterfaces() 316 .stream() 317 .filter(typeMirror -> !typeMirror.getKind().equals(TypeKind.NONE)) 318 .map(typeMirror -> TypeImpl.create(typeMirror, context)) 319 .toArray(Type[]::new); 320 } 321 return interfaceTypes; 322 } 323 324 @Override 325 @Used(implemented = true) typeParameters()326 public TypeVariable[] typeParameters() { 327 if (typeParameters == null) { 328 typeParameters = typeElement.getTypeParameters() 329 .stream() 330 .map(tp -> { 331 javax.lang.model.type.TypeVariable tv = (javax.lang.model.type.TypeVariable) tp.asType(); 332 return TypeVariableImpl.create(tv, context); 333 }) 334 .toArray(TypeVariable[]::new); 335 } 336 return typeParameters; 337 } 338 339 @Override 340 @Unused typeParamTags()341 public ParamTag[] typeParamTags() { 342 throw new UnsupportedOperationException("not yet implemented"); 343 } 344 345 @Override 346 @Unused(implemented = true) fields()347 public FieldDoc[] fields() { 348 if (fieldsAll == null) { 349 fieldsAll = getFields(true); 350 } 351 return fieldsAll; 352 } 353 354 @Override 355 @Used(implemented = true) fields(boolean filter)356 public FieldDoc[] fields(boolean filter) { 357 if (filter) { 358 if (fieldsFiltered == null) { 359 fieldsFiltered = getFields(true); 360 } 361 return fieldsFiltered; 362 } else { 363 if (fieldsAll == null) { 364 fieldsAll = getFields(false); 365 } 366 return fieldsAll; 367 } 368 } 369 getFields(boolean filter)370 private FieldDoc[] getFields(boolean filter) { 371 return typeElement.getEnclosedElements() 372 .stream() 373 .filter(e -> e.getKind() == ElementKind.FIELD) 374 .filter(field -> !filter || context.environment.isSelected(field)) 375 .map(field -> FieldDocImpl.create((VariableElement) field, context)) 376 .toArray(FieldDoc[]::new); 377 } 378 379 @Override 380 @Used(implemented = true) enumConstants()381 public FieldDoc[] enumConstants() { 382 if (enumConstants == null) { 383 enumConstants = typeElement.getEnclosedElements() 384 .stream() 385 .filter(e -> e.getKind() == ElementKind.ENUM_CONSTANT) 386 .map(enumConstant -> FieldDocImpl.create((VariableElement) enumConstant, 387 context)) 388 .toArray(FieldDoc[]::new); 389 } 390 return enumConstants; 391 } 392 393 @Override 394 @Unused(implemented = true) methods()395 public MethodDoc[] methods() { 396 if (methodsAll == null) { 397 methodsAll = getMethods(true); 398 } 399 return methodsAll; 400 } 401 402 @Override 403 @Used(implemented = true) methods(boolean filter)404 public MethodDoc[] methods(boolean filter) { 405 if (filter) { 406 if (methodsFiltered == null) { 407 methodsFiltered = getMethods(true); 408 } 409 return methodsFiltered; 410 } else { 411 if (methodsAll == null) { 412 methodsAll = getMethods(false); 413 } 414 return methodsAll; 415 } 416 } 417 getMethods(boolean filter)418 private MethodDoc[] getMethods(boolean filter) { 419 return typeElement.getEnclosedElements() 420 .stream() 421 .filter(e -> e.getKind() == ElementKind.METHOD) 422 .filter(method -> !filter || context.environment.isSelected(method)) 423 .map(method -> MethodDocImpl.create((ExecutableElement) method, context)) 424 .toArray(MethodDoc[]::new); 425 } 426 427 @Override 428 @Unused(implemented = true) constructors()429 public ConstructorDoc[] constructors() { 430 if (constructorsFiltered == null) { 431 constructorsFiltered = getConstructors(true); 432 } 433 return constructorsFiltered; 434 } 435 436 @Override 437 @Used(implemented = true) constructors(boolean filter)438 public ConstructorDoc[] constructors(boolean filter) { 439 if (filter) { 440 if (constructorsFiltered == null) { 441 constructorsFiltered = getConstructors(true); 442 } 443 return constructorsFiltered; 444 } else { 445 if (constructorsAll == null) { 446 constructorsAll = getConstructors(false); 447 } 448 return constructorsAll; 449 } 450 } 451 getConstructors(boolean filter)452 private ConstructorDoc[] getConstructors(boolean filter) { 453 return typeElement.getEnclosedElements() 454 .stream() 455 .filter(e -> e.getKind() == ElementKind.CONSTRUCTOR) 456 .filter(ctor -> !filter || context.environment.isSelected(ctor)) 457 .map(e -> ConstructorDocImpl.create((ExecutableElement) e, context)) 458 .toArray(ConstructorDoc[]::new); 459 } 460 461 @Override 462 @Used(implemented = true) innerClasses()463 public ClassDoc[] innerClasses() { 464 if (innerClassesFiltered == null) { 465 innerClassesFiltered = getInnerClasses(true); 466 } 467 return innerClassesFiltered; 468 } 469 470 @Override 471 @Used(implemented = true) innerClasses(boolean filter)472 public ClassDoc[] innerClasses(boolean filter) { 473 if (filter) { 474 return innerClasses(); 475 } else { 476 if (innerClassesAll == null) { 477 innerClassesAll = getInnerClasses(false); 478 } 479 return innerClassesAll; 480 } 481 } 482 getInnerClasses(boolean filter)483 private ClassDoc[] getInnerClasses(boolean filter) { 484 return ElementFilter.typesIn(typeElement.getEnclosedElements()) 485 .stream() 486 .filter(te -> te.getNestingKind() == NestingKind.MEMBER && 487 (te.getKind() == ElementKind.CLASS || te.getKind() == ElementKind.INTERFACE) 488 ) 489 .filter(te -> !filter || context.environment.isSelected(te)) 490 .map(te -> ClassDocImpl.create(te, context)) 491 .toArray(ClassDoc[]::new); 492 } 493 494 /** 495 * Note that this implementation does not search in sources! 496 * 497 * <p> 498 * 499 * {@inheritDoc} 500 * 501 * @implNote Does not search in sources. 502 */ 503 @Override 504 @Used(implemented = true) findClass(String className)505 public ClassDoc findClass(String className) { 506 ClassDoc result = searchClass(className); 507 if (result != null) { 508 return result; 509 } 510 511 ClassDoc enclosing = containingClass(); 512 while (enclosing != null && enclosing.containingClass() != null) { 513 enclosing = enclosing.containingClass(); 514 } 515 if (enclosing == null) { 516 return null; 517 } 518 return ((ClassDocImpl) enclosing).searchClass(className); 519 } 520 searchClass(String className)521 private ClassDoc searchClass(String className) { 522 TypeElement cls = context.environment.getElementUtils().getTypeElement(className); 523 if (cls != null) { 524 return ClassDocImpl.create(cls, context); 525 } 526 527 for (ClassDoc nested : innerClasses()) { 528 if (nested.name().equals(className) || nested.name().endsWith("." + className)) { 529 return nested; 530 } else { 531 ClassDoc inNested = ((ClassDocImpl) nested).searchClass(className); 532 if (inNested != null) { 533 return inNested; 534 } 535 } 536 } 537 538 ClassDoc inPackage = containingPackage().findClass(className); 539 if (inPackage != null) { 540 return inPackage; 541 } 542 543 // 544 if (! (typeElement instanceof ClassSymbol)) { 545 return null; 546 } 547 ClassSymbol tsym = (ClassSymbol)typeElement; 548 // make sure that this symbol has been completed 549 // TODO: do we need this anymore ? 550 if (tsym.completer != null) { 551 tsym.complete(); 552 } 553 554 // search imports 555 if (tsym.sourcefile != null) { 556 557 ToolEnvironment toolEnv = ((DocEnvImpl)(context.environment)).toolEnv; 558 //### This information is available only for source classes. 559 Env<AttrContext> compenv = toolEnv.getEnv(tsym); 560 if (compenv == null) { 561 return null; 562 } 563 Names names = tsym.name.table.names; 564 Scope s = compenv.toplevel.namedImportScope; 565 for (Symbol sym : s.getSymbolsByName(names.fromString(className))) { 566 if (sym.kind == TYP) { 567 return ClassDocImpl.create((TypeElement)sym, context); 568 } 569 } 570 571 s = compenv.toplevel.starImportScope; 572 for (Symbol sym : s.getSymbolsByName(names.fromString(className))) { 573 if (sym.kind == TYP) { 574 return ClassDocImpl.create((TypeElement)sym, context); 575 } 576 } 577 } 578 579 return null; 580 } 581 582 @Override 583 @Unused importedClasses()584 public ClassDoc[] importedClasses() { 585 throw new UnsupportedOperationException("not yet implemented"); 586 } 587 588 @Override 589 @Unused importedPackages()590 public PackageDoc[] importedPackages() { 591 throw new UnsupportedOperationException("not yet implemented"); 592 } 593 594 @Override 595 @Unused(implemented = true) typeName()596 public String typeName() { 597 return name(); 598 } 599 600 @Override 601 @Used(implemented = true) qualifiedTypeName()602 public String qualifiedTypeName() { 603 return qualifiedName(); 604 } 605 606 private String simpleTypeName; 607 608 @Override 609 @Used(implemented = true) simpleTypeName()610 public String simpleTypeName() { 611 if (simpleTypeName == null) { 612 simpleTypeName = typeElement.getSimpleName().toString(); 613 } 614 return simpleTypeName; 615 } 616 617 @Override 618 @Used(implemented = true) dimension()619 public String dimension() { 620 return ""; 621 } 622 623 @Override 624 @Used(implemented = true) isPrimitive()625 public boolean isPrimitive() { 626 return false; 627 } 628 629 @Override 630 @Used(implemented = true) asClassDoc()631 public ClassDoc asClassDoc() { 632 return this; 633 } 634 635 @Override 636 @Used(implemented = true) asParameterizedType()637 public ParameterizedType asParameterizedType() { 638 return null; 639 } 640 641 @Override 642 @Used(implemented = true) asTypeVariable()643 public TypeVariable asTypeVariable() { 644 return null; 645 } 646 647 @Override 648 @Used(implemented = true) asWildcardType()649 public WildcardType asWildcardType() { 650 return null; 651 } 652 653 @Override 654 @Used(implemented = true) asAnnotatedType()655 public AnnotatedType asAnnotatedType() { 656 return null; 657 } 658 659 @Override 660 @Unused(implemented = true) asAnnotationTypeDoc()661 public AnnotationTypeDoc asAnnotationTypeDoc() { 662 return null; 663 } 664 665 @Override 666 @Used(implemented = true) getElementType()667 public Type getElementType() { 668 return null; 669 } 670 } 671