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