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