1 /* 2 * Copyright (c) 1999, 2021, 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 jdk.javadoc.internal.doclets.toolkit.util; 27 28 import java.lang.annotation.Documented; 29 import java.lang.ref.SoftReference; 30 import java.net.URI; 31 import java.text.CollationKey; 32 import java.text.Collator; 33 import java.text.ParseException; 34 import java.text.RuleBasedCollator; 35 import java.util.ArrayDeque; 36 import java.util.ArrayList; 37 import java.util.Collection; 38 import java.util.Collections; 39 import java.util.Deque; 40 import java.util.EnumSet; 41 import java.util.HashMap; 42 import java.util.HashSet; 43 import java.util.Iterator; 44 import java.util.LinkedHashMap; 45 import java.util.LinkedHashSet; 46 import java.util.List; 47 import java.util.Locale; 48 import java.util.Map; 49 import java.util.Map.Entry; 50 import java.util.Objects; 51 import java.util.Set; 52 import java.util.SortedSet; 53 import java.util.TreeMap; 54 import java.util.TreeSet; 55 import java.util.function.Predicate; 56 import java.util.stream.Collectors; 57 58 import javax.lang.model.AnnotatedConstruct; 59 import javax.lang.model.SourceVersion; 60 import javax.lang.model.element.AnnotationMirror; 61 import javax.lang.model.element.AnnotationValue; 62 import javax.lang.model.element.Element; 63 import javax.lang.model.element.ElementKind; 64 import javax.lang.model.element.ExecutableElement; 65 import javax.lang.model.element.Modifier; 66 import javax.lang.model.element.ModuleElement; 67 import javax.lang.model.element.ModuleElement.RequiresDirective; 68 import javax.lang.model.element.PackageElement; 69 import javax.lang.model.element.RecordComponentElement; 70 import javax.lang.model.element.TypeElement; 71 import javax.lang.model.element.TypeParameterElement; 72 import javax.lang.model.element.VariableElement; 73 import javax.lang.model.type.ArrayType; 74 import javax.lang.model.type.DeclaredType; 75 import javax.lang.model.type.ErrorType; 76 import javax.lang.model.type.ExecutableType; 77 import javax.lang.model.type.NoType; 78 import javax.lang.model.type.PrimitiveType; 79 import javax.lang.model.type.TypeMirror; 80 import javax.lang.model.type.TypeVariable; 81 import javax.lang.model.type.WildcardType; 82 import javax.lang.model.util.ElementFilter; 83 import javax.lang.model.util.Elements; 84 import javax.lang.model.util.SimpleAnnotationValueVisitor14; 85 import javax.lang.model.util.SimpleElementVisitor14; 86 import javax.lang.model.util.SimpleTypeVisitor14; 87 import javax.lang.model.util.TypeKindVisitor9; 88 import javax.lang.model.util.Types; 89 import javax.tools.FileObject; 90 import javax.tools.JavaFileManager; 91 import javax.tools.JavaFileManager.Location; 92 import javax.tools.StandardLocation; 93 94 import com.sun.source.doctree.BlockTagTree; 95 import com.sun.source.doctree.DeprecatedTree; 96 import com.sun.source.doctree.DocCommentTree; 97 import com.sun.source.doctree.DocTree; 98 import com.sun.source.doctree.DocTree.Kind; 99 import com.sun.source.doctree.EndElementTree; 100 import com.sun.source.doctree.ParamTree; 101 import com.sun.source.doctree.ProvidesTree; 102 import com.sun.source.doctree.ReturnTree; 103 import com.sun.source.doctree.SeeTree; 104 import com.sun.source.doctree.SerialDataTree; 105 import com.sun.source.doctree.SerialFieldTree; 106 import com.sun.source.doctree.SerialTree; 107 import com.sun.source.doctree.StartElementTree; 108 import com.sun.source.doctree.TextTree; 109 import com.sun.source.doctree.ThrowsTree; 110 import com.sun.source.doctree.UnknownBlockTagTree; 111 import com.sun.source.doctree.UsesTree; 112 import com.sun.source.tree.CompilationUnitTree; 113 import com.sun.source.tree.LineMap; 114 import com.sun.source.util.DocSourcePositions; 115 import com.sun.source.util.DocTrees; 116 import com.sun.source.util.TreePath; 117 import com.sun.tools.javac.model.JavacTypes; 118 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; 119 import jdk.javadoc.internal.doclets.toolkit.BaseOptions; 120 import jdk.javadoc.internal.doclets.toolkit.CommentUtils; 121 import jdk.javadoc.internal.doclets.toolkit.CommentUtils.DocCommentInfo; 122 import jdk.javadoc.internal.doclets.toolkit.Resources; 123 import jdk.javadoc.internal.doclets.toolkit.taglets.BaseTaglet; 124 import jdk.javadoc.internal.doclets.toolkit.taglets.Taglet; 125 import jdk.javadoc.internal.tool.DocEnvImpl; 126 127 import static javax.lang.model.element.ElementKind.*; 128 import static javax.lang.model.type.TypeKind.*; 129 130 import static com.sun.source.doctree.DocTree.Kind.*; 131 import static jdk.javadoc.internal.doclets.toolkit.builders.ConstantsSummaryBuilder.MAX_CONSTANT_VALUE_INDEX_LENGTH; 132 133 /** 134 * Utilities Class for Doclets. 135 * 136 * <p><b>This is NOT part of any supported API. 137 * If you write code that depends on this, you do so at your own risk. 138 * This code and its internal interfaces are subject to change or 139 * deletion without notice.</b> 140 */ 141 public class Utils { 142 public final BaseConfiguration configuration; 143 private final BaseOptions options; 144 private final Resources resources; 145 public final DocTrees docTrees; 146 public final Elements elementUtils; 147 public final Types typeUtils; 148 public final Comparators comparators; 149 private final JavaScriptScanner javaScriptScanner; 150 Utils(BaseConfiguration c)151 public Utils(BaseConfiguration c) { 152 configuration = c; 153 options = configuration.getOptions(); 154 resources = configuration.getDocResources(); 155 elementUtils = c.docEnv.getElementUtils(); 156 typeUtils = c.docEnv.getTypeUtils(); 157 docTrees = c.docEnv.getDocTrees(); 158 javaScriptScanner = c.isAllowScriptInComments() ? null : new JavaScriptScanner(); 159 comparators = new Comparators(this); 160 } 161 162 // our own little symbol table 163 private HashMap<String, TypeMirror> symtab = new HashMap<>(); 164 getSymbol(String signature)165 public TypeMirror getSymbol(String signature) { 166 TypeMirror type = symtab.get(signature); 167 if (type == null) { 168 TypeElement typeElement = elementUtils.getTypeElement(signature); 169 if (typeElement == null) 170 return null; 171 type = typeElement.asType(); 172 if (type == null) 173 return null; 174 symtab.put(signature, type); 175 } 176 return type; 177 } 178 getObjectType()179 public TypeMirror getObjectType() { 180 return getSymbol("java.lang.Object"); 181 } 182 getExceptionType()183 public TypeMirror getExceptionType() { 184 return getSymbol("java.lang.Exception"); 185 } 186 getErrorType()187 public TypeMirror getErrorType() { 188 return getSymbol("java.lang.Error"); 189 } 190 getSerializableType()191 public TypeMirror getSerializableType() { 192 return getSymbol("java.io.Serializable"); 193 } 194 getExternalizableType()195 public TypeMirror getExternalizableType() { 196 return getSymbol("java.io.Externalizable"); 197 } 198 getIllegalArgumentExceptionType()199 public TypeMirror getIllegalArgumentExceptionType() { 200 return getSymbol("java.lang.IllegalArgumentException"); 201 } 202 getNullPointerExceptionType()203 public TypeMirror getNullPointerExceptionType() { 204 return getSymbol("java.lang.NullPointerException"); 205 } 206 getDeprecatedType()207 public TypeMirror getDeprecatedType() { 208 return getSymbol("java.lang.Deprecated"); 209 } 210 getFunctionalInterface()211 public TypeMirror getFunctionalInterface() { 212 return getSymbol("java.lang.FunctionalInterface"); 213 } 214 215 /** 216 * Return array of class members whose documentation is to be generated. 217 * If the member is deprecated do not include such a member in the 218 * returned array. 219 * 220 * @param members Array of members to choose from. 221 * @return List List of eligible members for whom 222 * documentation is getting generated. 223 */ excludeDeprecatedMembers(List<? extends Element> members)224 public List<Element> excludeDeprecatedMembers(List<? extends Element> members) { 225 return members.stream() 226 .filter(member -> !isDeprecated(member)) 227 .sorted(comparators.makeGeneralPurposeComparator()) 228 .collect(Collectors.toCollection(ArrayList::new)); 229 } 230 231 /** 232 * Search for the given method in the given class. 233 * 234 * @param te Class to search into. 235 * @param method Method to be searched. 236 * 237 * @return Method found, null otherwise. 238 */ findMethod(TypeElement te, ExecutableElement method)239 public ExecutableElement findMethod(TypeElement te, ExecutableElement method) { 240 for (ExecutableElement m : getMethods(te)) { 241 if (executableMembersEqual(method, m)) { 242 return m; 243 } 244 } 245 return null; 246 } 247 248 /** 249 * Test whether a class is a subclass of another class. 250 * 251 * @param t1 the candidate superclass. 252 * @param t2 the target 253 * @return true if t1 is a superclass of t2. 254 */ isSubclassOf(TypeElement t1, TypeElement t2)255 public boolean isSubclassOf(TypeElement t1, TypeElement t2) { 256 return typeUtils.isSubtype(typeUtils.erasure(t1.asType()), typeUtils.erasure(t2.asType())); 257 } 258 259 /** 260 * @param e1 the first method to compare. 261 * @param e2 the second method to compare. 262 * @return true if member1 overrides/hides or is overridden/hidden by member2. 263 */ executableMembersEqual(ExecutableElement e1, ExecutableElement e2)264 public boolean executableMembersEqual(ExecutableElement e1, ExecutableElement e2) { 265 // TODO: investigate if Elements.hides(..) will work here. 266 if (isStatic(e1) && isStatic(e2)) { 267 List<? extends VariableElement> parameters1 = e1.getParameters(); 268 List<? extends VariableElement> parameters2 = e2.getParameters(); 269 if (e1.getSimpleName().equals(e2.getSimpleName()) && 270 parameters1.size() == parameters2.size()) { 271 int j; 272 for (j = 0 ; j < parameters1.size(); j++) { 273 VariableElement v1 = parameters1.get(j); 274 VariableElement v2 = parameters2.get(j); 275 String t1 = getTypeName(v1.asType(), true); 276 String t2 = getTypeName(v2.asType(), true); 277 if (!(t1.equals(t2) || 278 isTypeVariable(v1.asType()) || isTypeVariable(v2.asType()))) { 279 break; 280 } 281 } 282 if (j == parameters1.size()) { 283 return true; 284 } 285 } 286 return false; 287 } else { 288 return elementUtils.overrides(e1, e2, getEnclosingTypeElement(e1)) || 289 elementUtils.overrides(e2, e1, getEnclosingTypeElement(e2)) || 290 e1.equals(e2); 291 } 292 } 293 294 /** 295 * According to <cite>The Java Language Specification</cite>, 296 * all the outer classes and static inner classes are core classes. 297 */ isCoreClass(TypeElement e)298 public boolean isCoreClass(TypeElement e) { 299 return getEnclosingTypeElement(e) == null || isStatic(e); 300 } 301 getLocationForPackage(PackageElement pd)302 public Location getLocationForPackage(PackageElement pd) { 303 ModuleElement mdle = configuration.docEnv.getElementUtils().getModuleOf(pd); 304 305 if (mdle == null) 306 return defaultLocation(); 307 308 return getLocationForModule(mdle); 309 } 310 getLocationForModule(ModuleElement mdle)311 public Location getLocationForModule(ModuleElement mdle) { 312 Location loc = configuration.workArounds.getLocationForModule(mdle); 313 if (loc != null) 314 return loc; 315 316 return defaultLocation(); 317 } 318 defaultLocation()319 private Location defaultLocation() { 320 JavaFileManager fm = configuration.docEnv.getJavaFileManager(); 321 return fm.hasLocation(StandardLocation.SOURCE_PATH) 322 ? StandardLocation.SOURCE_PATH 323 : StandardLocation.CLASS_PATH; 324 } 325 isAnnotated(TypeMirror e)326 public boolean isAnnotated(TypeMirror e) { 327 return !e.getAnnotationMirrors().isEmpty(); 328 } 329 isAnnotated(Element e)330 public boolean isAnnotated(Element e) { 331 return !e.getAnnotationMirrors().isEmpty(); 332 } 333 isAnnotationType(Element e)334 public boolean isAnnotationType(Element e) { 335 return new SimpleElementVisitor14<Boolean, Void>() { 336 @Override 337 public Boolean visitExecutable(ExecutableElement e, Void p) { 338 return visit(e.getEnclosingElement()); 339 } 340 341 @Override 342 public Boolean visitUnknown(Element e, Void p) { 343 return false; 344 } 345 346 @Override 347 protected Boolean defaultAction(Element e, Void p) { 348 return e.getKind() == ANNOTATION_TYPE; 349 } 350 }.visit(e); 351 } 352 353 /** 354 * An Enum implementation is almost identical, thus this method returns if 355 * this element represents a CLASS or an ENUM 356 * @param e element 357 * @return true if class or enum 358 */ 359 public boolean isClass(Element e) { 360 return e.getKind().isClass(); 361 } 362 363 public boolean isConstructor(Element e) { 364 return e.getKind() == CONSTRUCTOR; 365 } 366 367 public boolean isEnum(Element e) { 368 return e.getKind() == ENUM; 369 } 370 371 boolean isEnumConstant(Element e) { 372 return e.getKind() == ENUM_CONSTANT; 373 } 374 375 public boolean isField(Element e) { 376 return e.getKind() == FIELD; 377 } 378 379 public boolean isInterface(Element e) { 380 return e.getKind() == INTERFACE; 381 } 382 383 public boolean isMethod(Element e) { 384 return e.getKind() == METHOD; 385 } 386 387 public boolean isModule(Element e) { 388 return e.getKind() == ElementKind.MODULE; 389 } 390 391 public boolean isPackage(Element e) { 392 return e.getKind() == ElementKind.PACKAGE; 393 } 394 395 public boolean isAbstract(Element e) { 396 return e.getModifiers().contains(Modifier.ABSTRACT); 397 } 398 399 public boolean isDefault(Element e) { 400 return e.getModifiers().contains(Modifier.DEFAULT); 401 } 402 403 public boolean isFinal(Element e) { 404 return e.getModifiers().contains(Modifier.FINAL); 405 } 406 407 public boolean isPackagePrivate(Element e) { 408 return !(isPublic(e) || isPrivate(e) || isProtected(e)); 409 } 410 411 public boolean isPrivate(Element e) { 412 return e.getModifiers().contains(Modifier.PRIVATE); 413 } 414 415 public boolean isProtected(Element e) { 416 return e.getModifiers().contains(Modifier.PROTECTED); 417 } 418 419 public boolean isPublic(Element e) { 420 return e.getModifiers().contains(Modifier.PUBLIC); 421 } 422 423 public boolean isProperty(String name) { 424 return options.javafx() && name.endsWith("Property"); 425 } 426 427 public String getPropertyName(String name) { 428 return isProperty(name) 429 ? name.substring(0, name.length() - "Property".length()) 430 : name; 431 } 432 433 public String getPropertyLabel(String name) { 434 return name.substring(0, name.lastIndexOf("Property")); 435 } 436 437 public boolean isOverviewElement(Element e) { 438 return e.getKind() == ElementKind.OTHER; 439 } 440 441 public boolean isStatic(Element e) { 442 return e.getModifiers().contains(Modifier.STATIC); 443 } 444 445 public boolean isSerializable(TypeElement e) { 446 return typeUtils.isSubtype(e.asType(), getSerializableType()); 447 } 448 449 public boolean isExternalizable(TypeElement e) { 450 return typeUtils.isSubtype(e.asType(), getExternalizableType()); 451 } 452 453 public boolean isRecord(TypeElement e) { 454 return e.getKind() == ElementKind.RECORD; 455 } 456 457 public boolean isCanonicalRecordConstructor(ExecutableElement ee) { 458 TypeElement te = (TypeElement) ee.getEnclosingElement(); 459 List<? extends RecordComponentElement> stateComps = te.getRecordComponents(); 460 List<? extends VariableElement> params = ee.getParameters(); 461 if (stateComps.size() != params.size()) { 462 return false; 463 } 464 465 Iterator<? extends RecordComponentElement> stateIter = stateComps.iterator(); 466 Iterator<? extends VariableElement> paramIter = params.iterator(); 467 while (paramIter.hasNext() && stateIter.hasNext()) { 468 VariableElement param = paramIter.next(); 469 RecordComponentElement comp = stateIter.next(); 470 if (!Objects.equals(param.getSimpleName(), comp.getSimpleName()) 471 || !typeUtils.isSameType(param.asType(), comp.asType())) { 472 return false; 473 } 474 } 475 476 return true; 477 } 478 479 public SortedSet<VariableElement> serializableFields(TypeElement aclass) { 480 return configuration.workArounds.getSerializableFields(aclass); 481 } 482 483 public SortedSet<ExecutableElement> serializationMethods(TypeElement aclass) { 484 return configuration.workArounds.getSerializationMethods(aclass); 485 } 486 487 public boolean definesSerializableFields(TypeElement aclass) { 488 return configuration.workArounds.definesSerializableFields( aclass); 489 } 490 491 public boolean isFunctionalInterface(AnnotationMirror amirror) { 492 return amirror.getAnnotationType().equals(getFunctionalInterface()) && 493 configuration.docEnv.getSourceVersion() 494 .compareTo(SourceVersion.RELEASE_8) >= 0; 495 } 496 497 public boolean isNoType(TypeMirror t) { 498 return t.getKind() == NONE; 499 } 500 501 public boolean isOrdinaryClass(TypeElement te) { 502 if (isEnum(te) || isInterface(te) || isAnnotationType(te) || isRecord(te)) { 503 return false; 504 } 505 if (isError(te) || isException(te)) { 506 return false; 507 } 508 return true; 509 } 510 511 public boolean isUndocumentedEnclosure(TypeElement enclosingTypeElement) { 512 return (isPackagePrivate(enclosingTypeElement) || isPrivate(enclosingTypeElement) 513 || hasHiddenTag(enclosingTypeElement)) 514 && !isLinkable(enclosingTypeElement); 515 } 516 517 public boolean isError(TypeElement te) { 518 if (isEnum(te) || isInterface(te) || isAnnotationType(te)) { 519 return false; 520 } 521 return typeUtils.isSubtype(te.asType(), getErrorType()); 522 } 523 524 public boolean isException(TypeElement te) { 525 if (isEnum(te) || isInterface(te) || isAnnotationType(te)) { 526 return false; 527 } 528 return typeUtils.isSubtype(te.asType(), getExceptionType()); 529 } 530 531 public boolean isPrimitive(TypeMirror t) { 532 return new SimpleTypeVisitor14<Boolean, Void>() { 533 534 @Override 535 public Boolean visitNoType(NoType t, Void p) { 536 return t.getKind() == VOID; 537 } 538 @Override 539 public Boolean visitPrimitive(PrimitiveType t, Void p) { 540 return true; 541 } 542 @Override 543 public Boolean visitArray(ArrayType t, Void p) { 544 return visit(t.getComponentType()); 545 } 546 @Override 547 protected Boolean defaultAction(TypeMirror e, Void p) { 548 return false; 549 } 550 }.visit(t); 551 } 552 553 public boolean isExecutableElement(Element e) { 554 ElementKind kind = e.getKind(); 555 switch (kind) { 556 case CONSTRUCTOR: case METHOD: case INSTANCE_INIT: 557 return true; 558 default: 559 return false; 560 } 561 } 562 563 public boolean isVariableElement(Element e) { 564 ElementKind kind = e.getKind(); 565 switch(kind) { 566 case ENUM_CONSTANT: case EXCEPTION_PARAMETER: case FIELD: 567 case LOCAL_VARIABLE: case PARAMETER: 568 case RESOURCE_VARIABLE: 569 return true; 570 default: 571 return false; 572 } 573 } 574 575 public boolean isTypeElement(Element e) { 576 switch (e.getKind()) { 577 case CLASS: case ENUM: case INTERFACE: case ANNOTATION_TYPE: case RECORD: 578 return true; 579 default: 580 return false; 581 } 582 } 583 584 /** 585 * Get the signature of an executable element with qualified parameter types 586 * in the context of type element {@code site}. 587 * For instance, for a method {@code mymethod(String x, int y)}, 588 * it will return {@code (java.lang.String,int)}. 589 * 590 * @param e the executable element 591 * @param site the contextual site 592 * @return String signature with qualified parameter types 593 */ 594 public String signature(ExecutableElement e, TypeElement site) { 595 return makeSignature(e, site, true); 596 } 597 598 /** 599 * Get the flat signature of an executable element with simple (unqualified) 600 * parameter types in the context of type element {@code site}. 601 * For instance, for a method {@code mymethod(String x, int y)}, 602 * it will return {@code (String, int)}. 603 * 604 * @param e the executable element 605 * @param site the contextual site 606 * @return signature with simple (unqualified) parameter types 607 */ 608 public String flatSignature(ExecutableElement e, TypeElement site) { 609 return makeSignature(e, site, false); 610 } 611 612 public String makeSignature(ExecutableElement e, TypeElement site, boolean full) { 613 return makeSignature(e, site, full, false); 614 } 615 616 public String makeSignature(ExecutableElement e, TypeElement site, boolean full, boolean ignoreTypeParameters) { 617 StringBuilder result = new StringBuilder(); 618 result.append("("); 619 ExecutableType executableType = asInstantiatedMethodType(site, e); 620 Iterator<? extends TypeMirror> iterator = executableType.getParameterTypes().iterator(); 621 while (iterator.hasNext()) { 622 TypeMirror type = iterator.next(); 623 result.append(getTypeSignature(type, full, ignoreTypeParameters)); 624 if (iterator.hasNext()) { 625 result.append(", "); 626 } 627 } 628 if (e.isVarArgs()) { 629 int len = result.length(); 630 result.replace(len - 2, len, "..."); 631 } 632 result.append(")"); 633 return result.toString(); 634 } 635 636 public String getTypeSignature(TypeMirror t, boolean qualifiedName, boolean noTypeParameters) { 637 return new SimpleTypeVisitor14<StringBuilder, Void>() { 638 final StringBuilder sb = new StringBuilder(); 639 640 @Override 641 public StringBuilder visitArray(ArrayType t, Void p) { 642 TypeMirror componentType = t.getComponentType(); 643 visit(componentType); 644 sb.append("[]"); 645 return sb; 646 } 647 648 @Override 649 public StringBuilder visitDeclared(DeclaredType t, Void p) { 650 Element e = t.asElement(); 651 sb.append(qualifiedName ? getFullyQualifiedName(e) : getSimpleName(e)); 652 List<? extends TypeMirror> typeArguments = t.getTypeArguments(); 653 if (typeArguments.isEmpty() || noTypeParameters) { 654 return sb; 655 } 656 sb.append("<"); 657 Iterator<? extends TypeMirror> iterator = typeArguments.iterator(); 658 while (iterator.hasNext()) { 659 TypeMirror ta = iterator.next(); 660 visit(ta); 661 if (iterator.hasNext()) { 662 sb.append(", "); 663 } 664 } 665 sb.append(">"); 666 return sb; 667 } 668 669 @Override 670 public StringBuilder visitPrimitive(PrimitiveType t, Void p) { 671 sb.append(t.getKind().toString().toLowerCase(Locale.ROOT)); 672 return sb; 673 } 674 675 @Override 676 public StringBuilder visitTypeVariable(TypeVariable t, Void p) { 677 Element e = t.asElement(); 678 sb.append(qualifiedName ? getFullyQualifiedName(e, false) : getSimpleName(e)); 679 return sb; 680 } 681 682 @Override 683 public StringBuilder visitWildcard(WildcardType t, Void p) { 684 sb.append("?"); 685 TypeMirror upperBound = t.getExtendsBound(); 686 if (upperBound != null) { 687 sb.append(" extends "); 688 visit(upperBound); 689 } 690 TypeMirror superBound = t.getSuperBound(); 691 if (superBound != null) { 692 sb.append(" super "); 693 visit(superBound); 694 } 695 return sb; 696 } 697 698 @Override 699 protected StringBuilder defaultAction(TypeMirror e, Void p) { 700 return sb.append(e); 701 } 702 }.visit(t).toString(); 703 } 704 705 public boolean isArrayType(TypeMirror t) { 706 return t.getKind() == ARRAY; 707 } 708 709 public boolean isDeclaredType(TypeMirror t) { 710 return t.getKind() == DECLARED; 711 } 712 713 public boolean isErrorType(TypeMirror t) { 714 return t.getKind() == ERROR; 715 } 716 717 public boolean isIntersectionType(TypeMirror t) { 718 return t.getKind() == INTERSECTION; 719 } 720 721 public boolean isTypeParameterElement(Element e) { 722 return e.getKind() == TYPE_PARAMETER; 723 } 724 725 public boolean isTypeVariable(TypeMirror t) { 726 return t.getKind() == TYPEVAR; 727 } 728 729 public boolean isVoid(TypeMirror t) { 730 return t.getKind() == VOID; 731 } 732 733 public boolean isWildCard(TypeMirror t) { 734 return t.getKind() == WILDCARD; 735 } 736 737 public boolean ignoreBounds(TypeMirror bound) { 738 return bound.equals(getObjectType()) && !isAnnotated(bound); 739 } 740 741 /* 742 * a direct port of TypeVariable.getBounds 743 */ 744 public List<? extends TypeMirror> getBounds(TypeParameterElement tpe) { 745 List<? extends TypeMirror> bounds = tpe.getBounds(); 746 if (!bounds.isEmpty()) { 747 TypeMirror upperBound = bounds.get(bounds.size() - 1); 748 if (ignoreBounds(upperBound)) { 749 return Collections.emptyList(); 750 } 751 } 752 return bounds; 753 } 754 755 /** 756 * Returns the TypeMirror of the ExecutableElement if it is a method, or null 757 * if it is a constructor. 758 * @param site the contextual type 759 * @param ee the ExecutableElement 760 * @return the return type 761 */ 762 public TypeMirror getReturnType(TypeElement site, ExecutableElement ee) { 763 return ee.getKind() == CONSTRUCTOR ? null : asInstantiatedMethodType(site, ee).getReturnType(); 764 } 765 766 /** 767 * Returns the ExecutableType corresponding to the type of the method declaration seen as a 768 * member of a given declared type. This might cause type-variable substitution to kick in. 769 * @param site the contextual type. 770 * @param ee the method declaration. 771 * @return the instantiated method type. 772 */ 773 public ExecutableType asInstantiatedMethodType(TypeElement site, ExecutableElement ee) { 774 return shouldInstantiate(site, ee) ? 775 (ExecutableType)typeUtils.asMemberOf((DeclaredType)site.asType(), ee) : 776 (ExecutableType)ee.asType(); 777 } 778 779 /** 780 * Returns the TypeMirror corresponding to the type of the field declaration seen as a 781 * member of a given declared type. This might cause type-variable substitution to kick in. 782 * @param site the contextual type. 783 * @param ve the field declaration. 784 * @return the instantiated field type. 785 */ 786 public TypeMirror asInstantiatedFieldType(TypeElement site, VariableElement ve) { 787 return shouldInstantiate(site, ve) ? 788 typeUtils.asMemberOf((DeclaredType)site.asType(), ve) : 789 ve.asType(); 790 } 791 792 /* 793 * We should not instantiate if (i) there's no contextual type declaration, (ii) the declaration 794 * to which the member belongs to is the same as the one under consideration, (iii) if the 795 * declaration to which the member belongs to is not generic. 796 */ 797 private boolean shouldInstantiate(TypeElement site, Element e) { 798 return site != null && 799 site != e.getEnclosingElement() && 800 !((DeclaredType)e.getEnclosingElement().asType()).getTypeArguments().isEmpty(); 801 } 802 803 /** 804 * Return the type containing the method that this method overrides. 805 * It may be a {@code TypeElement} or a {@code TypeParameterElement}. 806 */ 807 public TypeMirror overriddenType(ExecutableElement method) { 808 return configuration.workArounds.overriddenType(method); 809 } 810 811 private TypeMirror getType(TypeMirror t) { 812 return (isNoType(t)) ? getObjectType() : t; 813 } 814 815 public TypeMirror getSuperType(TypeElement te) { 816 TypeMirror t = te.getSuperclass(); 817 return getType(t); 818 } 819 820 /** 821 * Return the class that originally defined the method that 822 * is overridden by the current definition, or null if no 823 * such class exists. 824 * 825 * @return a TypeElement representing the superclass that 826 * originally defined this method, null if this method does 827 * not override a definition in a superclass. 828 */ 829 public TypeElement overriddenClass(ExecutableElement ee) { 830 TypeMirror type = overriddenType(ee); 831 return (type != null) ? asTypeElement(type) : null; 832 } 833 834 public ExecutableElement overriddenMethod(ExecutableElement method) { 835 if (isStatic(method)) { 836 return null; 837 } 838 final TypeElement origin = getEnclosingTypeElement(method); 839 for (TypeMirror t = getSuperType(origin); 840 t.getKind() == DECLARED; 841 t = getSuperType(asTypeElement(t))) { 842 TypeElement te = asTypeElement(t); 843 if (te == null) { 844 return null; 845 } 846 VisibleMemberTable vmt = configuration.getVisibleMemberTable(te); 847 for (Element e : vmt.getMembers(VisibleMemberTable.Kind.METHODS)) { 848 ExecutableElement ee = (ExecutableElement)e; 849 if (configuration.workArounds.overrides(method, ee, origin) && 850 !isSimpleOverride(ee)) { 851 return ee; 852 } 853 } 854 if (t.equals(getObjectType())) 855 return null; 856 } 857 return null; 858 } 859 860 public SortedSet<TypeElement> getTypeElementsAsSortedSet(Iterable<TypeElement> typeElements) { 861 SortedSet<TypeElement> set = new TreeSet<>(comparators.makeGeneralPurposeComparator()); 862 typeElements.forEach(set::add); 863 return set; 864 } 865 866 public List<? extends SerialDataTree> getSerialDataTrees(ExecutableElement member) { 867 return getBlockTags(member, SERIAL_DATA, SerialDataTree.class); 868 } 869 870 public FileObject getFileObject(TypeElement te) { 871 return docTrees.getPath(te).getCompilationUnit().getSourceFile(); 872 } 873 874 public TypeMirror getDeclaredType(TypeElement enclosing, TypeMirror target) { 875 return getDeclaredType(Collections.emptyList(), enclosing, target); 876 } 877 878 /** 879 * Finds the declaration of the enclosing's type parameter. 880 * 881 * @param values 882 * @param enclosing a TypeElement whose type arguments we desire 883 * @param target the TypeMirror of the type as described by the enclosing 884 * @return 885 */ 886 public TypeMirror getDeclaredType(Collection<TypeMirror> values, 887 TypeElement enclosing, TypeMirror target) { 888 TypeElement targetElement = asTypeElement(target); 889 List<? extends TypeParameterElement> targetTypeArgs = targetElement.getTypeParameters(); 890 if (targetTypeArgs.isEmpty()) { 891 return target; 892 } 893 894 List<? extends TypeParameterElement> enclosingTypeArgs = enclosing.getTypeParameters(); 895 List<TypeMirror> targetTypeArgTypes = new ArrayList<>(targetTypeArgs.size()); 896 897 if (enclosingTypeArgs.isEmpty()) { 898 for (TypeMirror te : values) { 899 List<? extends TypeMirror> typeArguments = ((DeclaredType)te).getTypeArguments(); 900 if (typeArguments.size() >= targetTypeArgs.size()) { 901 for (int i = 0 ; i < targetTypeArgs.size(); i++) { 902 targetTypeArgTypes.add(typeArguments.get(i)); 903 } 904 break; 905 } 906 } 907 // we found no matches in the hierarchy 908 if (targetTypeArgTypes.isEmpty()) { 909 return target; 910 } 911 } else { 912 if (targetTypeArgs.size() > enclosingTypeArgs.size()) { 913 return target; 914 } 915 for (int i = 0; i < targetTypeArgs.size(); i++) { 916 TypeParameterElement tpe = enclosingTypeArgs.get(i); 917 targetTypeArgTypes.add(tpe.asType()); 918 } 919 } 920 TypeMirror dt = typeUtils.getDeclaredType(targetElement, 921 targetTypeArgTypes.toArray(new TypeMirror[targetTypeArgTypes.size()])); 922 return dt; 923 } 924 925 /** 926 * Returns all the implemented super-interfaces of a given type, 927 * in the case of classes, include all the super-interfaces of 928 * the supertype. The super-interfaces are collected before the 929 * super-interfaces of the supertype. 930 * 931 * @param te the type element to get the super-interfaces for. 932 * @return the list of super-interfaces. 933 */ 934 public Set<TypeMirror> getAllInterfaces(TypeElement te) { 935 Set<TypeMirror> results = new LinkedHashSet<>(); 936 getAllInterfaces(te.asType(), results); 937 return results; 938 } 939 940 private void getAllInterfaces(TypeMirror type, Set<TypeMirror> results) { 941 List<? extends TypeMirror> intfacs = typeUtils.directSupertypes(type); 942 TypeMirror superType = null; 943 for (TypeMirror intfac : intfacs) { 944 if (intfac == getObjectType()) 945 continue; 946 TypeElement e = asTypeElement(intfac); 947 if (isInterface(e)) { 948 if (isPublic(e) || isLinkable(e)) 949 results.add(intfac); 950 951 getAllInterfaces(intfac, results); 952 } else { 953 // Save the supertype for later. 954 superType = intfac; 955 } 956 } 957 // Collect the super-interfaces of the supertype. 958 if (superType != null) 959 getAllInterfaces(superType, results); 960 } 961 962 /** 963 * Lookup for a class within this package. 964 * 965 * @return TypeElement of found class, or null if not found. 966 */ 967 public TypeElement findClassInPackageElement(PackageElement pkg, String className) { 968 for (TypeElement c : getAllClasses(pkg)) { 969 if (getSimpleName(c).equals(className)) { 970 return c; 971 } 972 } 973 return null; 974 } 975 976 /** 977 * Returns true if {@code type} or any of its enclosing types has non-empty type arguments. 978 * @param type the type 979 * @return {@code true} if type arguments were found 980 */ 981 public boolean isGenericType(TypeMirror type) { 982 while (type instanceof DeclaredType dt) { 983 if (!dt.getTypeArguments().isEmpty()) { 984 return true; 985 } 986 type = dt.getEnclosingType(); 987 } 988 return false; 989 } 990 991 /** 992 * TODO: FIXME: port to javax.lang.model 993 * Find a class within the context of this class. Search order: qualified name, in this class 994 * (inner), in this package, in the class imports, in the package imports. Return the 995 * TypeElement if found, null if not found. 996 */ 997 //### The specified search order is not the normal rule the 998 //### compiler would use. Leave as specified or change it? 999 public TypeElement findClass(Element element, String className) { 1000 TypeElement encl = getEnclosingTypeElement(element); 1001 TypeElement searchResult = configuration.workArounds.searchClass(encl, className); 1002 if (searchResult == null) { 1003 encl = getEnclosingTypeElement(encl); 1004 //Expand search space to include enclosing class. 1005 while (encl != null && getEnclosingTypeElement(encl) != null) { 1006 encl = getEnclosingTypeElement(encl); 1007 } 1008 searchResult = encl == null 1009 ? null 1010 : configuration.workArounds.searchClass(encl, className); 1011 } 1012 return searchResult; 1013 } 1014 1015 /** 1016 * Enclose in quotes, used for paths and filenames that contains spaces 1017 */ 1018 public String quote(String filepath) { 1019 return ("\"" + filepath + "\""); 1020 } 1021 1022 /** 1023 * Parse the package name. We only want to display package name up to 1024 * 2 levels. 1025 */ 1026 public String parsePackageName(PackageElement p) { 1027 String pkgname = p.isUnnamed() ? "" : getPackageName(p); 1028 int index = -1; 1029 for (int j = 0; j < MAX_CONSTANT_VALUE_INDEX_LENGTH; j++) { 1030 index = pkgname.indexOf(".", index + 1); 1031 } 1032 if (index != -1) { 1033 pkgname = pkgname.substring(0, index); 1034 } 1035 return pkgname; 1036 } 1037 1038 /** 1039 * Given an annotation, return true if it should be documented and false 1040 * otherwise. 1041 * 1042 * @param annotation the annotation to check. 1043 * 1044 * @return true return true if it should be documented and false otherwise. 1045 */ 1046 public boolean isDocumentedAnnotation(TypeElement annotation) { 1047 for (AnnotationMirror anno : annotation.getAnnotationMirrors()) { 1048 if (getFullyQualifiedName(anno.getAnnotationType().asElement()).equals( 1049 Documented.class.getName())) { 1050 return true; 1051 } 1052 } 1053 return false; 1054 } 1055 1056 /** 1057 * Returns true if this class is linkable and false if we can't link to it. 1058 * 1059 * <p> 1060 * <b>NOTE:</b> You can only link to external classes if they are public or 1061 * protected. 1062 * 1063 * @return true if this class is linkable and false if we can't link to the 1064 * desired class. 1065 */ 1066 public boolean isLinkable(TypeElement typeElem) { 1067 return 1068 typeElem != null && 1069 ((isIncluded(typeElem) && configuration.isGeneratedDoc(typeElem) && 1070 !hasHiddenTag(typeElem)) || 1071 (configuration.extern.isExternal(typeElem) && 1072 (isPublic(typeElem) || isProtected(typeElem)))); 1073 } 1074 1075 /** 1076 * Returns true if an element is linkable in the context of a given type element. 1077 * 1078 * If the element is a type element, it delegates to {@link #isLinkable(TypeElement)}. 1079 * Otherwise, the element is linkable if any of the following are true: 1080 * <ul> 1081 * <li>it is "included" (see {@link jdk.javadoc.doclet}) 1082 * <li>it is inherited from an undocumented supertype 1083 * <li>it is a public or protected member of an external API 1084 * </ul> 1085 * 1086 * @param typeElem the type element 1087 * @param elem the element 1088 * @return whether or not the element is linkable 1089 */ 1090 public boolean isLinkable(TypeElement typeElem, Element elem) { 1091 if (isTypeElement(elem)) { 1092 return isLinkable((TypeElement) elem); // defer to existing behavior 1093 } 1094 1095 if (isIncluded(elem) && !hasHiddenTag(elem)) { 1096 return true; 1097 } 1098 1099 // Allow for the behavior that members of undocumented supertypes 1100 // may be included in documented types 1101 if (isUndocumentedEnclosure(getEnclosingTypeElement(elem))) { 1102 return true; 1103 } 1104 1105 // Allow for external members 1106 return isLinkable(typeElem) 1107 && configuration.extern.isExternal(typeElem) 1108 && (isPublic(elem) || isProtected(elem)); 1109 } 1110 1111 /** 1112 * Return this type as a {@code TypeElement} if it represents a class 1113 * interface or annotation. Array dimensions are ignored. 1114 * If this type {@code ParameterizedType} or {@code WildcardType}, return 1115 * the {@code TypeElement} of the type's erasure. If this is an 1116 * annotation, return this as a {@code TypeElement}. 1117 * If this is a primitive type, return null. 1118 * 1119 * @return the {@code TypeElement} of this type, 1120 * or null if it is a primitive type. 1121 */ 1122 public TypeElement asTypeElement(TypeMirror t) { 1123 return new SimpleTypeVisitor14<TypeElement, Void>() { 1124 1125 @Override 1126 public TypeElement visitDeclared(DeclaredType t, Void p) { 1127 return (TypeElement) t.asElement(); 1128 } 1129 1130 @Override 1131 public TypeElement visitArray(ArrayType t, Void p) { 1132 return visit(t.getComponentType()); 1133 } 1134 1135 @Override 1136 public TypeElement visitTypeVariable(TypeVariable t, Void p) { 1137 /* TODO, this may not be an optimal fix. 1138 * if we have an annotated type @DA T, then erasure returns a 1139 * none, in this case we use asElement instead. 1140 */ 1141 if (isAnnotated(t)) { 1142 return visit(typeUtils.asElement(t).asType()); 1143 } 1144 return visit(typeUtils.erasure(t)); 1145 } 1146 1147 @Override 1148 public TypeElement visitWildcard(WildcardType t, Void p) { 1149 return visit(typeUtils.erasure(t)); 1150 } 1151 1152 @Override 1153 public TypeElement visitError(ErrorType t, Void p) { 1154 return (TypeElement)t.asElement(); 1155 } 1156 1157 @Override 1158 protected TypeElement defaultAction(TypeMirror e, Void p) { 1159 return super.defaultAction(e, p); 1160 } 1161 }.visit(t); 1162 } 1163 1164 public TypeMirror getComponentType(TypeMirror t) { 1165 while (isArrayType(t)) { 1166 t = ((ArrayType) t).getComponentType(); 1167 } 1168 return t; 1169 } 1170 1171 /** 1172 * Return the type's dimension information, as a string. 1173 * <p> 1174 * For example, a two dimensional array of String returns "{@code [][]}". 1175 * 1176 * @return the type's dimension information as a string. 1177 */ 1178 public String getDimension(TypeMirror t) { 1179 return new SimpleTypeVisitor14<String, Void>() { 1180 StringBuilder dimension = new StringBuilder(); 1181 @Override 1182 public String visitArray(ArrayType t, Void p) { 1183 dimension.append("[]"); 1184 return visit(t.getComponentType()); 1185 } 1186 1187 @Override 1188 protected String defaultAction(TypeMirror e, Void p) { 1189 return dimension.toString(); 1190 } 1191 1192 }.visit(t); 1193 } 1194 1195 public TypeElement getSuperClass(TypeElement te) { 1196 if (isInterface(te) || isAnnotationType(te) || 1197 te.asType().equals(getObjectType())) { 1198 return null; 1199 } 1200 TypeMirror superclass = te.getSuperclass(); 1201 if (isNoType(superclass) && isClass(te)) { 1202 superclass = getObjectType(); 1203 } 1204 return asTypeElement(superclass); 1205 } 1206 1207 public TypeElement getFirstVisibleSuperClassAsTypeElement(TypeElement te) { 1208 if (isAnnotationType(te) || isInterface(te) || 1209 te.asType().equals(getObjectType())) { 1210 return null; 1211 } 1212 TypeMirror firstVisibleSuperClass = getFirstVisibleSuperClass(te); 1213 return firstVisibleSuperClass == null ? null : asTypeElement(firstVisibleSuperClass); 1214 } 1215 1216 /** 1217 * Given a class, return the closest visible super class. 1218 * @param type the TypeMirror to be interrogated 1219 * @return the closest visible super class. Return null if it cannot 1220 * be found. 1221 */ 1222 1223 public TypeMirror getFirstVisibleSuperClass(TypeMirror type) { 1224 return getFirstVisibleSuperClass(asTypeElement(type)); 1225 } 1226 1227 1228 /** 1229 * Given a class, return the closest visible super class. 1230 * 1231 * @param te the TypeElement to be interrogated 1232 * @return the closest visible super class. Return null if it cannot 1233 * be found.. 1234 */ 1235 public TypeMirror getFirstVisibleSuperClass(TypeElement te) { 1236 TypeMirror superType = te.getSuperclass(); 1237 if (isNoType(superType)) { 1238 superType = getObjectType(); 1239 } 1240 TypeElement superClass = asTypeElement(superType); 1241 // skip "hidden" classes 1242 while ((superClass != null && hasHiddenTag(superClass)) 1243 || (superClass != null && !isPublic(superClass) && !isLinkable(superClass))) { 1244 TypeMirror supersuperType = superClass.getSuperclass(); 1245 TypeElement supersuperClass = asTypeElement(supersuperType); 1246 if (supersuperClass == null 1247 || supersuperClass.getQualifiedName().equals(superClass.getQualifiedName())) { 1248 break; 1249 } 1250 superType = supersuperType; 1251 superClass = supersuperClass; 1252 } 1253 if (te.asType().equals(superType)) { 1254 return null; 1255 } 1256 return superType; 1257 } 1258 1259 /** 1260 * Returns the name of the kind of a type element (Class, Interface, etc.). 1261 * 1262 * @param te the type element 1263 * @param lowerCaseOnly true if you want the name returned in lower case; 1264 * if false, the first letter of the name is capitalized 1265 * @return the name 1266 */ 1267 public String getTypeElementKindName(TypeElement te, boolean lowerCaseOnly) { 1268 String kindName = switch (te.getKind()) { 1269 case ANNOTATION_TYPE -> 1270 "doclet.AnnotationType"; 1271 case ENUM -> 1272 "doclet.Enum"; 1273 case INTERFACE -> 1274 "doclet.Interface"; 1275 case RECORD -> 1276 "doclet.RecordClass"; 1277 case CLASS -> 1278 isException(te) ? "doclet.Exception" 1279 : isError(te) ? "doclet.Error" 1280 : "doclet.Class"; 1281 default -> 1282 throw new IllegalArgumentException(te.getKind().toString()); 1283 }; 1284 kindName = lowerCaseOnly ? toLowerCase(kindName) : kindName; 1285 return kindNameMap.computeIfAbsent(kindName, resources::getText); 1286 } 1287 1288 private final Map<String, String> kindNameMap = new HashMap<>(); 1289 1290 public String getTypeName(TypeMirror t, boolean fullyQualified) { 1291 return new SimpleTypeVisitor14<String, Void>() { 1292 1293 @Override 1294 public String visitArray(ArrayType t, Void p) { 1295 return visit(t.getComponentType()); 1296 } 1297 1298 @Override 1299 public String visitDeclared(DeclaredType t, Void p) { 1300 TypeElement te = asTypeElement(t); 1301 return fullyQualified 1302 ? te.getQualifiedName().toString() 1303 : getSimpleName(te); 1304 } 1305 1306 @Override 1307 public String visitExecutable(ExecutableType t, Void p) { 1308 return t.toString(); 1309 } 1310 1311 @Override 1312 public String visitPrimitive(PrimitiveType t, Void p) { 1313 return t.toString(); 1314 } 1315 1316 @Override 1317 public String visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 1318 return getSimpleName(t.asElement()); 1319 } 1320 1321 @Override 1322 public String visitWildcard(javax.lang.model.type.WildcardType t, Void p) { 1323 return t.toString(); 1324 } 1325 1326 @Override 1327 protected String defaultAction(TypeMirror e, Void p) { 1328 return e.toString(); 1329 } 1330 }.visit(t); 1331 } 1332 1333 /** 1334 * Replace all tabs in a string with the appropriate number of spaces. 1335 * The string may be a multi-line string. 1336 * @param text the text for which the tabs should be expanded 1337 * @return the text with all tabs expanded 1338 */ 1339 public String replaceTabs(String text) { 1340 if (!text.contains("\t")) 1341 return text; 1342 1343 final int tabLength = options.sourceTabSize(); 1344 final String whitespace = " ".repeat(tabLength); 1345 final int textLength = text.length(); 1346 StringBuilder result = new StringBuilder(textLength); 1347 int pos = 0; 1348 int lineLength = 0; 1349 for (int i = 0; i < textLength; i++) { 1350 char ch = text.charAt(i); 1351 switch (ch) { 1352 case '\n': case '\r': 1353 lineLength = 0; 1354 break; 1355 case '\t': 1356 result.append(text, pos, i); 1357 int spaceCount = tabLength - lineLength % tabLength; 1358 result.append(whitespace, 0, spaceCount); 1359 lineLength += spaceCount; 1360 pos = i + 1; 1361 break; 1362 default: 1363 lineLength++; 1364 } 1365 } 1366 result.append(text, pos, textLength); 1367 return result.toString(); 1368 } 1369 1370 public CharSequence normalizeNewlines(CharSequence text) { 1371 StringBuilder sb = new StringBuilder(); 1372 final int textLength = text.length(); 1373 final String NL = DocletConstants.NL; 1374 int pos = 0; 1375 for (int i = 0; i < textLength; i++) { 1376 char ch = text.charAt(i); 1377 switch (ch) { 1378 case '\n': 1379 sb.append(text, pos, i); 1380 sb.append(NL); 1381 pos = i + 1; 1382 break; 1383 case '\r': 1384 sb.append(text, pos, i); 1385 sb.append(NL); 1386 if (i + 1 < textLength && text.charAt(i + 1) == '\n') 1387 i++; 1388 pos = i + 1; 1389 break; 1390 } 1391 } 1392 sb.append(text, pos, textLength); 1393 return sb; 1394 } 1395 1396 /** 1397 * Returns a locale independent lower cased String. That is, it 1398 * always uses US locale, this is a clone of the one in StringUtils. 1399 * @param s to convert 1400 * @return converted String 1401 */ 1402 public static String toLowerCase(String s) { 1403 return s.toLowerCase(Locale.US); 1404 } 1405 1406 /** 1407 * Return true if the given Element is deprecated. 1408 * 1409 * @param e the Element to check. 1410 * @return true if the given Element is deprecated. 1411 */ 1412 public boolean isDeprecated(Element e) { 1413 if (isPackage(e)) { 1414 return configuration.workArounds.isDeprecated0(e); 1415 } 1416 return elementUtils.isDeprecated(e); 1417 } 1418 1419 /** 1420 * Returns true if the given Element is deprecated for removal. 1421 * 1422 * @param e the Element to check. 1423 * @return true if the given Element is deprecated for removal. 1424 */ 1425 public boolean isDeprecatedForRemoval(Element e) { 1426 Object forRemoval = getDeprecatedElement(e, "forRemoval"); 1427 return forRemoval != null && (boolean) forRemoval; 1428 } 1429 1430 /** 1431 * Returns the value of the {@code Deprecated.since} element if it is set on the given Element. 1432 * 1433 * @param e the Element to check. 1434 * @return the Deprecated.since value for e, or null. 1435 */ 1436 public String getDeprecatedSince(Element e) { 1437 return (String) getDeprecatedElement(e, "since"); 1438 } 1439 1440 /** 1441 * Returns the Deprecated annotation element value of the given element, or null. 1442 */ 1443 private Object getDeprecatedElement(Element e, String elementName) { 1444 List<? extends AnnotationMirror> annotationList = e.getAnnotationMirrors(); 1445 JavacTypes jctypes = ((DocEnvImpl) configuration.docEnv).toolEnv.typeutils; 1446 for (AnnotationMirror anno : annotationList) { 1447 if (jctypes.isSameType(anno.getAnnotationType().asElement().asType(), getDeprecatedType())) { 1448 Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = anno.getElementValues(); 1449 if (!pairs.isEmpty()) { 1450 for (ExecutableElement element : pairs.keySet()) { 1451 if (element.getSimpleName().contentEquals(elementName)) { 1452 return (pairs.get(element)).getValue(); 1453 } 1454 } 1455 } 1456 } 1457 } 1458 return null; 1459 } 1460 1461 /** 1462 * A convenience method to get property name from the name of the 1463 * getter or setter method. 1464 * @param e the input method. 1465 * @return the name of the property of the given setter of getter. 1466 */ 1467 public String propertyName(ExecutableElement e) { 1468 String name = getSimpleName(e); 1469 String propertyName = null; 1470 if (name.startsWith("get") || name.startsWith("set")) { 1471 propertyName = name.substring(3); 1472 } else if (name.startsWith("is")) { 1473 propertyName = name.substring(2); 1474 } 1475 if ((propertyName == null) || propertyName.isEmpty()){ 1476 return ""; 1477 } 1478 return propertyName.substring(0, 1).toLowerCase(configuration.getLocale()) 1479 + propertyName.substring(1); 1480 } 1481 1482 /** 1483 * Returns true if the element is included or selected, contains @hidden tag, 1484 * or if javafx flag is present and element contains @treatAsPrivate 1485 * tag. 1486 * @param e the queried element 1487 * @return true if it exists, false otherwise 1488 */ 1489 public boolean hasHiddenTag(Element e) { 1490 // Non-included elements may still be visible via "transclusion" from undocumented enclosures, 1491 // but we don't want to run doclint on them, possibly causing warnings or errors. 1492 if (!isIncluded(e)) { 1493 return hasBlockTagUnchecked(e, HIDDEN); 1494 } 1495 if (options.javafx() && 1496 hasBlockTag(e, DocTree.Kind.UNKNOWN_BLOCK_TAG, "treatAsPrivate")) { 1497 return true; 1498 } 1499 return hasBlockTag(e, DocTree.Kind.HIDDEN); 1500 } 1501 1502 /** 1503 * Returns true if the method has no comments, or a lone @inheritDoc. 1504 * @param m a method 1505 * @return true if there are no comments, false otherwise 1506 */ 1507 public boolean isSimpleOverride(ExecutableElement m) { 1508 if (!options.summarizeOverriddenMethods() || !isIncluded(m)) { 1509 return false; 1510 } 1511 1512 if (!getBlockTags(m).isEmpty() || isDeprecated(m)) 1513 return false; 1514 1515 List<? extends DocTree> fullBody = getFullBody(m); 1516 return fullBody.isEmpty() || 1517 (fullBody.size() == 1 && fullBody.get(0).getKind().equals(Kind.INHERIT_DOC)); 1518 } 1519 1520 /** 1521 * In case of JavaFX mode on, filters out classes that are private, 1522 * package private, these are not documented in JavaFX mode, also 1523 * remove those classes that have @hidden or @treatAsPrivate comment tag. 1524 * 1525 * @param classlist a collection of TypeElements 1526 * @param javafx set to true if in JavaFX mode. 1527 * @return list of filtered classes. 1528 */ 1529 public SortedSet<TypeElement> filterOutPrivateClasses(Iterable<TypeElement> classlist, 1530 boolean javafx) { 1531 SortedSet<TypeElement> filteredOutClasses = 1532 new TreeSet<>(comparators.makeGeneralPurposeComparator()); 1533 if (!javafx) { 1534 for (TypeElement te : classlist) { 1535 if (!hasHiddenTag(te)) { 1536 filteredOutClasses.add(te); 1537 } 1538 } 1539 return filteredOutClasses; 1540 } 1541 for (TypeElement e : classlist) { 1542 if (isPrivate(e) || isPackagePrivate(e) || hasHiddenTag(e)) { 1543 continue; 1544 } 1545 filteredOutClasses.add(e); 1546 } 1547 return filteredOutClasses; 1548 } 1549 1550 /** 1551 * Compares two elements. 1552 * @param e1 first Element 1553 * @param e2 second Element 1554 * @return a true if they are the same, false otherwise. 1555 */ 1556 public boolean elementsEqual(Element e1, Element e2) { 1557 if (e1.getKind() != e2.getKind()) { 1558 return false; 1559 } 1560 String s1 = getSimpleName(e1); 1561 String s2 = getSimpleName(e2); 1562 if (compareStrings(s1, s2) == 0) { 1563 String f1 = getFullyQualifiedName(e1, true); 1564 String f2 = getFullyQualifiedName(e2, true); 1565 return compareStrings(f1, f2) == 0; 1566 } 1567 return false; 1568 } 1569 1570 /** 1571 * A general purpose case insensitive String comparator, which compares 1572 * two Strings using a Collator strength of "TERTIARY". 1573 * 1574 * @param s1 first String to compare. 1575 * @param s2 second String to compare. 1576 * @return a negative integer, zero, or a positive integer as the first 1577 * argument is less than, equal to, or greater than the second. 1578 */ 1579 public int compareStrings(String s1, String s2) { 1580 return compareStrings(true, s1, s2); 1581 } 1582 1583 /** 1584 * A general purpose case sensitive String comparator, which 1585 * compares two Strings using a Collator strength of "SECONDARY". 1586 * 1587 * @param s1 first String to compare. 1588 * @param s2 second String to compare. 1589 * @return a negative integer, zero, or a positive integer as the first 1590 * argument is less than, equal to, or greater than the second. 1591 */ 1592 public int compareCaseCompare(String s1, String s2) { 1593 return compareStrings(false, s1, s2); 1594 } 1595 1596 private DocCollator tertiaryCollator = null; 1597 private DocCollator secondaryCollator = null; 1598 1599 int compareStrings(boolean caseSensitive, String s1, String s2) { 1600 if (caseSensitive) { 1601 if (tertiaryCollator == null) { 1602 tertiaryCollator = new DocCollator(configuration.locale, Collator.TERTIARY); 1603 } 1604 return tertiaryCollator.compare(s1, s2); 1605 } 1606 if (secondaryCollator == null) { 1607 secondaryCollator = new DocCollator(configuration.locale, Collator.SECONDARY); 1608 } 1609 return secondaryCollator.compare(s1, s2); 1610 } 1611 1612 public String getHTMLTitle(Element element) { 1613 List<? extends DocTree> preamble = getPreamble(element); 1614 StringBuilder sb = new StringBuilder(); 1615 boolean titleFound = false; 1616 loop: 1617 for (DocTree dt : preamble) { 1618 switch (dt.getKind()) { 1619 case START_ELEMENT: 1620 StartElementTree nodeStart = (StartElementTree)dt; 1621 if (Utils.toLowerCase(nodeStart.getName().toString()).equals("title")) { 1622 titleFound = true; 1623 } 1624 break; 1625 1626 case END_ELEMENT: 1627 EndElementTree nodeEnd = (EndElementTree)dt; 1628 if (Utils.toLowerCase(nodeEnd.getName().toString()).equals("title")) { 1629 break loop; 1630 } 1631 break; 1632 1633 case TEXT: 1634 TextTree nodeText = (TextTree)dt; 1635 if (titleFound) 1636 sb.append(nodeText.getBody()); 1637 break; 1638 1639 default: 1640 // do nothing 1641 } 1642 } 1643 return sb.toString().trim(); 1644 } 1645 1646 private static class DocCollator { 1647 private final Map<String, CollationKey> keys; 1648 private final Collator instance; 1649 private final int MAX_SIZE = 1000; 1650 private DocCollator(Locale locale, int strength) { 1651 instance = createCollator(locale); 1652 instance.setStrength(strength); 1653 1654 keys = new LinkedHashMap<String, CollationKey>(MAX_SIZE + 1, 0.75f, true) { 1655 private static final long serialVersionUID = 1L; 1656 @Override 1657 protected boolean removeEldestEntry(Entry<String, CollationKey> eldest) { 1658 return size() > MAX_SIZE; 1659 } 1660 }; 1661 } 1662 1663 CollationKey getKey(String s) { 1664 return keys.computeIfAbsent(s, instance :: getCollationKey); 1665 } 1666 1667 public int compare(String s1, String s2) { 1668 return getKey(s1).compareTo(getKey(s2)); 1669 } 1670 1671 private Collator createCollator(Locale locale) { 1672 Collator baseCollator = Collator.getInstance(locale); 1673 if (baseCollator instanceof RuleBasedCollator rbc) { 1674 // Extend collator to sort signatures with additional args and var-args in a well-defined order: 1675 // () < (int) < (int, int) < (int...) 1676 try { 1677 return new RuleBasedCollator(rbc.getRules() 1678 + "& ')' < ',' < '.','['"); 1679 } catch (ParseException e) { 1680 throw new RuntimeException(e); 1681 } 1682 } 1683 return baseCollator; 1684 } 1685 } 1686 1687 /** 1688 * Get the qualified type name of a TypeMirror compatible with the Element's 1689 * getQualified name, returns the qualified name of the Reference type 1690 * otherwise the primitive name. 1691 * @param t the type whose name is to be obtained. 1692 * @return the fully qualified name of Reference type or the primitive name 1693 */ 1694 public String getQualifiedTypeName(TypeMirror t) { 1695 return new SimpleTypeVisitor14<String, Void>() { 1696 @Override 1697 public String visitDeclared(DeclaredType t, Void p) { 1698 return getFullyQualifiedName(t.asElement()); 1699 } 1700 1701 @Override 1702 public String visitArray(ArrayType t, Void p) { 1703 return visit(t.getComponentType()); 1704 } 1705 1706 @Override 1707 public String visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 1708 // The knee jerk reaction is to do this but don't!, as we would like 1709 // it to be compatible with the old world, now if we decide to do so 1710 // care must be taken to avoid collisions. 1711 // return getFullyQualifiedName(t.asElement()); 1712 return t.toString(); 1713 } 1714 1715 @Override 1716 protected String defaultAction(TypeMirror t, Void p) { 1717 return t.toString(); 1718 } 1719 1720 }.visit(t); 1721 } 1722 1723 /** 1724 * A generic utility which returns the fully qualified names of an entity, 1725 * if the entity is not qualifiable then its enclosing entity, it is up to 1726 * the caller to add the elements name as required. 1727 * @param e the element to get FQN for. 1728 * @return the name 1729 */ 1730 public String getFullyQualifiedName(Element e) { 1731 return getFullyQualifiedName(e, true); 1732 } 1733 1734 public String getFullyQualifiedName(Element e, final boolean outer) { 1735 return new SimpleElementVisitor14<String, Void>() { 1736 @Override 1737 public String visitModule(ModuleElement e, Void p) { 1738 return e.getQualifiedName().toString(); 1739 } 1740 1741 @Override 1742 public String visitPackage(PackageElement e, Void p) { 1743 return e.getQualifiedName().toString(); 1744 } 1745 1746 @Override 1747 public String visitType(TypeElement e, Void p) { 1748 return e.getQualifiedName().toString(); 1749 } 1750 1751 @Override 1752 protected String defaultAction(Element e, Void p) { 1753 return outer ? visit(e.getEnclosingElement()) : e.getSimpleName().toString(); 1754 } 1755 }.visit(e); 1756 } 1757 1758 1759 /** 1760 * Returns the recursively enclosed documented type elements in a package 1761 * 1762 * @param pkg the package 1763 * @return the elements 1764 */ 1765 public Iterable<TypeElement> getEnclosedTypeElements(PackageElement pkg) { 1766 return getItems(pkg, false, this::isTypeElement, TypeElement.class); 1767 } 1768 1769 // Element related methods 1770 1771 /** 1772 * Returns the fields and methods declared in an annotation interface. 1773 * 1774 * @param te the annotation interface 1775 * @return the fields and methods 1776 */ 1777 public List<Element> getAnnotationMembers(TypeElement te) { 1778 return getItems(te, false, e_ -> 1779 switch (e_.getKind()) { 1780 case FIELD, METHOD -> shouldDocument(e_); 1781 default -> false; 1782 }, 1783 Element.class); 1784 1785 } 1786 1787 /** 1788 * Returns the documented annotation interfaces in a package. 1789 * 1790 * @param pkg the package 1791 * @return the annotation interfaces 1792 */ 1793 public List<TypeElement> getAnnotationTypes(PackageElement pkg) { 1794 return getDocumentedItems(pkg, ANNOTATION_TYPE, TypeElement.class); 1795 } 1796 1797 /** 1798 * Returns the documented record classes in a package. 1799 * 1800 * @param pkg the package 1801 * @return the record classes 1802 */ 1803 public List<TypeElement> getRecords(PackageElement pkg) { 1804 return getDocumentedItems(pkg, RECORD, TypeElement.class); 1805 } 1806 1807 /** 1808 * Returns the documented fields in a type element. 1809 * 1810 * @param te the element 1811 * @return the fields 1812 */ 1813 public List<VariableElement> getFields(TypeElement te) { 1814 return getDocumentedItems(te, FIELD, VariableElement.class); 1815 } 1816 1817 /** 1818 * Returns the fields in a type element. 1819 * 1820 * @param te the element 1821 * @return the fields 1822 */ 1823 public List<VariableElement> getFieldsUnfiltered(TypeElement te) { 1824 return getAllItems(te, FIELD, VariableElement.class); 1825 } 1826 1827 /** 1828 * Returns the documented classes in an element, 1829 * such as a package element or type element. 1830 * 1831 * @param e the element 1832 * @return the classes 1833 */ 1834 public List<TypeElement> getClasses(Element e) { 1835 return getDocumentedItems(e, CLASS, TypeElement.class); 1836 } 1837 1838 /** 1839 * Returns the documented constructors in a type element. 1840 * 1841 * @param te the type element 1842 * @return the constructors 1843 */ 1844 public List<ExecutableElement> getConstructors(TypeElement te) { 1845 return getDocumentedItems(te, CONSTRUCTOR, ExecutableElement.class); 1846 } 1847 1848 1849 /** 1850 * Returns the documented methods in a type element. 1851 * 1852 * @param te the type element 1853 * @return the methods 1854 */ 1855 public List<ExecutableElement> getMethods(TypeElement te) { 1856 return getDocumentedItems(te, METHOD, ExecutableElement.class); 1857 } 1858 1859 public int getOrdinalValue(VariableElement member) { 1860 if (member == null || member.getKind() != ENUM_CONSTANT) { 1861 throw new IllegalArgumentException("must be an enum constant: " + member); 1862 } 1863 return member.getEnclosingElement().getEnclosedElements().indexOf(member); 1864 } 1865 1866 private Map<ModuleElement, Set<PackageElement>> modulePackageMap = null; 1867 public Map<ModuleElement, Set<PackageElement>> getModulePackageMap() { 1868 if (modulePackageMap == null) { 1869 modulePackageMap = new HashMap<>(); 1870 Set<PackageElement> pkgs = configuration.getIncludedPackageElements(); 1871 pkgs.forEach(pkg -> { 1872 ModuleElement mod = elementUtils.getModuleOf(pkg); 1873 modulePackageMap.computeIfAbsent(mod, m -> new HashSet<>()).add(pkg); 1874 }); 1875 } 1876 return modulePackageMap; 1877 } 1878 1879 public Map<ModuleElement, String> getDependentModules(ModuleElement mdle) { 1880 Map<ModuleElement, String> result = new TreeMap<>(comparators.makeModuleComparator()); 1881 Deque<ModuleElement> queue = new ArrayDeque<>(); 1882 // get all the requires for the element in question 1883 for (RequiresDirective rd : ElementFilter.requiresIn(mdle.getDirectives())) { 1884 ModuleElement dep = rd.getDependency(); 1885 // add the dependency to work queue 1886 if (!result.containsKey(dep)) { 1887 if (rd.isTransitive()) { 1888 queue.addLast(dep); 1889 } 1890 } 1891 // add all exports for the primary module 1892 result.put(rd.getDependency(), getModifiers(rd)); 1893 } 1894 1895 // add only requires public for subsequent module dependencies 1896 for (ModuleElement m = queue.poll(); m != null; m = queue.poll()) { 1897 for (RequiresDirective rd : ElementFilter.requiresIn(m.getDirectives())) { 1898 ModuleElement dep = rd.getDependency(); 1899 if (!result.containsKey(dep)) { 1900 if (rd.isTransitive()) { 1901 result.put(dep, getModifiers(rd)); 1902 queue.addLast(dep); 1903 } 1904 } 1905 } 1906 } 1907 return result; 1908 } 1909 1910 public String getModifiers(RequiresDirective rd) { 1911 StringBuilder modifiers = new StringBuilder(); 1912 String sep = ""; 1913 if (rd.isTransitive()) { 1914 modifiers.append("transitive"); 1915 sep = " "; 1916 } 1917 if (rd.isStatic()) { 1918 modifiers.append(sep); 1919 modifiers.append("static"); 1920 } 1921 return (modifiers.length() == 0) ? " " : modifiers.toString(); 1922 } 1923 1924 public long getLineNumber(Element e) { 1925 TreePath path = getTreePath(e); 1926 if (path == null) { // maybe null if synthesized 1927 TypeElement encl = getEnclosingTypeElement(e); 1928 path = getTreePath(encl); 1929 } 1930 CompilationUnitTree cu = path.getCompilationUnit(); 1931 LineMap lineMap = cu.getLineMap(); 1932 DocSourcePositions spos = docTrees.getSourcePositions(); 1933 long pos = spos.getStartPosition(cu, path.getLeaf()); 1934 return lineMap.getLineNumber(pos); 1935 } 1936 1937 /** 1938 * Returns the documented interfaces in a package. 1939 * 1940 * @param pkg the package 1941 * @return the interfaces 1942 */ 1943 public List<TypeElement> getInterfaces(PackageElement pkg) { 1944 return getDocumentedItems(pkg, INTERFACE, TypeElement.class); 1945 } 1946 1947 /** 1948 * Returns the documented enum constants in a type element. 1949 * 1950 * @param te the element 1951 * @return the interfaces 1952 */ 1953 public List<VariableElement> getEnumConstants(TypeElement te) { 1954 return getDocumentedItems(te, ENUM_CONSTANT, VariableElement.class); 1955 } 1956 1957 /** 1958 * Returns the documented enum classes in a package. 1959 * 1960 * @param pkg the package 1961 * @return the interfaces 1962 */ 1963 public List<TypeElement> getEnums(PackageElement pkg) { 1964 return getDocumentedItems(pkg, ENUM, TypeElement.class); 1965 } 1966 1967 /** 1968 * Returns all the classes in a package. 1969 * 1970 * @param pkg the package 1971 * @return the interfaces 1972 */ 1973 public SortedSet<TypeElement> getAllClassesUnfiltered(PackageElement pkg) { 1974 SortedSet<TypeElement> set = new TreeSet<>(comparators.makeGeneralPurposeComparator()); 1975 set.addAll(getItems(pkg, true, this::isTypeElement, TypeElement.class)); 1976 return set; 1977 } 1978 1979 private final HashMap<Element, SortedSet<TypeElement>> cachedClasses = new HashMap<>(); 1980 1981 /** 1982 * Returns a sorted set containing the documented classes and interfaces in a package. 1983 * 1984 * @param pkg the element 1985 * @return the classes and interfaces 1986 */ 1987 public SortedSet<TypeElement> getAllClasses(PackageElement pkg) { 1988 return cachedClasses.computeIfAbsent(pkg, p_ -> { 1989 List<TypeElement> clist = getItems(pkg, false, this::isTypeElement, TypeElement.class); 1990 SortedSet<TypeElement>oset = new TreeSet<>(comparators.makeGeneralPurposeComparator()); 1991 oset.addAll(clist); 1992 return oset; 1993 }); 1994 } 1995 1996 /** 1997 * Returns a list of classes that are not errors or exceptions 1998 * @param e Element 1999 * @return List 2000 */ 2001 public List<TypeElement> getOrdinaryClasses(Element e) { 2002 return getClasses(e).stream() 2003 .filter(te -> (!isException(te) && !isError(te))) 2004 .toList(); 2005 } 2006 2007 public List<TypeElement> getErrors(Element e) { 2008 return getClasses(e) 2009 .stream() 2010 .filter(this::isError) 2011 .toList(); 2012 } 2013 2014 public List<TypeElement> getExceptions(Element e) { 2015 return getClasses(e) 2016 .stream() 2017 .filter(this::isException) 2018 .toList(); 2019 } 2020 2021 /** 2022 * Returns a list of documented elements of a given type with a given kind. 2023 * If the root of the search is a package, the search is recursive. 2024 * 2025 * @param e the element, such as a package element or type element 2026 * @param kind the element kind 2027 * @param clazz the class of the filtered members 2028 * @param <T> the class of the filtered members 2029 * 2030 * @return the list of enclosed elements 2031 */ 2032 private <T extends Element> List<T> getDocumentedItems(Element e, ElementKind kind, Class<T> clazz) { 2033 return getItems(e, false, e_ -> e_.getKind() == kind && shouldDocument(e_), clazz); 2034 } 2035 2036 /** 2037 * Returns a list of elements of a given type with a given kind. 2038 * If the root of the search is a package, the search is recursive. 2039 * 2040 * @param e the element, such as a package element or type element 2041 * @param kind the element kind 2042 * @param clazz the class of the filtered members 2043 * @param <T> the class of the filtered members 2044 * 2045 * @return the list of enclosed elements 2046 */ 2047 private <T extends Element> List<T> getAllItems(Element e, ElementKind kind, Class<T> clazz) { 2048 return getItems(e, true, e_ -> e_.getKind() == kind, clazz); 2049 } 2050 2051 /** 2052 * Returns a list of elements of a given type that match a predicate. 2053 * If the root of the search is a package, the search is recursive through packages 2054 * and classes. 2055 * 2056 * @param e the element, such as a package element or type element 2057 * @param all whether to search through all packages and classes, or just documented ones 2058 * @param select the predicate to select members 2059 * @param clazz the class of the filtered members 2060 * @param <T> the class of the filtered members 2061 * 2062 * @return the list of enclosed elements 2063 */ 2064 private <T extends Element> List<T> getItems(Element e, boolean all, Predicate<Element> select, Class<T> clazz) { 2065 if (e.getKind() == ElementKind.PACKAGE) { 2066 List<T> elements = new ArrayList<>(); 2067 recursiveGetItems(elements, e, all, select, clazz); 2068 return elements; 2069 } else { 2070 return getItems0(e, all, select, clazz); 2071 } 2072 } 2073 2074 /** 2075 * Searches for a list of recursively enclosed elements of a given class that match a predicate. 2076 * The recursion is through nested types. 2077 * 2078 * @param e the element, such as a package element or type element 2079 * @param all whether to search all packages and classes, or just documented ones 2080 * @param filter the filter 2081 * @param clazz the class of the filtered members 2082 * @param <T> the class of the filtered members 2083 */ 2084 private <T extends Element> void recursiveGetItems(Collection<T> list, Element e, boolean all, Predicate<Element> filter, Class<T> clazz) { 2085 list.addAll(getItems0(e, all, filter, clazz)); 2086 List<TypeElement> classes = getItems0(e, all, this::isTypeElement, TypeElement.class); 2087 for (TypeElement c : classes) { 2088 recursiveGetItems(list, c, all, filter, clazz); 2089 } 2090 } 2091 2092 /** 2093 * Returns a list of immediately enclosed elements of a given class that match a predicate. 2094 * 2095 * @param e the element, such as a package element or type element 2096 * @param all whether to search all packages and classes, or just documented ones 2097 * @param select the predicate for the selected members 2098 * @param clazz the class of the filtered members 2099 * @param <T> the class of the filtered members 2100 * 2101 * @return the list of enclosed elements 2102 */ 2103 private <T extends Element> List<T> getItems0(Element e, boolean all, Predicate<Element> select, Class<T> clazz) { 2104 return e.getEnclosedElements().stream() 2105 .filter(e_ -> select.test(e_) && (all || shouldDocument(e_))) 2106 .map(clazz::cast) 2107 .toList(); 2108 } 2109 2110 private SimpleElementVisitor14<Boolean, Void> shouldDocumentVisitor = null; 2111 2112 public boolean shouldDocument(Element e) { 2113 if (shouldDocumentVisitor == null) { 2114 shouldDocumentVisitor = new SimpleElementVisitor14<Boolean, Void>() { 2115 private boolean hasSource(TypeElement e) { 2116 return configuration.docEnv.getFileKind(e) == 2117 javax.tools.JavaFileObject.Kind.SOURCE; 2118 } 2119 2120 // handle types 2121 @Override 2122 public Boolean visitType(TypeElement e, Void p) { 2123 // treat inner classes etc as members 2124 if (e.getNestingKind().isNested()) { 2125 return defaultAction(e, p); 2126 } 2127 return configuration.docEnv.isSelected(e) && hasSource(e); 2128 } 2129 2130 // handle everything else 2131 @Override 2132 protected Boolean defaultAction(Element e, Void p) { 2133 return configuration.docEnv.isSelected(e); 2134 } 2135 2136 @Override 2137 public Boolean visitUnknown(Element e, Void p) { 2138 throw new AssertionError("unknown element: " + e); 2139 } 2140 }; 2141 } 2142 return shouldDocumentVisitor.visit(e); 2143 } 2144 2145 /* 2146 * nameCache is maintained for improving the comparator 2147 * performance, noting that the Collator used by the comparators 2148 * use Strings, as of this writing. 2149 * TODO: when those APIs handle charSequences, the use of 2150 * this nameCache must be re-investigated and removed. 2151 */ 2152 private final Map<Element, String> nameCache = new LinkedHashMap<>(); 2153 2154 /** 2155 * Returns the name of the element after the last dot of the package name. 2156 * This emulates the behavior of the old doclet. 2157 * @param e an element whose name is required 2158 * @return the name 2159 */ 2160 public String getSimpleName(Element e) { 2161 return nameCache.computeIfAbsent(e, this::getSimpleName0); 2162 } 2163 2164 private SimpleElementVisitor14<String, Void> snvisitor = null; 2165 2166 private String getSimpleName0(Element e) { 2167 if (snvisitor == null) { 2168 snvisitor = new SimpleElementVisitor14<>() { 2169 @Override 2170 public String visitModule(ModuleElement e, Void p) { 2171 return e.getQualifiedName().toString(); // temp fix for 8182736 2172 } 2173 2174 @Override 2175 public String visitType(TypeElement e, Void p) { 2176 StringBuilder sb = new StringBuilder(e.getSimpleName().toString()); 2177 Element enclosed = e.getEnclosingElement(); 2178 while (enclosed != null 2179 && (enclosed.getKind().isClass() || enclosed.getKind().isInterface())) { 2180 sb.insert(0, enclosed.getSimpleName() + "."); 2181 enclosed = enclosed.getEnclosingElement(); 2182 } 2183 return sb.toString(); 2184 } 2185 2186 @Override 2187 public String visitExecutable(ExecutableElement e, Void p) { 2188 if (e.getKind() == CONSTRUCTOR || e.getKind() == STATIC_INIT) { 2189 return e.getEnclosingElement().getSimpleName().toString(); 2190 } 2191 return e.getSimpleName().toString(); 2192 } 2193 2194 @Override 2195 protected String defaultAction(Element e, Void p) { 2196 return e.getSimpleName().toString(); 2197 } 2198 }; 2199 } 2200 return snvisitor.visit(e); 2201 } 2202 2203 public TypeElement getEnclosingTypeElement(Element e) { 2204 if (isPackage(e) || isModule(e)) { 2205 return null; 2206 } 2207 Element encl = e.getEnclosingElement(); 2208 if (isPackage(encl)) { 2209 return null; 2210 } 2211 ElementKind kind = encl.getKind(); 2212 while (!(kind.isClass() || kind.isInterface())) { 2213 encl = encl.getEnclosingElement(); 2214 kind = encl.getKind(); 2215 } 2216 return (TypeElement)encl; 2217 } 2218 2219 private ConstantValueExpression cve = null; 2220 2221 public String constantValueExpression(VariableElement ve) { 2222 if (cve == null) 2223 cve = new ConstantValueExpression(); 2224 return cve.visit(ve.asType(), ve.getConstantValue()); 2225 } 2226 2227 // We could also use Elements.getConstantValueExpression, which provides 2228 // similar functionality, but which also includes casts to provide valid 2229 // compilable constants: e.g. (byte) 0x7f 2230 private static class ConstantValueExpression extends TypeKindVisitor9<String, Object> { 2231 @Override 2232 public String visitPrimitiveAsBoolean(PrimitiveType t, Object val) { 2233 return ((boolean) val) ? "true" : "false"; 2234 } 2235 2236 @Override 2237 public String visitPrimitiveAsByte(PrimitiveType t, Object val) { 2238 return "0x" + Integer.toString(((Byte) val) & 0xff, 16); 2239 } 2240 2241 @Override 2242 public String visitPrimitiveAsChar(PrimitiveType t, Object val) { 2243 StringBuilder buf = new StringBuilder(8); 2244 buf.append('\''); 2245 sourceChar((char) val, buf); 2246 buf.append('\''); 2247 return buf.toString(); 2248 } 2249 2250 @Override 2251 public String visitPrimitiveAsDouble(PrimitiveType t, Object val) { 2252 return sourceForm(((Double) val), 'd'); 2253 } 2254 2255 @Override 2256 public String visitPrimitiveAsFloat(PrimitiveType t, Object val) { 2257 return sourceForm(((Float) val).doubleValue(), 'f'); 2258 } 2259 2260 @Override 2261 public String visitPrimitiveAsLong(PrimitiveType t, Object val) { 2262 return val + "L"; 2263 } 2264 2265 @Override 2266 protected String defaultAction(TypeMirror e, Object val) { 2267 if (val == null) 2268 return null; 2269 else if (val instanceof String s) 2270 return sourceForm(s); 2271 return val.toString(); // covers int, short 2272 } 2273 2274 private String sourceForm(double v, char suffix) { 2275 if (Double.isNaN(v)) 2276 return "0" + suffix + "/0" + suffix; 2277 if (v == Double.POSITIVE_INFINITY) 2278 return "1" + suffix + "/0" + suffix; 2279 if (v == Double.NEGATIVE_INFINITY) 2280 return "-1" + suffix + "/0" + suffix; 2281 return v + (suffix == 'f' || suffix == 'F' ? "" + suffix : ""); 2282 } 2283 2284 private String sourceForm(String s) { 2285 StringBuilder buf = new StringBuilder(s.length() + 5); 2286 buf.append('\"'); 2287 for (int i = 0; i < s.length(); i++) { 2288 char c = s.charAt(i); 2289 sourceChar(c, buf); 2290 } 2291 buf.append('\"'); 2292 return buf.toString(); 2293 } 2294 2295 private void sourceChar(char c, StringBuilder buf) { 2296 switch (c) { 2297 case '\b' -> buf.append("\\b"); 2298 case '\t' -> buf.append("\\t"); 2299 case '\n' -> buf.append("\\n"); 2300 case '\f' -> buf.append("\\f"); 2301 case '\r' -> buf.append("\\r"); 2302 case '\"' -> buf.append("\\\""); 2303 case '\'' -> buf.append("\\\'"); 2304 case '\\' -> buf.append("\\\\"); 2305 default -> { 2306 if (isPrintableAscii(c)) { 2307 buf.append(c); 2308 return; 2309 } 2310 unicodeEscape(c, buf); 2311 } 2312 } 2313 } 2314 2315 private void unicodeEscape(char c, StringBuilder buf) { 2316 final String chars = "0123456789abcdef"; 2317 buf.append("\\u"); 2318 buf.append(chars.charAt(15 & (c >> 12))); 2319 buf.append(chars.charAt(15 & (c >> 8))); 2320 buf.append(chars.charAt(15 & (c >> 4))); 2321 buf.append(chars.charAt(15 & (c >> 0))); 2322 } 2323 2324 private boolean isPrintableAscii(char c) { 2325 return c >= ' ' && c <= '~'; 2326 } 2327 } 2328 2329 public boolean isEnclosingPackageIncluded(TypeElement te) { 2330 return isIncluded(containingPackage(te)); 2331 } 2332 2333 public boolean isIncluded(Element e) { 2334 return configuration.docEnv.isIncluded(e); 2335 } 2336 2337 private SimpleElementVisitor14<Boolean, Void> specifiedVisitor = null; 2338 public boolean isSpecified(Element e) { 2339 if (specifiedVisitor == null) { 2340 specifiedVisitor = new SimpleElementVisitor14<>() { 2341 @Override 2342 public Boolean visitModule(ModuleElement e, Void p) { 2343 return configuration.getSpecifiedModuleElements().contains(e); 2344 } 2345 2346 @Override 2347 public Boolean visitPackage(PackageElement e, Void p) { 2348 return configuration.getSpecifiedPackageElements().contains(e); 2349 } 2350 2351 @Override 2352 public Boolean visitType(TypeElement e, Void p) { 2353 return configuration.getSpecifiedTypeElements().contains(e); 2354 } 2355 2356 @Override 2357 protected Boolean defaultAction(Element e, Void p) { 2358 return false; 2359 } 2360 }; 2361 } 2362 return specifiedVisitor.visit(e); 2363 } 2364 2365 /** 2366 * Get the package name for a given package element. An unnamed package is returned as <Unnamed> 2367 * Use {@link jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter#getLocalizedPackageName(PackageElement)} 2368 * to get a localized string for the unnamed package instead. 2369 * 2370 * @param pkg 2371 * @return 2372 */ 2373 public String getPackageName(PackageElement pkg) { 2374 if (pkg == null || pkg.isUnnamed()) { 2375 return DocletConstants.DEFAULT_ELEMENT_NAME; 2376 } 2377 return pkg.getQualifiedName().toString(); 2378 } 2379 2380 /** 2381 * Get the module name for a given module element. An unnamed module is returned as <Unnamed> 2382 * 2383 * @param mdle a ModuleElement 2384 * @return 2385 */ 2386 public String getModuleName(ModuleElement mdle) { 2387 if (mdle == null || mdle.isUnnamed()) { 2388 return DocletConstants.DEFAULT_ELEMENT_NAME; 2389 } 2390 return mdle.getQualifiedName().toString(); 2391 } 2392 2393 public boolean isAttribute(DocTree doctree) { 2394 return isKind(doctree, ATTRIBUTE); 2395 } 2396 2397 public boolean isAuthor(DocTree doctree) { 2398 return isKind(doctree, AUTHOR); 2399 } 2400 2401 public boolean isComment(DocTree doctree) { 2402 return isKind(doctree, COMMENT); 2403 } 2404 2405 public boolean isDeprecated(DocTree doctree) { 2406 return isKind(doctree, DEPRECATED); 2407 } 2408 2409 public boolean isDocComment(DocTree doctree) { 2410 return isKind(doctree, DOC_COMMENT); 2411 } 2412 2413 public boolean isDocRoot(DocTree doctree) { 2414 return isKind(doctree, DOC_ROOT); 2415 } 2416 2417 public boolean isEndElement(DocTree doctree) { 2418 return isKind(doctree, END_ELEMENT); 2419 } 2420 2421 public boolean isEntity(DocTree doctree) { 2422 return isKind(doctree, ENTITY); 2423 } 2424 2425 public boolean isErroneous(DocTree doctree) { 2426 return isKind(doctree, ERRONEOUS); 2427 } 2428 2429 public boolean isException(DocTree doctree) { 2430 return isKind(doctree, EXCEPTION); 2431 } 2432 2433 public boolean isIdentifier(DocTree doctree) { 2434 return isKind(doctree, IDENTIFIER); 2435 } 2436 2437 public boolean isInheritDoc(DocTree doctree) { 2438 return isKind(doctree, INHERIT_DOC); 2439 } 2440 2441 public boolean isLink(DocTree doctree) { 2442 return isKind(doctree, LINK); 2443 } 2444 2445 public boolean isLinkPlain(DocTree doctree) { 2446 return isKind(doctree, LINK_PLAIN); 2447 } 2448 2449 public boolean isLiteral(DocTree doctree) { 2450 return isKind(doctree, LITERAL); 2451 } 2452 2453 public boolean isOther(DocTree doctree) { 2454 return doctree.getKind() == DocTree.Kind.OTHER; 2455 } 2456 2457 public boolean isParam(DocTree doctree) { 2458 return isKind(doctree, PARAM); 2459 } 2460 2461 public boolean isReference(DocTree doctree) { 2462 return isKind(doctree, REFERENCE); 2463 } 2464 2465 public boolean isReturn(DocTree doctree) { 2466 return isKind(doctree, RETURN); 2467 } 2468 2469 public boolean isSee(DocTree doctree) { 2470 return isKind(doctree, SEE); 2471 } 2472 2473 public boolean isSerial(DocTree doctree) { 2474 return isKind(doctree, SERIAL); 2475 } 2476 2477 public boolean isSerialData(DocTree doctree) { 2478 return isKind(doctree, SERIAL_DATA); 2479 } 2480 2481 public boolean isSerialField(DocTree doctree) { 2482 return isKind(doctree, SERIAL_FIELD); 2483 } 2484 2485 public boolean isSince(DocTree doctree) { 2486 return isKind(doctree, SINCE); 2487 } 2488 2489 public boolean isStartElement(DocTree doctree) { 2490 return isKind(doctree, START_ELEMENT); 2491 } 2492 2493 public boolean isText(DocTree doctree) { 2494 return isKind(doctree, TEXT); 2495 } 2496 2497 public boolean isThrows(DocTree doctree) { 2498 return isKind(doctree, THROWS); 2499 } 2500 2501 public boolean isUnknownBlockTag(DocTree doctree) { 2502 return isKind(doctree, UNKNOWN_BLOCK_TAG); 2503 } 2504 2505 public boolean isUnknownInlineTag(DocTree doctree) { 2506 return isKind(doctree, UNKNOWN_INLINE_TAG); 2507 } 2508 2509 public boolean isValue(DocTree doctree) { 2510 return isKind(doctree, VALUE); 2511 } 2512 2513 public boolean isVersion(DocTree doctree) { 2514 return isKind(doctree, VERSION); 2515 } 2516 2517 private boolean isKind(DocTree doctree, DocTree.Kind match) { 2518 return doctree.getKind() == match; 2519 } 2520 2521 private final CommentHelperCache commentHelperCache = new CommentHelperCache(this); 2522 2523 public CommentHelper getCommentHelper(Element element) { 2524 return commentHelperCache.computeIfAbsent(element); 2525 } 2526 2527 public void removeCommentHelper(Element element) { 2528 commentHelperCache.remove(element); 2529 } 2530 2531 public List<? extends DocTree> getBlockTags(Element element) { 2532 return getBlockTags(getDocCommentTree(element)); 2533 } 2534 2535 public List<? extends DocTree> getBlockTags(DocCommentTree dcTree) { 2536 return dcTree == null ? Collections.emptyList() : dcTree.getBlockTags(); 2537 } 2538 2539 public List<? extends DocTree> getBlockTags(Element element, Predicate<DocTree> filter) { 2540 return getBlockTags(element).stream() 2541 .filter(t -> t.getKind() != ERRONEOUS) 2542 .filter(filter) 2543 .toList(); 2544 } 2545 2546 public <T extends DocTree> List<? extends T> getBlockTags(Element element, Predicate<DocTree> filter, Class<T> tClass) { 2547 return getBlockTags(element).stream() 2548 .filter(t -> t.getKind() != ERRONEOUS) 2549 .filter(filter) 2550 .map(tClass::cast) 2551 .toList(); 2552 } 2553 2554 public List<? extends DocTree> getBlockTags(Element element, DocTree.Kind kind) { 2555 return getBlockTags(element, t -> t.getKind() == kind); 2556 } 2557 2558 public <T extends DocTree> List<? extends T> getBlockTags(Element element, DocTree.Kind kind, Class<T> tClass) { 2559 return getBlockTags(element, t -> t.getKind() == kind, tClass); 2560 } 2561 2562 public List<? extends DocTree> getBlockTags(Element element, DocTree.Kind kind, DocTree.Kind altKind) { 2563 return getBlockTags(element, t -> t.getKind() == kind || t.getKind() == altKind); 2564 } 2565 2566 public List<? extends DocTree> getBlockTags(Element element, Taglet taglet) { 2567 return getBlockTags(element, t -> { 2568 if (taglet instanceof BaseTaglet baseTaglet) { 2569 return baseTaglet.accepts(t); 2570 } else if (t instanceof BlockTagTree blockTagTree) { 2571 return blockTagTree.getTagName().equals(taglet.getName()); 2572 } else { 2573 return false; 2574 } 2575 }); 2576 } 2577 2578 public boolean hasBlockTag(Element element, DocTree.Kind kind) { 2579 return hasBlockTag(element, kind, null); 2580 } 2581 2582 public boolean hasBlockTag(Element element, DocTree.Kind kind, final String tagName) { 2583 if (hasDocCommentTree(element)) { 2584 CommentHelper ch = getCommentHelper(element); 2585 for (DocTree dt : getBlockTags(ch.dcTree)) { 2586 if (dt.getKind() == kind && (tagName == null || ch.getTagName(dt).equals(tagName))) { 2587 return true; 2588 } 2589 } 2590 } 2591 return false; 2592 } 2593 2594 /* 2595 * Tests whether an element's doc comment contains a block tag without caching it or 2596 * running doclint on it. This is done by using getDocCommentInfo(Element) to retrieve 2597 * the doc comment info. 2598 */ 2599 boolean hasBlockTagUnchecked(Element element, DocTree.Kind kind) { 2600 DocCommentInfo dcInfo = getDocCommentInfo(element); 2601 if (dcInfo != null && dcInfo.dcTree != null) { 2602 for (DocTree dt : getBlockTags(dcInfo.dcTree)) { 2603 if (dt.getKind() == kind) { 2604 return true; 2605 } 2606 } 2607 } 2608 return false; 2609 } 2610 2611 /** 2612 * Gets a TreePath for an Element. Note this method is called very 2613 * frequently, care must be taken to ensure this method is lithe 2614 * and efficient. 2615 * @param e an Element 2616 * @return TreePath 2617 */ 2618 public TreePath getTreePath(Element e) { 2619 DocCommentInfo info = dcTreeCache.get(e); 2620 if (info != null && info.treePath != null) { 2621 return info.treePath; 2622 } 2623 info = configuration.cmtUtils.getSyntheticCommentInfo(e); 2624 if (info != null && info.treePath != null) { 2625 return info.treePath; 2626 } 2627 Map<Element, TreePath> elementToTreePath = configuration.workArounds.getElementToTreePath(); 2628 TreePath path = elementToTreePath.get(e); 2629 if (path != null || elementToTreePath.containsKey(e)) { 2630 // expedite the path and one that is a null 2631 return path; 2632 } 2633 return elementToTreePath.computeIfAbsent(e, docTrees::getPath); 2634 } 2635 2636 /** 2637 * A cache of doc comment info objects for elements. 2638 * The entries may come from the AST and DocCommentParser, or may be autromatically 2639 * generated comments for mandated elements and JavaFX properties. 2640 * 2641 * @see CommentUtils#dcInfoMap 2642 */ 2643 private final Map<Element, DocCommentInfo> dcTreeCache = new LinkedHashMap<>(); 2644 2645 /** 2646 * Checks whether an element has an associated doc comment. 2647 * @param element the element 2648 * @return {@code true} if the element has a comment, and false otherwise 2649 */ 2650 public boolean hasDocCommentTree(Element element) { 2651 DocCommentInfo info = getDocCommentInfo(element); 2652 return info != null && info.dcTree != null; 2653 } 2654 2655 /** 2656 * Retrieves the doc comments for a given element. 2657 * @param element the element 2658 * @return DocCommentTree for the Element 2659 */ 2660 public DocCommentTree getDocCommentTree0(Element element) { 2661 2662 DocCommentInfo info = getDocCommentInfo(element); 2663 2664 DocCommentTree docCommentTree = info == null ? null : info.dcTree; 2665 if (!dcTreeCache.containsKey(element)) { 2666 TreePath path = info == null ? null : info.treePath; 2667 if (path != null) { 2668 if (docCommentTree != null && !configuration.isAllowScriptInComments()) { 2669 try { 2670 javaScriptScanner.scan(docCommentTree, path, p -> { 2671 throw new JavaScriptScanner.Fault(); 2672 }); 2673 } catch (JavaScriptScanner.Fault jsf) { 2674 String text = resources.getText("doclet.JavaScript_in_comment"); 2675 throw new UncheckedDocletException(new SimpleDocletException(text, jsf)); 2676 } 2677 } 2678 // run doclint even if docCommentTree is null, to trigger checks for missing comments 2679 configuration.runDocLint(path); 2680 } 2681 dcTreeCache.put(element, info); 2682 } 2683 return docCommentTree; 2684 } 2685 2686 private DocCommentInfo getDocCommentInfo(Element element) { 2687 DocCommentInfo info = null; 2688 2689 ElementKind kind = element.getKind(); 2690 if (kind == ElementKind.PACKAGE || kind == ElementKind.OTHER) { 2691 info = dcTreeCache.get(element); // local cache 2692 if (info == null && kind == ElementKind.PACKAGE) { 2693 // package-info.java 2694 info = getDocCommentInfo0(element); 2695 } 2696 if (info == null) { 2697 // package.html or overview.html 2698 info = configuration.cmtUtils.getHtmlCommentInfo(element); // html source 2699 } 2700 } else { 2701 info = configuration.cmtUtils.getSyntheticCommentInfo(element); 2702 if (info == null) { 2703 info = dcTreeCache.get(element); // local cache 2704 } 2705 if (info == null) { 2706 info = getDocCommentInfo0(element); // get the real mccoy 2707 } 2708 } 2709 2710 return info; 2711 } 2712 2713 private DocCommentInfo getDocCommentInfo0(Element element) { 2714 // prevent nasty things downstream with overview element 2715 if (!isOverviewElement(element)) { 2716 TreePath path = getTreePath(element); 2717 if (path != null) { 2718 DocCommentTree docCommentTree = docTrees.getDocCommentTree(path); 2719 return new DocCommentInfo(path, docCommentTree); 2720 } 2721 } 2722 return null; 2723 } 2724 2725 public void checkJavaScriptInOption(String name, String value) { 2726 if (!configuration.isAllowScriptInComments()) { 2727 DocCommentTree dct = configuration.cmtUtils.parse( 2728 URI.create("option://" + name.replace("-", "")), "<body>" + value + "</body>"); 2729 2730 if (dct == null) 2731 return; 2732 2733 try { 2734 javaScriptScanner.scan(dct, null, p -> { 2735 throw new JavaScriptScanner.Fault(); 2736 }); 2737 } catch (JavaScriptScanner.Fault jsf) { 2738 String text = resources.getText("doclet.JavaScript_in_option", name); 2739 throw new UncheckedDocletException(new SimpleDocletException(text, jsf)); 2740 } 2741 } 2742 } 2743 2744 public DocCommentTree getDocCommentTree(Element element) { 2745 CommentHelper ch = commentHelperCache.get(element); 2746 if (ch != null) { 2747 return ch.dcTree; 2748 } 2749 DocCommentTree dcTree = getDocCommentTree0(element); 2750 if (dcTree != null) { 2751 commentHelperCache.put(element, new CommentHelper(configuration, element, getTreePath(element), dcTree)); 2752 } 2753 return dcTree; 2754 } 2755 2756 public List<? extends DocTree> getPreamble(Element element) { 2757 DocCommentTree docCommentTree = getDocCommentTree(element); 2758 return docCommentTree == null 2759 ? Collections.emptyList() 2760 : docCommentTree.getPreamble(); 2761 } 2762 2763 public List<? extends DocTree> getFullBody(Element element) { 2764 DocCommentTree docCommentTree = getDocCommentTree(element); 2765 return (docCommentTree == null) 2766 ? Collections.emptyList() 2767 : docCommentTree.getFullBody(); 2768 } 2769 2770 public List<? extends DocTree> getBody(Element element) { 2771 DocCommentTree docCommentTree = getDocCommentTree(element); 2772 return (docCommentTree == null) 2773 ? Collections.emptyList() 2774 : docCommentTree.getFullBody(); 2775 } 2776 2777 public List<? extends DeprecatedTree> getDeprecatedTrees(Element element) { 2778 return getBlockTags(element, DEPRECATED, DeprecatedTree.class); 2779 } 2780 2781 public List<? extends ProvidesTree> getProvidesTrees(Element element) { 2782 return getBlockTags(element, PROVIDES, ProvidesTree.class); 2783 } 2784 2785 public List<? extends SeeTree> getSeeTrees(Element element) { 2786 return getBlockTags(element, SEE, SeeTree.class); 2787 } 2788 2789 public List<? extends SerialTree> getSerialTrees(Element element) { 2790 return getBlockTags(element, SERIAL, SerialTree.class); 2791 } 2792 2793 public List<? extends SerialFieldTree> getSerialFieldTrees(VariableElement field) { 2794 return getBlockTags(field, DocTree.Kind.SERIAL_FIELD, SerialFieldTree.class); 2795 } 2796 2797 public List<? extends ThrowsTree> getThrowsTrees(Element element) { 2798 return getBlockTags(element, 2799 t -> switch (t.getKind()) { case EXCEPTION, THROWS -> true; default -> false; }, 2800 ThrowsTree.class); 2801 } 2802 2803 public List<? extends ParamTree> getTypeParamTrees(Element element) { 2804 return getParamTrees(element, true); 2805 } 2806 2807 public List<? extends ParamTree> getParamTrees(Element element) { 2808 return getParamTrees(element, false); 2809 } 2810 2811 private List<? extends ParamTree> getParamTrees(Element element, boolean isTypeParameters) { 2812 return getBlockTags(element, 2813 t -> t.getKind() == PARAM && ((ParamTree) t).isTypeParameter() == isTypeParameters, 2814 ParamTree.class); 2815 } 2816 2817 public List<? extends ReturnTree> getReturnTrees(Element element) { 2818 return new ArrayList<>(getBlockTags(element, RETURN, ReturnTree.class)); 2819 } 2820 2821 public List<? extends UsesTree> getUsesTrees(Element element) { 2822 return getBlockTags(element, USES, UsesTree.class); 2823 } 2824 2825 public List<? extends DocTree> getFirstSentenceTrees(Element element) { 2826 DocCommentTree dcTree = getDocCommentTree(element); 2827 if (dcTree == null) { 2828 return Collections.emptyList(); 2829 } 2830 return new ArrayList<>(dcTree.getFirstSentence()); 2831 } 2832 2833 public ModuleElement containingModule(Element e) { 2834 return elementUtils.getModuleOf(e); 2835 } 2836 2837 public PackageElement containingPackage(Element e) { 2838 return elementUtils.getPackageOf(e); 2839 } 2840 2841 public TypeElement getTopMostContainingTypeElement(Element e) { 2842 if (isPackage(e)) { 2843 return null; 2844 } 2845 TypeElement outer = getEnclosingTypeElement(e); 2846 if (outer == null) 2847 return (TypeElement)e; 2848 while (outer != null && outer.getNestingKind().isNested()) { 2849 outer = getEnclosingTypeElement(outer); 2850 } 2851 return outer; 2852 } 2853 2854 /** 2855 * A memory-sensitive cache for {@link CommentHelper} objects, 2856 * which are expensive to compute. 2857 */ 2858 private static class CommentHelperCache { 2859 2860 private final Map<Element, SoftReference<CommentHelper>> map; 2861 private final Utils utils; 2862 2863 public CommentHelperCache(Utils utils) { 2864 map = new HashMap<>(); 2865 this.utils = utils; 2866 } 2867 2868 public CommentHelper remove(Element key) { 2869 SoftReference<CommentHelper> value = map.remove(key); 2870 return value == null ? null : value.get(); 2871 } 2872 2873 public CommentHelper put(Element key, CommentHelper value) { 2874 SoftReference<CommentHelper> prev = map.put(key, new SoftReference<>(value)); 2875 return prev == null ? null : prev.get(); 2876 } 2877 2878 public CommentHelper get(Object key) { 2879 SoftReference<CommentHelper> value = map.get(key); 2880 return value == null ? null : value.get(); 2881 } 2882 2883 public CommentHelper computeIfAbsent(Element key) { 2884 SoftReference<CommentHelper> refValue = map.get(key); 2885 if (refValue != null) { 2886 CommentHelper value = refValue.get(); 2887 if (value != null) { 2888 return value; 2889 } 2890 } 2891 CommentHelper newValue = new CommentHelper(utils.configuration, key, utils.getTreePath(key), 2892 utils.getDocCommentTree(key)); 2893 map.put(key, new SoftReference<>(newValue)); 2894 return newValue; 2895 } 2896 } 2897 2898 /** 2899 * A container holding a pair of values (tuple). 2900 * 2901 * @param <K> the type of the first value 2902 * @param <L> the type of the second value 2903 */ 2904 public static class Pair<K, L> { 2905 public final K first; 2906 public final L second; 2907 2908 public Pair(K first, L second) { 2909 this.first = first; 2910 this.second = second; 2911 } 2912 2913 @Override 2914 public String toString() { 2915 return first + ":" + second; 2916 } 2917 } 2918 2919 /** 2920 * Return the set of preview language features used to declare the given element. 2921 * 2922 * @param e the Element to check. 2923 * @return the set of preview language features used to declare the given element 2924 */ 2925 public Set<DeclarationPreviewLanguageFeatures> previewLanguageFeaturesUsed(Element e) { 2926 return new HashSet<>(); 2927 } 2928 2929 public enum DeclarationPreviewLanguageFeatures { 2930 NONE(List.of("")); 2931 public final List<String> features; 2932 2933 DeclarationPreviewLanguageFeatures(List<String> features) { 2934 this.features = features; 2935 } 2936 } 2937 2938 public PreviewSummary declaredUsingPreviewAPIs(Element el) { 2939 List<TypeElement> usedInDeclaration = new ArrayList<>(); 2940 usedInDeclaration.addAll(annotations2Classes(el)); 2941 switch (el.getKind()) { 2942 case ANNOTATION_TYPE, CLASS, ENUM, INTERFACE, RECORD -> { 2943 TypeElement te = (TypeElement) el; 2944 for (TypeParameterElement tpe : te.getTypeParameters()) { 2945 usedInDeclaration.addAll(types2Classes(tpe.getBounds())); 2946 } 2947 usedInDeclaration.addAll(types2Classes(List.of(te.getSuperclass()))); 2948 usedInDeclaration.addAll(types2Classes(te.getInterfaces())); 2949 usedInDeclaration.addAll(types2Classes(te.getPermittedSubclasses())); 2950 usedInDeclaration.addAll(types2Classes(te.getRecordComponents().stream().map(Element::asType).toList())); //TODO: annotations on record components??? 2951 } 2952 case CONSTRUCTOR, METHOD -> { 2953 ExecutableElement ee = (ExecutableElement) el; 2954 for (TypeParameterElement tpe : ee.getTypeParameters()) { 2955 usedInDeclaration.addAll(types2Classes(tpe.getBounds())); 2956 } 2957 usedInDeclaration.addAll(types2Classes(List.of(ee.getReturnType()))); 2958 usedInDeclaration.addAll(types2Classes(List.of(ee.getReceiverType()))); 2959 usedInDeclaration.addAll(types2Classes(ee.getThrownTypes())); 2960 usedInDeclaration.addAll(types2Classes(ee.getParameters().stream().map(VariableElement::asType).toList())); 2961 usedInDeclaration.addAll(annotationValue2Classes(ee.getDefaultValue())); 2962 } 2963 case FIELD, ENUM_CONSTANT, RECORD_COMPONENT -> { 2964 VariableElement ve = (VariableElement) el; 2965 usedInDeclaration.addAll(types2Classes(List.of(ve.asType()))); 2966 } 2967 case MODULE, PACKAGE -> { 2968 } 2969 default -> throw new IllegalArgumentException("Unexpected: " + el.getKind()); 2970 } 2971 2972 Set<TypeElement> previewAPI = new HashSet<>(); 2973 Set<TypeElement> reflectivePreviewAPI = new HashSet<>(); 2974 Set<TypeElement> declaredUsingPreviewFeature = new HashSet<>(); 2975 2976 for (TypeElement type : usedInDeclaration) { 2977 if (!isIncluded(type) && !configuration.extern.isExternal(type)) { 2978 continue; 2979 } 2980 if (isPreviewAPI(type)) { 2981 if (isReflectivePreviewAPI(type)) { 2982 reflectivePreviewAPI.add(type); 2983 } else { 2984 previewAPI.add(type); 2985 } 2986 } 2987 if (!previewLanguageFeaturesUsed(type).isEmpty()) { 2988 declaredUsingPreviewFeature.add(type); 2989 } 2990 } 2991 2992 return new PreviewSummary(previewAPI, reflectivePreviewAPI, declaredUsingPreviewFeature); 2993 } 2994 2995 private Collection<TypeElement> types2Classes(List<? extends TypeMirror> types) { 2996 List<TypeElement> result = new ArrayList<>(); 2997 List<TypeMirror> todo = new ArrayList<>(types); 2998 2999 while (!todo.isEmpty()) { 3000 TypeMirror type = todo.remove(todo.size() - 1); 3001 3002 result.addAll(annotations2Classes(type)); 3003 3004 if (type.getKind() == DECLARED) { 3005 DeclaredType dt = (DeclaredType) type; 3006 result.add((TypeElement) dt.asElement()); 3007 todo.addAll(dt.getTypeArguments()); 3008 } 3009 } 3010 3011 return result; 3012 } 3013 3014 private Collection<TypeElement> annotations2Classes(AnnotatedConstruct annotated) { 3015 List<TypeElement> result = new ArrayList<>(); 3016 3017 for (AnnotationMirror am : annotated.getAnnotationMirrors()) { 3018 result.addAll(annotation2Classes(am)); 3019 } 3020 3021 return result; 3022 } 3023 3024 private Collection<TypeElement> annotation2Classes(AnnotationMirror am) { 3025 List<TypeElement> result = new ArrayList<>(); 3026 3027 result.addAll(types2Classes(List.of(am.getAnnotationType()))); 3028 am.getElementValues() 3029 .values() 3030 .stream() 3031 .flatMap(av -> annotationValue2Classes(av).stream()) 3032 .forEach(result::add); 3033 3034 return result; 3035 } 3036 3037 private Collection<TypeElement> annotationValue2Classes(AnnotationValue value) { 3038 if (value == null) { 3039 return List.of(); 3040 } 3041 3042 List<TypeElement> result = new ArrayList<>(); 3043 3044 value.accept(new SimpleAnnotationValueVisitor14<>() { 3045 @Override 3046 public Object visitArray(List<? extends AnnotationValue> vals, Object p) { 3047 vals.stream() 3048 .forEach(v -> v.accept(this, null)); 3049 return super.visitArray(vals, p); 3050 } 3051 @Override 3052 public Object visitAnnotation(AnnotationMirror a, Object p) { 3053 result.addAll(annotation2Classes(a)); 3054 return super.visitAnnotation(a, p); 3055 } 3056 3057 @Override 3058 public Object visitType(TypeMirror t, Object p) { 3059 result.addAll(types2Classes(List.of(t))); 3060 return super.visitType(t, p); 3061 } 3062 3063 }, null); 3064 3065 return result; 3066 } 3067 3068 public static final class PreviewSummary { 3069 public final Set<TypeElement> previewAPI; 3070 public final Set<TypeElement> reflectivePreviewAPI; 3071 public final Set<TypeElement> declaredUsingPreviewFeature; 3072 3073 public PreviewSummary(Set<TypeElement> previewAPI, Set<TypeElement> reflectivePreviewAPI, Set<TypeElement> declaredUsingPreviewFeature) { 3074 this.previewAPI = previewAPI; 3075 this.reflectivePreviewAPI = reflectivePreviewAPI; 3076 this.declaredUsingPreviewFeature = declaredUsingPreviewFeature; 3077 } 3078 3079 @Override 3080 public String toString() { 3081 return "PreviewSummary{" + "previewAPI=" + previewAPI + ", reflectivePreviewAPI=" + reflectivePreviewAPI + ", declaredUsingPreviewFeature=" + declaredUsingPreviewFeature + '}'; 3082 } 3083 3084 } 3085 3086 /** 3087 * Checks whether the given Element should be marked as a preview API. 3088 * 3089 * Note that if a type is marked as a preview, its members are not. 3090 * 3091 * @param el the element to check 3092 * @return true if and only if the given element should be marked as a preview API 3093 */ 3094 public boolean isPreviewAPI(Element el) { 3095 boolean parentPreviewAPI = false; 3096 Element enclosing = el.getEnclosingElement(); 3097 if (enclosing != null && (enclosing.getKind().isClass() || enclosing.getKind().isInterface())) { 3098 parentPreviewAPI = configuration.workArounds.isPreviewAPI(enclosing); 3099 } 3100 boolean previewAPI = configuration.workArounds.isPreviewAPI(el); 3101 return !parentPreviewAPI && previewAPI; 3102 } 3103 3104 /** 3105 * Checks whether the given Element should be marked as a reflective preview API. 3106 * 3107 * Note that if a type is marked as a preview, its members are not. 3108 * 3109 * @param el the element to check 3110 * @return true if and only if the given element should be marked 3111 * as a reflective preview API 3112 */ 3113 public boolean isReflectivePreviewAPI(Element el) { 3114 return isPreviewAPI(el) && configuration.workArounds.isReflectivePreviewAPI(el); 3115 } 3116 3117 /** 3118 * Return all flags for the given Element. 3119 * 3120 * @param el the element to test 3121 * @return the set of all the element's flags. 3122 */ 3123 public Set<ElementFlag> elementFlags(Element el) { 3124 Set<ElementFlag> flags = EnumSet.noneOf(ElementFlag.class); 3125 PreviewSummary previewAPIs = declaredUsingPreviewAPIs(el); 3126 3127 if (isDeprecated(el)) { 3128 flags.add(ElementFlag.DEPRECATED); 3129 } 3130 3131 if ((!previewLanguageFeaturesUsed(el).isEmpty() || 3132 configuration.workArounds.isPreviewAPI(el) || 3133 !previewAPIs.previewAPI.isEmpty() || 3134 !previewAPIs.reflectivePreviewAPI.isEmpty() || 3135 !previewAPIs.declaredUsingPreviewFeature.isEmpty()) && 3136 !hasNoProviewAnnotation(el)) { 3137 flags.add(ElementFlag.PREVIEW); 3138 } 3139 3140 return flags; 3141 } 3142 3143 /** 3144 * An element can have flags that place it into some sub-categories, like 3145 * being a preview or a deprecated element. 3146 */ 3147 public enum ElementFlag { 3148 DEPRECATED, 3149 PREVIEW 3150 } 3151 3152 private boolean hasNoProviewAnnotation(Element el) { 3153 return el.getAnnotationMirrors() 3154 .stream() 3155 .anyMatch(am -> "jdk.internal.javac.NoPreview".equals(getQualifiedTypeName(am.getAnnotationType()))); 3156 } 3157 } 3158