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