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