1/*
2 * Copyright (c) 1999, 2017, 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    public 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 (configuration.workArounds.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 compareFullyQualifiedNames(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 and packages then simply compare the qualified names, if comparing a module
1776     *  or a package with a type/member then compare the FullyQualifiedName of the module or a package
1777     *  with the SimpleName of the entity, otherwise
1778     *  1. compare the ElementKind ex: Module, Package, Interface etc.
1779     *  2a. if equal and if the type is of ExecutableElement(Constructor, Methods),
1780     *      a case insensitive comparison of parameter the type signatures
1781     *  2b. if equal, case sensitive comparison of the type signatures
1782     *  3. finally, if equal, compare the FQNs of the entities
1783     * @return a comparator for index file use
1784     */
1785    public Comparator<Element> makeIndexUseComparator() {
1786        return new Utils.ElementComparator<Element>() {
1787            /**
1788             * Compare two given elements, if comparing two modules or two packages, return the
1789             * comparison of FullyQualifiedName, if comparing a module or a package with a
1790             * type/member then compare the FullyQualifiedName of the module or the package
1791             * with the SimpleName of the entity, then sort on the kinds, then on
1792             * 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) || isPackage(e1)) && (isModule(e2) || isPackage(e2))) {
1804                    result = compareFullyQualifiedNames(e1, e2);
1805                    if (result != 0) {
1806                        return result;
1807                    }
1808                    return compareElementTypeKinds(e1, e2);
1809                }
1810                if (isModule(e1) || isPackage(e1)) {
1811                    result = compareStrings(getFullyQualifiedName(e1), getSimpleName(e2));
1812                } else if (isModule(e2) || isPackage(e2)) {
1813                    result = compareStrings(getSimpleName(e1), getFullyQualifiedName(e2));
1814                } else {
1815                    result = compareNames(e1, e2);
1816                }
1817                if (result != 0) {
1818                    return result;
1819                }
1820                result = compareElementTypeKinds(e1, e2);
1821                if (result != 0) {
1822                    return result;
1823                }
1824                if (hasParameters(e1)) {
1825                    List<? extends VariableElement> parameters1 = ((ExecutableElement)e1).getParameters();
1826                    List<? extends VariableElement> parameters2 = ((ExecutableElement)e2).getParameters();
1827                    result = compareParameters(false, parameters1, parameters2);
1828                    if (result != 0) {
1829                        return result;
1830                    }
1831                    result = compareParameters(true, parameters1, parameters2);
1832                    if (result != 0) {
1833                        return result;
1834                    }
1835                }
1836                return compareFullyQualifiedNames(e1, e2);
1837            }
1838        };
1839    }
1840
1841    /**
1842     * Compares the FullyQualifiedNames of two TypeMirrors
1843     * @return
1844     */
1845    public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() {
1846        return (TypeMirror type1, TypeMirror type2) -> {
1847            String s1 = getQualifiedTypeName(type1);
1848            String s2 = getQualifiedTypeName(type2);
1849            return compareStrings(s1, s2);
1850        };
1851    }
1852
1853    /**
1854     * Compares the SimpleNames of TypeMirrors if equal then the
1855     * FullyQualifiedNames of TypeMirrors.
1856     *
1857     * @return
1858     */
1859    public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() {
1860        return (TypeMirror t1, TypeMirror t2) -> {
1861            int result = compareStrings(getTypeName(t1, false), getTypeName(t2, false));
1862            if (result != 0)
1863                return result;
1864            return compareStrings(getQualifiedTypeName(t1), getQualifiedTypeName(t2));
1865        };
1866    }
1867
1868    /**
1869     * Get the qualified type name of a TypeMiror compatible with the Element's
1870     * getQualified name, returns  the qualified name of the Reference type
1871     * otherwise the primitive name.
1872     * @param t the type whose name is to be obtained.
1873     * @return the fully qualified name of Reference type or the primitive name
1874     */
1875    public String getQualifiedTypeName(TypeMirror t) {
1876        return new SimpleTypeVisitor9<String, Void>() {
1877            @Override
1878            public String visitDeclared(DeclaredType t, Void p) {
1879                return getFullyQualifiedName(t.asElement());
1880            }
1881
1882            @Override
1883            public String visitArray(ArrayType t, Void p) {
1884               return visit(t.getComponentType());
1885            }
1886
1887            @Override
1888            public String visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) {
1889                // The knee jerk reaction is to do this but don't!, as we would like
1890                // it to be compatible with the old world, now if we decide to do so
1891                // care must be taken to avoid collisions.
1892                // return getFullyQualifiedName(t.asElement());
1893                return t.toString();
1894            }
1895
1896            @Override
1897            protected String defaultAction(TypeMirror t, Void p) {
1898                return t.toString();
1899            }
1900
1901        }.visit(t);
1902    }
1903
1904    /**
1905     * A generic utility which returns the fully qualified names of an entity,
1906     * if the entity is not qualifiable then its enclosing entity, it is upto
1907     * the caller to add the elements name as required.
1908     * @param e the element to get FQN for.
1909     * @return the name
1910     */
1911    public String getFullyQualifiedName(Element e) {
1912        return getFullyQualifiedName(e, true);
1913    }
1914
1915    public String getFullyQualifiedName(Element e, final boolean outer) {
1916        return new SimpleElementVisitor9<String, Void>() {
1917            @Override
1918            public String visitModule(ModuleElement e, Void p) {
1919                return e.getQualifiedName().toString();
1920            }
1921
1922            @Override
1923            public String visitPackage(PackageElement e, Void p) {
1924                return e.getQualifiedName().toString();
1925            }
1926
1927            @Override
1928            public String visitType(TypeElement e, Void p) {
1929                return e.getQualifiedName().toString();
1930            }
1931
1932            @Override
1933            protected String defaultAction(Element e, Void p) {
1934                return outer ? visit(e.getEnclosingElement()) : e.getSimpleName().toString();
1935            }
1936        }.visit(e);
1937    }
1938
1939    /**
1940     * Comparator for ClassUse presentations, and sorts as follows:
1941     * 1. member names
1942     * 2. then fully qualified member names
1943     * 3. then parameter types if applicable
1944     * 4. finally the element kinds ie. package, class, interface etc.
1945     * @return a comparator to sort classes and members for class use
1946     */
1947    public Comparator<Element> makeClassUseComparator() {
1948        return new Utils.ElementComparator<Element>() {
1949            /**
1950             * Compare two Elements, first sort on simple name, and if
1951             * applicable on the fully qualified name, and finally if applicable
1952             * on the parameter types.
1953             * @param e1 - an element.
1954             * @param e2 - an element.
1955             * @return a negative integer, zero, or a positive integer as the first
1956             *         argument is less than, equal to, or greater than the second.
1957             */
1958            @Override
1959            public int compare(Element e1, Element e2) {
1960                int result = compareNames(e1, e2);
1961                if (result != 0) {
1962                    return result;
1963                }
1964                result = compareFullyQualifiedNames(e1, e2);
1965                if (result != 0) {
1966                    return result;
1967                }
1968                if (hasParameters(e1) && hasParameters(e2)) {
1969                    @SuppressWarnings("unchecked")
1970                    List<VariableElement> parameters1 = (List<VariableElement>) ((ExecutableElement)e1).getParameters();
1971                    @SuppressWarnings("unchecked")
1972                    List<VariableElement> parameters2 = (List<VariableElement>) ((ExecutableElement)e2).getParameters();
1973                    result =  compareParameters(false, parameters1, parameters2);
1974                    if (result != 0) {
1975                        return result;
1976                    }
1977                    result =  compareParameters(true, parameters1, parameters2);
1978                }
1979                if (result != 0) {
1980                    return result;
1981                }
1982                return compareElementTypeKinds(e1, e2);
1983            }
1984        };
1985    }
1986
1987    /**
1988     * A general purpose comparator to sort Element entities, basically provides the building blocks
1989     * for creating specific comparators for an use-case.
1990     * @param <T> an Element
1991     */
1992    private abstract class ElementComparator<T extends Element> implements Comparator<Element> {
1993        /**
1994         * compares two parameter arrays by first comparing the length of the arrays, and
1995         * then each Type of the parameter in the array.
1996         * @param params1 the first parameter array.
1997         * @param params2 the first parameter array.
1998         * @return a negative integer, zero, or a positive integer as the first
1999         *         argument is less than, equal to, or greater than the second.
2000         */
2001        final EnumMap<ElementKind, Integer> elementKindOrder;
2002        public ElementComparator() {
2003            elementKindOrder = new EnumMap<>(ElementKind.class);
2004            elementKindOrder.put(ElementKind.MODULE, 0);
2005            elementKindOrder.put(ElementKind.PACKAGE, 1);
2006            elementKindOrder.put(ElementKind.CLASS, 2);
2007            elementKindOrder.put(ElementKind.ENUM, 3);
2008            elementKindOrder.put(ElementKind.ENUM_CONSTANT, 4);
2009            elementKindOrder.put(ElementKind.INTERFACE, 5);
2010            elementKindOrder.put(ElementKind.ANNOTATION_TYPE, 6);
2011            elementKindOrder.put(ElementKind.FIELD, 7);
2012            elementKindOrder.put(ElementKind.CONSTRUCTOR, 8);
2013            elementKindOrder.put(ElementKind.METHOD, 9);
2014        }
2015
2016        protected int compareParameters(boolean caseSensitive, List<? extends VariableElement> params1,
2017                                                               List<? extends VariableElement> params2) {
2018
2019            return compareStrings(caseSensitive, getParametersAsString(params1),
2020                                                 getParametersAsString(params2));
2021        }
2022
2023        String getParametersAsString(List<? extends VariableElement> params) {
2024            StringBuilder sb = new StringBuilder();
2025            for (VariableElement param : params) {
2026                TypeMirror t = param.asType();
2027                // prefix P for primitive and R for reference types, thus items will
2028                // be ordered lexically and correctly.
2029                sb.append(getTypeCode(t)).append("-").append(t).append("-");
2030            }
2031            return sb.toString();
2032        }
2033
2034        private String getTypeCode(TypeMirror t) {
2035            return new SimpleTypeVisitor9<String, Void>() {
2036
2037                @Override
2038                public String visitPrimitive(PrimitiveType t, Void p) {
2039                    return "P";
2040                }
2041                @Override
2042                public String visitArray(ArrayType t, Void p) {
2043                    return visit(t.getComponentType());
2044                }
2045                @Override
2046                protected String defaultAction(TypeMirror e, Void p) {
2047                    return "R";
2048                }
2049
2050            }.visit(t);
2051        }
2052
2053        /**
2054         * Compares two Elements, typically the name of a method,
2055         * field or constructor.
2056         * @param e1 the first Element.
2057         * @param e2 the second Element.
2058         * @return a negative integer, zero, or a positive integer as the first
2059         *         argument is less than, equal to, or greater than the second.
2060         */
2061        protected int compareNames(Element e1, Element e2) {
2062            return compareStrings(getSimpleName(e1), getSimpleName(e2));
2063        }
2064
2065        /**
2066         * Compares the fully qualified names of the entities
2067         * @param e1 the first Element.
2068         * @param e2 the first Element.
2069         * @return a negative integer, zero, or a positive integer as the first
2070         *         argument is less than, equal to, or greater than the second.
2071         */
2072        protected int compareFullyQualifiedNames(Element e1, Element e2) {
2073            // add simplename to be compatible
2074            String thisElement = getFullyQualifiedName(e1);
2075            String thatElement = getFullyQualifiedName(e2);
2076            return compareStrings(thisElement, thatElement);
2077        }
2078        protected int compareElementTypeKinds(Element e1, Element e2) {
2079            return Integer.compare(elementKindOrder.get(e1.getKind()),
2080                                   elementKindOrder.get(e2.getKind()));
2081        }
2082        boolean hasParameters(Element e) {
2083            return new SimpleElementVisitor9<Boolean, Void>() {
2084                @Override
2085                public Boolean visitExecutable(ExecutableElement e, Void p) {
2086                    return true;
2087                }
2088
2089                @Override
2090                protected Boolean defaultAction(Element e, Void p) {
2091                    return false;
2092                }
2093
2094            }.visit(e);
2095        }
2096
2097        /**
2098         * The fully qualified names of the entities, used solely by the comparator.
2099         *
2100         * @param p1 the first Element.
2101         * @param p2 the first Element.
2102         * @return a negative integer, zero, or a positive integer as the first argument is less
2103         * than, equal to, or greater than the second.
2104         */
2105        private String getFullyQualifiedName(Element e) {
2106            return new SimpleElementVisitor9<String, Void>() {
2107                @Override
2108                public String visitModule(ModuleElement e, Void p) {
2109                    return e.getQualifiedName().toString();
2110                }
2111
2112                @Override
2113                public String visitPackage(PackageElement e, Void p) {
2114                    return e.getQualifiedName().toString();
2115                }
2116
2117                @Override
2118                public String visitExecutable(ExecutableElement e, Void p) {
2119                    // For backward compatibility
2120                    return getFullyQualifiedName(e.getEnclosingElement())
2121                            + "." + e.getSimpleName().toString();
2122                }
2123
2124                @Override
2125                public String visitType(TypeElement e, Void p) {
2126                    return e.getQualifiedName().toString();
2127                }
2128
2129                @Override
2130                protected String defaultAction(Element e, Void p) {
2131                    return getEnclosingTypeElement(e).getQualifiedName().toString()
2132                            + "." + e.getSimpleName().toString();
2133                }
2134            }.visit(e);
2135        }
2136    }
2137
2138    public Iterable<TypeElement> getEnclosedTypeElements(PackageElement pkg) {
2139        List<TypeElement> out = getInterfaces(pkg);
2140        out.addAll(getClasses(pkg));
2141        out.addAll(getEnums(pkg));
2142        out.addAll(getAnnotationTypes(pkg));
2143        return out;
2144    }
2145
2146    // Element related methods
2147    public List<Element> getAnnotationMembers(TypeElement aClass) {
2148        List<Element> members = getAnnotationFields(aClass);
2149        members.addAll(getAnnotationMethods(aClass));
2150        return members;
2151    }
2152
2153    public List<Element> getAnnotationFields(TypeElement aClass) {
2154        return getItems0(aClass, true, FIELD);
2155    }
2156
2157    List<Element> getAnnotationFieldsUnfiltered(TypeElement aClass) {
2158        return getItems0(aClass, true, FIELD);
2159    }
2160
2161    public List<Element> getAnnotationMethods(TypeElement aClass) {
2162        return getItems0(aClass, true, METHOD);
2163    }
2164
2165    public List<TypeElement> getAnnotationTypes(Element e) {
2166        return convertToTypeElement(getItems(e, true, ANNOTATION_TYPE));
2167    }
2168
2169    public List<TypeElement> getAnnotationTypesUnfiltered(Element e) {
2170        return convertToTypeElement(getItems(e, false, ANNOTATION_TYPE));
2171    }
2172
2173    public List<VariableElement> getFields(Element e) {
2174        return convertToVariableElement(getItems(e, true, FIELD));
2175    }
2176
2177    public List<VariableElement> getFieldsUnfiltered(Element e) {
2178        return convertToVariableElement(getItems(e, false, FIELD));
2179    }
2180
2181    public List<TypeElement> getClasses(Element e) {
2182       return convertToTypeElement(getItems(e, true, CLASS));
2183    }
2184
2185    public List<TypeElement> getClassesUnfiltered(Element e) {
2186       return convertToTypeElement(getItems(e, false, CLASS));
2187    }
2188
2189    public List<ExecutableElement> getConstructors(Element e) {
2190        return convertToExecutableElement(getItems(e, true, CONSTRUCTOR));
2191    }
2192
2193    public List<ExecutableElement> getMethods(Element e) {
2194        return convertToExecutableElement(getItems(e, true, METHOD));
2195    }
2196
2197    List<ExecutableElement> getMethodsUnfiltered(Element e) {
2198        return convertToExecutableElement(getItems(e, false, METHOD));
2199    }
2200
2201    public int getOrdinalValue(VariableElement member) {
2202        if (member == null || member.getKind() != ENUM_CONSTANT) {
2203            throw new IllegalArgumentException("must be an enum constant: " + member);
2204        }
2205        return member.getEnclosingElement().getEnclosedElements().indexOf(member);
2206    }
2207
2208    private Map<ModuleElement, Set<PackageElement>> modulePackageMap = null;
2209    public Map<ModuleElement, Set<PackageElement>> getModulePackageMap() {
2210        if (modulePackageMap == null) {
2211            modulePackageMap = new HashMap<>();
2212            Set<PackageElement> pkgs = configuration.getIncludedPackageElements();
2213            pkgs.forEach((pkg) -> {
2214                ModuleElement mod = elementUtils.getModuleOf(pkg);
2215                modulePackageMap.computeIfAbsent(mod, m -> new HashSet<>()).add(pkg);
2216            });
2217        }
2218        return modulePackageMap;
2219    }
2220
2221    public Map<ModuleElement, String> getDependentModules(ModuleElement mdle) {
2222        Map<ModuleElement, String> result = new TreeMap<>(makeModuleComparator());
2223        Deque<ModuleElement> queue = new ArrayDeque<>();
2224        // get all the requires for the element in question
2225        for (RequiresDirective rd : ElementFilter.requiresIn(mdle.getDirectives())) {
2226            ModuleElement dep = rd.getDependency();
2227            // add the dependency to work queue
2228            if (!result.containsKey(dep)) {
2229                if (rd.isTransitive()) {
2230                    queue.addLast(dep);
2231                }
2232            }
2233            // add all exports for the primary module
2234            result.put(rd.getDependency(), getModifiers(rd));
2235        }
2236
2237        // add only requires public for subsequent module dependencies
2238        for (ModuleElement m = queue.poll(); m != null; m = queue.poll()) {
2239            for (RequiresDirective rd : ElementFilter.requiresIn(m.getDirectives())) {
2240                ModuleElement dep = rd.getDependency();
2241                if (!result.containsKey(dep)) {
2242                    if (rd.isTransitive()) {
2243                        result.put(dep, getModifiers(rd));
2244                        queue.addLast(dep);
2245                    }
2246                }
2247            }
2248        }
2249        return result;
2250    }
2251
2252    public String getModifiers(RequiresDirective rd) {
2253        StringBuilder modifiers = new StringBuilder();
2254        String sep="";
2255        if (rd.isTransitive()) {
2256            modifiers.append("transitive");
2257            sep = " ";
2258        }
2259        if (rd.isStatic()) {
2260            modifiers.append(sep);
2261            modifiers.append("static");
2262        }
2263        return (modifiers.length() == 0) ? " " : modifiers.toString();
2264    }
2265
2266    public long getLineNumber(Element e) {
2267        TreePath path = getTreePath(e);
2268        if (path == null) { // maybe null if synthesized
2269            TypeElement encl = getEnclosingTypeElement(e);
2270            path = getTreePath(encl);
2271        }
2272        CompilationUnitTree cu = path.getCompilationUnit();
2273        LineMap lineMap = cu.getLineMap();
2274        DocSourcePositions spos = docTrees.getSourcePositions();
2275        long pos = spos.getStartPosition(cu, path.getLeaf());
2276        return lineMap.getLineNumber(pos);
2277    }
2278
2279    public List<ExecutableElement> convertToExecutableElement(List<Element> list) {
2280        List<ExecutableElement> out = new ArrayList<>(list.size());
2281        for (Element e : list) {
2282            out.add((ExecutableElement)e);
2283        }
2284        return out;
2285    }
2286
2287    public List<TypeElement> convertToTypeElement(List<Element> list) {
2288        List<TypeElement> out = new ArrayList<>(list.size());
2289        for (Element e : list) {
2290            out.add((TypeElement)e);
2291        }
2292        return out;
2293    }
2294
2295    public List<VariableElement> convertToVariableElement(List<Element> list) {
2296        List<VariableElement> out = new ArrayList<>(list.size());
2297        for (Element e : list) {
2298            out.add((VariableElement) e);
2299        }
2300        return out;
2301    }
2302
2303    public List<TypeElement> getInterfaces(Element e)  {
2304        return convertToTypeElement(getItems(e, true, INTERFACE));
2305    }
2306
2307    public List<TypeElement> getInterfacesUnfiltered(Element e)  {
2308        return convertToTypeElement(getItems(e, false, INTERFACE));
2309    }
2310
2311    List<Element> getNestedClasses(TypeElement e) {
2312        List<Element> result = new ArrayList<>();
2313        recursiveGetItems(result, e, true, CLASS);
2314        return result;
2315    }
2316
2317    List<Element> getNestedClassesUnfiltered(TypeElement e) {
2318        List<Element> result = new ArrayList<>();
2319        recursiveGetItems(result, e, false, CLASS);
2320        return result;
2321    }
2322
2323    public List<Element> getEnumConstants(Element e) {
2324        return getItems(e, true, ENUM_CONSTANT);
2325    }
2326
2327    public List<TypeElement> getEnums(Element e) {
2328        return convertToTypeElement(getItems(e, true, ENUM));
2329    }
2330
2331    public List<TypeElement> getEnumsUnfiltered(Element e) {
2332        return convertToTypeElement(getItems(e, false, ENUM));
2333    }
2334
2335    public SortedSet<TypeElement> getAllClassesUnfiltered(Element e) {
2336        List<TypeElement> clist = getClassesUnfiltered(e);
2337        clist.addAll(getInterfacesUnfiltered(e));
2338        clist.addAll(getAnnotationTypesUnfiltered(e));
2339        SortedSet<TypeElement> oset = new TreeSet<>(makeGeneralPurposeComparator());
2340        oset.addAll(clist);
2341        return oset;
2342    }
2343
2344    private final HashMap<Element, SortedSet<TypeElement>> cachedClasses = new HashMap<>();
2345    /**
2346     * Returns a list containing classes and interfaces,
2347     * including annotation types.
2348     * @param e Element
2349     * @return List
2350     */
2351    public SortedSet<TypeElement> getAllClasses(Element e) {
2352        SortedSet<TypeElement> oset = cachedClasses.get(e);
2353        if (oset != null)
2354            return oset;
2355        List<TypeElement> clist = getClasses(e);
2356        clist.addAll(getInterfaces(e));
2357        clist.addAll(getAnnotationTypes(e));
2358        clist.addAll(getEnums(e));
2359        oset = new TreeSet<>(makeGeneralPurposeComparator());
2360        oset.addAll(clist);
2361        cachedClasses.put(e, oset);
2362        return oset;
2363    }
2364
2365    /*
2366     * Get all the elements unfiltered and filter them finally based
2367     * on its visibility, this works differently from the other getters.
2368     */
2369    private List<TypeElement> getInnerClasses(Element e, boolean filter) {
2370        List<TypeElement> olist = new ArrayList<>();
2371        for (TypeElement te : getClassesUnfiltered(e)) {
2372            if (!filter || configuration.docEnv.isSelected(te)) {
2373                olist.add(te);
2374            }
2375        }
2376        for (TypeElement te : getInterfacesUnfiltered(e)) {
2377            if (!filter || configuration.docEnv.isSelected(te)) {
2378                olist.add(te);
2379            }
2380        }
2381        for (TypeElement te : getAnnotationTypesUnfiltered(e)) {
2382            if (!filter || configuration.docEnv.isSelected(te)) {
2383                olist.add(te);
2384            }
2385        }
2386        for (TypeElement te : getEnumsUnfiltered(e)) {
2387            if (!filter || configuration.docEnv.isSelected(te)) {
2388                olist.add(te);
2389            }
2390        }
2391        return olist;
2392    }
2393
2394    public List<TypeElement> getInnerClasses(Element e) {
2395        return getInnerClasses(e, true);
2396    }
2397
2398    public List<TypeElement> getInnerClassesUnfiltered(Element e) {
2399        return getInnerClasses(e, false);
2400    }
2401
2402    /**
2403     * Returns a list of classes that are not errors or exceptions
2404     * @param e Element
2405     * @return List
2406     */
2407    public List<TypeElement> getOrdinaryClasses(Element e) {
2408        return getClasses(e).stream()
2409                .filter(te -> (!isException(te) && !isError(te)))
2410                .collect(Collectors.toList());
2411    }
2412
2413    public List<TypeElement> getErrors(Element e) {
2414        return getClasses(e)
2415                .stream()
2416                .filter(this::isError)
2417                .collect(Collectors.toList());
2418    }
2419
2420    public List<TypeElement> getExceptions(Element e) {
2421        return getClasses(e)
2422                .stream()
2423                .filter(this::isException)
2424                .collect(Collectors.toList());
2425    }
2426
2427    List<Element> getItems(Element e, boolean filter, ElementKind select) {
2428        List<Element> elements = new ArrayList<>();
2429        // maintain backward compatibility by returning a null list, see AnnotationDocType.methods().
2430        if (configuration.backwardCompatibility && e.getKind() == ANNOTATION_TYPE)
2431            return elements;
2432        return new SimpleElementVisitor9<List<Element>, Void>() {
2433
2434            @Override
2435            public List<Element> visitPackage(PackageElement e, Void p) {
2436                recursiveGetItems(elements, e, filter, select);
2437                return elements;
2438            }
2439
2440            @Override
2441            protected List<Element> defaultAction(Element e0, Void p) {
2442                return getItems0(e0, filter, select);
2443            }
2444
2445        }.visit(e);
2446    }
2447
2448    EnumSet<ElementKind> nestedKinds = EnumSet.of(ANNOTATION_TYPE, CLASS, ENUM, INTERFACE);
2449
2450    void recursiveGetItems(Collection<Element> list, Element e, boolean filter, ElementKind... select) {
2451        list.addAll(getItems0(e, filter, select));
2452        List<Element> classes = getItems0(e, filter, nestedKinds);
2453        for (Element c : classes) {
2454            list.addAll(getItems0(c, filter, select));
2455            if (isTypeElement(c)) {
2456                recursiveGetItems(list, c, filter, select);
2457            }
2458        }
2459    }
2460
2461    private List<Element> getItems0(Element te, boolean filter, ElementKind... select) {
2462        EnumSet<ElementKind> kinds = EnumSet.copyOf(Arrays.asList(select));
2463        return getItems0(te, filter, kinds);
2464    }
2465
2466    private List<Element> getItems0(Element te, boolean filter, Set<ElementKind> kinds) {
2467        List<Element> elements = new ArrayList<>();
2468        for (Element e : te.getEnclosedElements()) {
2469            if (kinds.contains(e.getKind())) {
2470                if (!filter || shouldDocument(e)) {
2471                    elements.add(e);
2472                }
2473            }
2474        }
2475        return elements;
2476    }
2477
2478    private SimpleElementVisitor9<Boolean, Void> shouldDocumentVisitor = null;
2479    private boolean shouldDocument(Element e) {
2480        if (shouldDocumentVisitor == null) {
2481            shouldDocumentVisitor = new SimpleElementVisitor9<Boolean, Void>() {
2482                private boolean hasSource(TypeElement e) {
2483                    return configuration.docEnv.getFileKind(e) ==
2484                            javax.tools.JavaFileObject.Kind.SOURCE;
2485                }
2486
2487                // handle types
2488                @Override
2489                public Boolean visitType(TypeElement e, Void p) {
2490                    return configuration.docEnv.isSelected(e) && hasSource(e);
2491                }
2492
2493                // handle everything else
2494                @Override
2495                protected Boolean defaultAction(Element e, Void p) {
2496                    return configuration.docEnv.isSelected(e);
2497                }
2498
2499                @Override
2500                public Boolean visitUnknown(Element e, Void p) {
2501                    throw new AssertionError("unkown element: " + p);
2502                }
2503            };
2504        }
2505        return shouldDocumentVisitor.visit(e);
2506    }
2507
2508    /*
2509     * nameCache is maintained for improving the comparator
2510     * performance, noting that the Collator used by the comparators
2511     * use Strings, as of this writing.
2512     * TODO: when those APIs handle charSequences, the use of
2513     * this nameCache must be re-investigated and removed.
2514     */
2515    private final Map<Element, String> nameCache = new LinkedHashMap<>();
2516
2517    /**
2518     * Returns the name of the element after the last dot of the package name.
2519     * This emulates the behavior of the old doclet.
2520     * @param e an element whose name is required
2521     * @return the name
2522     */
2523    public String getSimpleName(Element e) {
2524        return nameCache.computeIfAbsent(e, this::getSimpleName0);
2525    }
2526
2527    private SimpleElementVisitor9<String, Void> snvisitor = null;
2528
2529    private String getSimpleName0(Element e) {
2530        if (snvisitor == null) {
2531            snvisitor = new SimpleElementVisitor9<String, Void>() {
2532                @Override
2533                public String visitModule(ModuleElement e, Void p) {
2534                    return e.getQualifiedName().toString();  // temp fix for 8182736
2535                }
2536
2537                @Override
2538                public String visitType(TypeElement e, Void p) {
2539                    StringBuilder sb = new StringBuilder(e.getSimpleName());
2540                    Element enclosed = e.getEnclosingElement();
2541                    while (enclosed != null
2542                            && (enclosed.getKind().isClass() || enclosed.getKind().isInterface())) {
2543                        sb.insert(0, enclosed.getSimpleName() + ".");
2544                        enclosed = enclosed.getEnclosingElement();
2545                    }
2546                    return sb.toString();
2547                }
2548
2549                @Override
2550                public String visitExecutable(ExecutableElement e, Void p) {
2551                    if (e.getKind() == CONSTRUCTOR || e.getKind() == STATIC_INIT) {
2552                        return e.getEnclosingElement().getSimpleName().toString();
2553                    }
2554                    return e.getSimpleName().toString();
2555                }
2556
2557                @Override
2558                protected String defaultAction(Element e, Void p) {
2559                    return e.getSimpleName().toString();
2560                }
2561            };
2562        }
2563        return snvisitor.visit(e);
2564    }
2565
2566    public TypeElement getEnclosingTypeElement(Element e) {
2567        if (e.getKind() == ElementKind.PACKAGE)
2568            return null;
2569        Element encl = e.getEnclosingElement();
2570        ElementKind kind = encl.getKind();
2571        if (kind == ElementKind.PACKAGE)
2572            return null;
2573        while (!(kind.isClass() || kind.isInterface())) {
2574            encl = encl.getEnclosingElement();
2575        }
2576        return (TypeElement)encl;
2577    }
2578
2579    private ConstantValueExpression cve = null;
2580
2581    public String constantValueExpresion(VariableElement ve) {
2582        if (cve == null)
2583            cve = new ConstantValueExpression();
2584        return cve.constantValueExpression(configuration.workArounds, ve);
2585    }
2586
2587    private static class ConstantValueExpression {
2588        public String constantValueExpression(WorkArounds workArounds, VariableElement ve) {
2589            return new TypeKindVisitor9<String, Object>() {
2590                /* TODO: we need to fix this correctly.
2591                 * we have a discrepancy here, note the use of getConstValue
2592                 * vs. getConstantValue, at some point we need to use
2593                 * getConstantValue.
2594                 * In the legacy world byte and char primitives appear as Integer values,
2595                 * thus a byte value of 127 will appear as 127, but in the new world,
2596                 * a byte value appears as Byte thus 0x7f will be printed, similarly
2597                 * chars will be  translated to \n, \r etc. however, in the new world,
2598                 * they will be printed as decimal values. The new world is correct,
2599                 * and we should fix this by using getConstantValue and the visitor to
2600                 * address this in the future.
2601                 */
2602                @Override
2603                public String visitPrimitiveAsBoolean(PrimitiveType t, Object val) {
2604                    return (int)val == 0 ? "false" : "true";
2605                }
2606
2607                @Override
2608                public String visitPrimitiveAsDouble(PrimitiveType t, Object val) {
2609                    return sourceForm(((Double)val), 'd');
2610                }
2611
2612                @Override
2613                public String visitPrimitiveAsFloat(PrimitiveType t, Object val) {
2614                    return sourceForm(((Float)val).doubleValue(), 'f');
2615                }
2616
2617                @Override
2618                public String visitPrimitiveAsLong(PrimitiveType t, Object val) {
2619                    return val + "L";
2620                }
2621
2622                @Override
2623                protected String defaultAction(TypeMirror e, Object val) {
2624                    if (val == null)
2625                        return null;
2626                    else if (val instanceof Character)
2627                        return sourceForm(((Character)val));
2628                    else if (val instanceof Byte)
2629                        return sourceForm(((Byte)val));
2630                    else if (val instanceof String)
2631                        return sourceForm((String)val);
2632                    return val.toString(); // covers int, short
2633                }
2634            }.visit(ve.asType(), workArounds.getConstValue(ve));
2635        }
2636
2637        // where
2638        private String sourceForm(double v, char suffix) {
2639            if (Double.isNaN(v))
2640                return "0" + suffix + "/0" + suffix;
2641            if (v == Double.POSITIVE_INFINITY)
2642                return "1" + suffix + "/0" + suffix;
2643            if (v == Double.NEGATIVE_INFINITY)
2644                return "-1" + suffix + "/0" + suffix;
2645            return v + (suffix == 'f' || suffix == 'F' ? "" + suffix : "");
2646        }
2647
2648        private  String sourceForm(char c) {
2649            StringBuilder buf = new StringBuilder(8);
2650            buf.append('\'');
2651            sourceChar(c, buf);
2652            buf.append('\'');
2653            return buf.toString();
2654        }
2655
2656        private String sourceForm(byte c) {
2657            return "0x" + Integer.toString(c & 0xff, 16);
2658        }
2659
2660        private String sourceForm(String s) {
2661            StringBuilder buf = new StringBuilder(s.length() + 5);
2662            buf.append('\"');
2663            for (int i=0; i<s.length(); i++) {
2664                char c = s.charAt(i);
2665                sourceChar(c, buf);
2666            }
2667            buf.append('\"');
2668            return buf.toString();
2669        }
2670
2671        private void sourceChar(char c, StringBuilder buf) {
2672            switch (c) {
2673            case '\b': buf.append("\\b"); return;
2674            case '\t': buf.append("\\t"); return;
2675            case '\n': buf.append("\\n"); return;
2676            case '\f': buf.append("\\f"); return;
2677            case '\r': buf.append("\\r"); return;
2678            case '\"': buf.append("\\\""); return;
2679            case '\'': buf.append("\\\'"); return;
2680            case '\\': buf.append("\\\\"); return;
2681            default:
2682                if (isPrintableAscii(c)) {
2683                    buf.append(c); return;
2684                }
2685                unicodeEscape(c, buf);
2686                return;
2687            }
2688        }
2689
2690        private void unicodeEscape(char c, StringBuilder buf) {
2691            final String chars = "0123456789abcdef";
2692            buf.append("\\u");
2693            buf.append(chars.charAt(15 & (c>>12)));
2694            buf.append(chars.charAt(15 & (c>>8)));
2695            buf.append(chars.charAt(15 & (c>>4)));
2696            buf.append(chars.charAt(15 & (c>>0)));
2697        }
2698        private boolean isPrintableAscii(char c) {
2699            return c >= ' ' && c <= '~';
2700        }
2701    }
2702
2703    public boolean isEnclosingPackageIncluded(TypeElement te) {
2704        return isIncluded(containingPackage(te));
2705    }
2706
2707    public boolean isIncluded(Element e) {
2708        return configuration.docEnv.isIncluded(e);
2709    }
2710
2711    private SimpleElementVisitor9<Boolean, Void> specifiedVisitor = null;
2712    public boolean isSpecified(Element e) {
2713        if (specifiedVisitor == null) {
2714            specifiedVisitor = new SimpleElementVisitor9<Boolean, Void>() {
2715                @Override
2716                public Boolean visitModule(ModuleElement e, Void p) {
2717                    return configuration.getSpecifiedModuleElements().contains(e);
2718                }
2719
2720                @Override
2721                public Boolean visitPackage(PackageElement e, Void p) {
2722                    return configuration.getSpecifiedPackageElements().contains(e);
2723                }
2724
2725                @Override
2726                public Boolean visitType(TypeElement e, Void p) {
2727                    return configuration.getSpecifiedTypeElements().contains(e);
2728                }
2729
2730                @Override
2731                protected Boolean defaultAction(Element e, Void p) {
2732                    return false;
2733                }
2734            };
2735        }
2736        return specifiedVisitor.visit(e);
2737    }
2738
2739    /**
2740     * package name, an unnamed package is returned as &lt;Unnamed&gt;
2741     * @param pkg
2742     * @return
2743     */
2744    public String getPackageName(PackageElement pkg) {
2745        if (pkg == null || pkg.isUnnamed()) {
2746            return DocletConstants.DEFAULT_PACKAGE_NAME;
2747        }
2748        return pkg.getQualifiedName().toString();
2749    }
2750
2751    public boolean isAttribute(DocTree doctree) {
2752        return isKind(doctree, ATTRIBUTE);
2753    }
2754
2755    public boolean isAuthor(DocTree doctree) {
2756        return isKind(doctree, AUTHOR);
2757    }
2758
2759    public boolean isComment(DocTree doctree) {
2760        return isKind(doctree, COMMENT);
2761    }
2762
2763    public boolean isDeprecated(DocTree doctree) {
2764        return isKind(doctree, DEPRECATED);
2765    }
2766
2767    public boolean isDocComment(DocTree doctree) {
2768        return isKind(doctree, DOC_COMMENT);
2769    }
2770
2771    public boolean isDocRoot(DocTree doctree) {
2772        return isKind(doctree, DOC_ROOT);
2773    }
2774
2775    public boolean isEndElement(DocTree doctree) {
2776        return isKind(doctree, END_ELEMENT);
2777    }
2778
2779    public boolean isEntity(DocTree doctree) {
2780        return isKind(doctree, ENTITY);
2781    }
2782
2783    public boolean isErroneous(DocTree doctree) {
2784        return isKind(doctree, ERRONEOUS);
2785    }
2786
2787    public boolean isException(DocTree doctree) {
2788        return isKind(doctree, EXCEPTION);
2789    }
2790
2791    public boolean isIdentifier(DocTree doctree) {
2792        return isKind(doctree, IDENTIFIER);
2793    }
2794
2795    public boolean isInheritDoc(DocTree doctree) {
2796        return isKind(doctree, INHERIT_DOC);
2797    }
2798
2799    public boolean isLink(DocTree doctree) {
2800        return isKind(doctree, LINK);
2801    }
2802
2803    public boolean isLinkPlain(DocTree doctree) {
2804        return isKind(doctree, LINK_PLAIN);
2805    }
2806
2807    public boolean isLiteral(DocTree doctree) {
2808        return isKind(doctree, LITERAL);
2809    }
2810
2811    public boolean isOther(DocTree doctree) {
2812        return doctree.getKind() == DocTree.Kind.OTHER;
2813    }
2814
2815    public boolean isParam(DocTree doctree) {
2816        return isKind(doctree, PARAM);
2817    }
2818
2819    public boolean isReference(DocTree doctree) {
2820        return isKind(doctree, REFERENCE);
2821    }
2822
2823    public boolean isReturn(DocTree doctree) {
2824        return isKind(doctree, RETURN);
2825    }
2826
2827    public boolean isSee(DocTree doctree) {
2828        return isKind(doctree, SEE);
2829    }
2830
2831    public boolean isSerial(DocTree doctree) {
2832        return isKind(doctree, SERIAL);
2833    }
2834
2835    public boolean isSerialData(DocTree doctree) {
2836        return isKind(doctree, SERIAL_DATA);
2837    }
2838
2839    public boolean isSerialField(DocTree doctree) {
2840        return isKind(doctree, SERIAL_FIELD);
2841    }
2842
2843    public boolean isSince(DocTree doctree) {
2844        return isKind(doctree, SINCE);
2845    }
2846
2847    public boolean isStartElement(DocTree doctree) {
2848        return isKind(doctree, START_ELEMENT);
2849    }
2850
2851    public boolean isText(DocTree doctree) {
2852        return isKind(doctree, TEXT);
2853    }
2854
2855    public boolean isThrows(DocTree doctree) {
2856        return isKind(doctree, THROWS);
2857    }
2858
2859    public boolean isUnknownBlockTag(DocTree doctree) {
2860        return isKind(doctree, UNKNOWN_BLOCK_TAG);
2861    }
2862
2863    public boolean isUnknownInlineTag(DocTree doctree) {
2864        return isKind(doctree, UNKNOWN_INLINE_TAG);
2865    }
2866
2867    public boolean isValue(DocTree doctree) {
2868        return isKind(doctree, VALUE);
2869    }
2870
2871    public boolean isVersion(DocTree doctree) {
2872        return isKind(doctree, VERSION);
2873    }
2874
2875    private boolean isKind(DocTree doctree, DocTree.Kind match) {
2876        return  doctree.getKind() == match;
2877    }
2878
2879    private final WeakSoftHashMap wksMap = new WeakSoftHashMap(this);
2880
2881    public CommentHelper getCommentHelper(Element element) {
2882        return wksMap.computeIfAbsent(element);
2883    }
2884
2885    public void removeCommentHelper(Element element) {
2886        wksMap.remove(element);
2887    }
2888
2889    public List<? extends DocTree> filteredList(List<? extends DocTree> dlist, DocTree.Kind... select) {
2890        List<DocTree> list = new ArrayList<>(dlist.size());
2891        if (select == null)
2892            return dlist;
2893        for (DocTree dt : dlist) {
2894            if (dt.getKind() != ERRONEOUS) {
2895                for (DocTree.Kind kind : select) {
2896                    if (dt.getKind() == kind) {
2897                        list.add(dt);
2898                    }
2899                }
2900            }
2901        }
2902        return list;
2903    }
2904
2905    private List<? extends DocTree> getBlockTags0(Element element, DocTree.Kind... kinds) {
2906        DocCommentTree dcTree = getDocCommentTree(element);
2907        if (dcTree == null)
2908            return Collections.emptyList();
2909
2910        return filteredList(dcTree.getBlockTags(), kinds);
2911    }
2912
2913    public List<? extends DocTree> getBlockTags(Element element) {
2914        return getBlockTags0(element, (Kind[]) null);
2915    }
2916
2917    public List<? extends DocTree> getBlockTags(Element element, DocTree.Kind... kinds) {
2918        return getBlockTags0(element, kinds);
2919    }
2920
2921    public List<? extends DocTree> getBlockTags(Element element, String tagName) {
2922        DocTree.Kind kind = null;
2923        switch (tagName) {
2924            case "author":
2925            case "deprecated":
2926            case "hidden":
2927            case "param":
2928            case "return":
2929            case "see":
2930            case "serial":
2931            case "since":
2932            case "throws":
2933            case "exception":
2934            case "version":
2935                kind = DocTree.Kind.valueOf(tagName.toUpperCase());
2936                return getBlockTags(element, kind);
2937            case "serialData":
2938                kind = SERIAL_DATA;
2939                return getBlockTags(element, kind);
2940            case "serialField":
2941                kind = SERIAL_FIELD;
2942                return getBlockTags(element, kind);
2943            default:
2944                kind = DocTree.Kind.UNKNOWN_BLOCK_TAG;
2945                break;
2946        }
2947        List<? extends DocTree> blockTags = getBlockTags(element, kind);
2948        List<DocTree> out = new ArrayList<>();
2949        String tname = tagName.startsWith("@") ? tagName.substring(1) : tagName;
2950        CommentHelper ch = getCommentHelper(element);
2951        for (DocTree dt : blockTags) {
2952            if (ch.getTagName(dt).equals(tname)) {
2953                out.add(dt);
2954            }
2955        }
2956        return out;
2957    }
2958
2959    public boolean hasBlockTag(Element element, DocTree.Kind kind) {
2960        return hasBlockTag(element, kind, null);
2961    }
2962
2963    public boolean hasBlockTag(Element element, DocTree.Kind kind, final String tagName) {
2964        CommentHelper ch = getCommentHelper(element);
2965        String tname = tagName != null && tagName.startsWith("@")
2966                ? tagName.substring(1)
2967                : tagName;
2968        for (DocTree dt : getBlockTags(element, kind)) {
2969            if (dt.getKind() == kind) {
2970                if (tname == null || ch.getTagName(dt).equals(tname)) {
2971                    return true;
2972                }
2973            }
2974        }
2975        return false;
2976    }
2977
2978    /**
2979     * Gets a TreePath for an Element. Note this method is called very
2980     * frequently, care must be taken to ensure this method is lithe
2981     * and efficient.
2982     * @param e an Element
2983     * @return TreePath
2984     */
2985    public TreePath getTreePath(Element e) {
2986        DocCommentDuo duo = dcTreeCache.get(e);
2987        if (isValidDuo(duo) && duo.treePath != null) {
2988            return duo.treePath;
2989        }
2990        duo = configuration.cmtUtils.getSyntheticCommentDuo(e);
2991        if (isValidDuo(duo) && duo.treePath != null) {
2992            return duo.treePath;
2993        }
2994        Map<Element, TreePath> elementToTreePath = configuration.workArounds.getElementToTreePath();
2995        TreePath path = elementToTreePath.get(e);
2996        if (path != null || elementToTreePath.containsKey(e)) {
2997            // expedite the path and one that is a null
2998            return path;
2999        }
3000        return elementToTreePath.computeIfAbsent(e, docTrees::getPath);
3001    }
3002
3003    private final Map<Element, DocCommentDuo> dcTreeCache = new LinkedHashMap<>();
3004
3005    /**
3006     * Retrieves the doc comments for a given element.
3007     * @param element
3008     * @return DocCommentTree for the Element
3009     */
3010    public DocCommentTree getDocCommentTree0(Element element) {
3011
3012        DocCommentDuo duo = null;
3013
3014        ElementKind kind = element.getKind();
3015        if (kind == ElementKind.PACKAGE || kind == ElementKind.OTHER) {
3016            duo = dcTreeCache.get(element); // local cache
3017            if (!isValidDuo(duo) && kind == ElementKind.PACKAGE) {
3018                // package-info.java
3019                duo = getDocCommentTuple(element);
3020            }
3021            if (!isValidDuo(duo)) {
3022                // package.html or overview.html
3023                duo = configuration.cmtUtils.getHtmlCommentDuo(element); // html source
3024            }
3025        } else {
3026            duo = configuration.cmtUtils.getSyntheticCommentDuo(element);
3027            if (!isValidDuo(duo)) {
3028                duo = dcTreeCache.get(element); // local cache
3029            }
3030            if (!isValidDuo(duo)) {
3031                duo = getDocCommentTuple(element); // get the real mccoy
3032            }
3033        }
3034
3035        DocCommentTree docCommentTree = isValidDuo(duo) ? duo.dcTree : null;
3036        TreePath path = isValidDuo(duo) ? duo.treePath : null;
3037        if (!dcTreeCache.containsKey(element)) {
3038            if (docCommentTree != null && path != null) {
3039                if (!configuration.isAllowScriptInComments()) {
3040                    try {
3041                        javaScriptScanner.scan(docCommentTree, path, p -> {
3042                            throw new JavaScriptScanner.Fault();
3043                        });
3044                    } catch (JavaScriptScanner.Fault jsf) {
3045                        String text = configuration.getText("doclet.JavaScript_in_comment");
3046                        throw new UncheckedDocletException(new SimpleDocletException(text, jsf));
3047                    }
3048                }
3049                configuration.workArounds.runDocLint(path);
3050            }
3051            dcTreeCache.put(element, duo);
3052        }
3053        return docCommentTree;
3054    }
3055
3056    private DocCommentDuo getDocCommentTuple(Element element) {
3057        // prevent nasty things downstream with overview element
3058        if (element.getKind() != ElementKind.OTHER) {
3059            TreePath path = getTreePath(element);
3060            if (path != null) {
3061                DocCommentTree docCommentTree = docTrees.getDocCommentTree(path);
3062                return new DocCommentDuo(path, docCommentTree);
3063            }
3064        }
3065        return null;
3066    }
3067
3068    public void checkJavaScriptInOption(String name, String value) {
3069        if (!configuration.isAllowScriptInComments()) {
3070            DocCommentTree dct = configuration.cmtUtils.parse(
3071                    URI.create("option://" + name.replace("-", "")), "<body>" + value + "</body>");
3072            try {
3073                javaScriptScanner.scan(dct, null, p -> {
3074                    throw new JavaScriptScanner.Fault();
3075                });
3076            } catch (JavaScriptScanner.Fault jsf) {
3077                String text = configuration.getText("doclet.JavaScript_in_option", name);
3078                throw new UncheckedDocletException(new SimpleDocletException(text, jsf));
3079            }
3080        }
3081    }
3082
3083    boolean isValidDuo(DocCommentDuo duo) {
3084        return duo != null && duo.dcTree != null;
3085    }
3086
3087    public DocCommentTree getDocCommentTree(Element element) {
3088        CommentHelper ch = wksMap.get(element);
3089        if (ch != null) {
3090            return ch.dctree;
3091        }
3092        DocCommentTree dcTree = getDocCommentTree0(element);
3093        if (dcTree != null) {
3094            wksMap.put(element, new CommentHelper(configuration, element, getTreePath(element), dcTree));
3095        }
3096        return dcTree;
3097    }
3098
3099    public List<? extends DocTree> getFullBody(Element element) {
3100        DocCommentTree docCommentTree = getDocCommentTree(element);
3101            return (docCommentTree == null)
3102                    ? Collections.emptyList()
3103                    : docCommentTree.getFullBody();
3104    }
3105
3106    public List<? extends DocTree> getBody(Element element) {
3107        DocCommentTree docCommentTree = getDocCommentTree(element);
3108        return (docCommentTree == null)
3109                ? Collections.emptyList()
3110                : docCommentTree.getFullBody();
3111    }
3112
3113    public List<? extends DocTree> getDeprecatedTrees(Element element) {
3114        return getBlockTags(element, DEPRECATED);
3115    }
3116
3117    public List<? extends DocTree> getProvidesTrees(Element element) {
3118        return getBlockTags(element, PROVIDES);
3119    }
3120
3121    public List<? extends DocTree> getSeeTrees(Element element) {
3122        return getBlockTags(element, SEE);
3123    }
3124
3125    public List<? extends DocTree> getSerialTrees(Element element) {
3126        return getBlockTags(element, SERIAL);
3127    }
3128
3129    public List<? extends DocTree> getSerialFieldTrees(VariableElement field) {
3130        return getBlockTags(field, DocTree.Kind.SERIAL_FIELD);
3131    }
3132
3133    public List<? extends DocTree> getThrowsTrees(Element element) {
3134        return getBlockTags(element, DocTree.Kind.EXCEPTION, DocTree.Kind.THROWS);
3135    }
3136
3137    public List<? extends DocTree> getTypeParamTrees(Element element) {
3138        return getParamTrees(element, true);
3139    }
3140
3141    public List<? extends DocTree> getParamTrees(Element element) {
3142        return getParamTrees(element, false);
3143    }
3144
3145    private  List<? extends DocTree> getParamTrees(Element element, boolean isTypeParameters) {
3146        List<DocTree> out = new ArrayList<>();
3147        for (DocTree dt : getBlockTags(element, PARAM)) {
3148            ParamTree pt = (ParamTree) dt;
3149            if (pt.isTypeParameter() == isTypeParameters) {
3150                out.add(dt);
3151            }
3152        }
3153        return out;
3154    }
3155
3156    public  List<? extends DocTree> getReturnTrees(Element element) {
3157        List<DocTree> out = new ArrayList<>();
3158        for (DocTree dt : getBlockTags(element, RETURN)) {
3159            out.add(dt);
3160        }
3161        return out;
3162    }
3163
3164    public List<? extends DocTree> getUsesTrees(Element element) {
3165        return getBlockTags(element, USES);
3166    }
3167
3168    public List<? extends DocTree> getFirstSentenceTrees(Element element) {
3169        DocCommentTree dcTree = getDocCommentTree(element);
3170        if (dcTree == null) {
3171            return Collections.emptyList();
3172        }
3173        List<DocTree> out = new ArrayList<>();
3174        for (DocTree dt : dcTree.getFirstSentence()) {
3175            out.add(dt);
3176        }
3177        return out;
3178    }
3179
3180    public ModuleElement containingModule(Element e) {
3181        return elementUtils.getModuleOf(e);
3182    }
3183
3184    public PackageElement containingPackage(Element e) {
3185        return elementUtils.getPackageOf(e);
3186    }
3187
3188    public TypeElement getTopMostContainingTypeElement(Element e) {
3189        if (isPackage(e)) {
3190            return null;
3191        }
3192        TypeElement outer = getEnclosingTypeElement(e);
3193        if (outer == null)
3194            return (TypeElement)e;
3195        while (outer != null && outer.getNestingKind().isNested()) {
3196            outer = getEnclosingTypeElement(outer);
3197        }
3198        return outer;
3199    }
3200
3201    static class WeakSoftHashMap implements Map<Element, CommentHelper> {
3202
3203        private final WeakHashMap<Element, SoftReference<CommentHelper>> wkMap;
3204        private final Utils utils;
3205        public WeakSoftHashMap(Utils utils) {
3206            wkMap = new WeakHashMap<>();
3207            this.utils = utils;
3208        }
3209
3210        @Override
3211        public boolean containsKey(Object key) {
3212            return wkMap.containsKey(key);
3213        }
3214
3215        @Override
3216        public Collection<CommentHelper> values() {
3217            Set<CommentHelper> out = new LinkedHashSet<>();
3218            for (SoftReference<CommentHelper> v : wkMap.values()) {
3219                out.add(v.get());
3220            }
3221            return out;
3222        }
3223
3224        @Override
3225        public boolean containsValue(Object value) {
3226            return wkMap.containsValue(new SoftReference<>((CommentHelper)value));
3227        }
3228
3229        @Override
3230        public CommentHelper remove(Object key) {
3231            SoftReference<CommentHelper> value = wkMap.remove(key);
3232            return value == null ? null : value.get();
3233        }
3234
3235
3236        @Override
3237        public CommentHelper put(Element key, CommentHelper value) {
3238            SoftReference<CommentHelper> nvalue = wkMap.put(key, new SoftReference<>(value));
3239            return nvalue == null ? null : nvalue.get();
3240        }
3241
3242        @Override
3243        public CommentHelper get(Object key) {
3244            SoftReference<CommentHelper> value = wkMap.get(key);
3245            return value == null ? null : value.get();
3246        }
3247
3248        @Override
3249        public int size() {
3250            return wkMap.size();
3251        }
3252
3253        @Override
3254        public boolean isEmpty() {
3255            return wkMap.isEmpty();
3256        }
3257
3258        @Override
3259        public void clear() {
3260            wkMap.clear();
3261        }
3262
3263        public CommentHelper computeIfAbsent(Element key) {
3264            if (wkMap.containsKey(key)) {
3265                SoftReference<CommentHelper> value = wkMap.get(key);
3266                if (value != null) {
3267                    CommentHelper cvalue = value.get();
3268                    if (cvalue != null) {
3269                        return cvalue;
3270                    }
3271                }
3272            }
3273            CommentHelper newValue = new CommentHelper(utils.configuration, key, utils.getTreePath(key),
3274                    utils.getDocCommentTree(key));
3275            wkMap.put(key, new SoftReference<>(newValue));
3276            return newValue;
3277        }
3278
3279
3280        @Override
3281        public void putAll(Map<? extends Element, ? extends CommentHelper> map) {
3282            for (Map.Entry<? extends Element, ? extends CommentHelper> entry : map.entrySet()) {
3283                put(entry.getKey(), entry.getValue());
3284            }
3285        }
3286
3287        @Override
3288        public Set<Element> keySet() {
3289            return wkMap.keySet();
3290        }
3291
3292        @Override
3293        public Set<Entry<Element, CommentHelper>> entrySet() {
3294            Set<Entry<Element, CommentHelper>> out = new LinkedHashSet<>();
3295            for (Element e : wkMap.keySet()) {
3296                SimpleEntry<Element, CommentHelper> n = new SimpleEntry<>(e, get(e));
3297                out.add(n);
3298            }
3299            return out;
3300        }
3301    }
3302}
3303