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