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