Utils.java revision 3692:87b48a8fb3cf
1223328Sgavin/* 2223328Sgavin * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. 379971Sobrien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 479971Sobrien * 5223328Sgavin * This code is free software; you can redistribute it and/or modify it 679971Sobrien * under the terms of the GNU General Public License version 2 only, as 779971Sobrien * published by the Free Software Foundation. Oracle designates this 879971Sobrien * particular file as subject to the "Classpath" exception as provided 979971Sobrien * by Oracle in the LICENSE file that accompanied this code. 1079971Sobrien * 1179971Sobrien * This code is distributed in the hope that it will be useful, but WITHOUT 1279971Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1379971Sobrien * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1479971Sobrien * version 2 for more details (a copy is included in the LICENSE file that 1579971Sobrien * accompanied this code). 1679971Sobrien * 1779971Sobrien * You should have received a copy of the GNU General Public License version 1879971Sobrien * 2 along with this work; if not, write to the Free Software Foundation, 1979971Sobrien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2079971Sobrien * 2179971Sobrien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2279971Sobrien * or visit www.oracle.com if you need additional information or have any 2379971Sobrien * questions. 2479971Sobrien */ 2579971Sobrien 2679971Sobrienpackage jdk.javadoc.internal.doclets.toolkit.util; 2779971Sobrien 2879971Sobrienimport java.io.IOException; 2979971Sobrienimport java.lang.annotation.Documented; 3079971Sobrienimport java.lang.ref.SoftReference; 3179971Sobrienimport java.text.CollationKey; 3279971Sobrienimport java.text.Collator; 3379971Sobrienimport java.util.*; 3479971Sobrienimport java.util.AbstractMap.SimpleEntry; 3579971Sobrienimport java.util.Map.Entry; 3679971Sobrienimport java.util.stream.Collectors; 3779971Sobrien 3879971Sobrienimport javax.lang.model.SourceVersion; 3979971Sobrienimport javax.lang.model.element.AnnotationMirror; 4079971Sobrienimport javax.lang.model.element.Element; 4179971Sobrienimport javax.lang.model.element.ElementKind; 4279971Sobrienimport javax.lang.model.element.ExecutableElement; 4379971Sobrienimport javax.lang.model.element.Modifier; 4479971Sobrienimport javax.lang.model.element.ModuleElement; 45121966Smikehimport javax.lang.model.element.PackageElement; 4679971Sobrienimport javax.lang.model.element.TypeElement; 4779971Sobrienimport javax.lang.model.element.TypeParameterElement; 4879971Sobrienimport javax.lang.model.element.VariableElement; 4979971Sobrienimport javax.lang.model.type.ArrayType; 5079971Sobrienimport javax.lang.model.type.DeclaredType; 5179971Sobrienimport javax.lang.model.type.ErrorType; 5279971Sobrienimport javax.lang.model.type.ExecutableType; 5379971Sobrienimport javax.lang.model.type.NoType; 5479971Sobrienimport javax.lang.model.type.PrimitiveType; 5579971Sobrienimport javax.lang.model.type.TypeMirror; 5679971Sobrienimport javax.lang.model.util.ElementFilter; 5779971Sobrienimport javax.lang.model.util.ElementKindVisitor9; 5879971Sobrienimport javax.lang.model.util.Elements; 5979971Sobrienimport javax.lang.model.util.SimpleElementVisitor9; 6079971Sobrienimport javax.lang.model.util.SimpleTypeVisitor9; 6179971Sobrienimport javax.lang.model.util.TypeKindVisitor9; 6279971Sobrienimport javax.lang.model.util.Types; 6379971Sobrienimport javax.tools.FileObject; 6479971Sobrienimport javax.tools.JavaFileManager; 65146309Smikehimport javax.tools.JavaFileManager.Location; 6679971Sobrienimport javax.tools.StandardLocation; 6779971Sobrien 6879971Sobrienimport com.sun.source.doctree.DocCommentTree; 6979971Sobrienimport com.sun.source.doctree.DocTree; 7079971Sobrienimport com.sun.source.doctree.DocTree.Kind; 7179971Sobrienimport com.sun.source.doctree.ParamTree; 7279971Sobrienimport com.sun.source.doctree.SerialFieldTree; 7379971Sobrienimport com.sun.source.tree.CompilationUnitTree; 7479971Sobrienimport com.sun.source.tree.LineMap; 7579971Sobrienimport com.sun.source.util.DocSourcePositions; 7679971Sobrienimport com.sun.source.util.DocTrees; 77146309Smikehimport com.sun.source.util.TreePath; 7879971Sobrienimport jdk.javadoc.internal.doclets.toolkit.CommentUtils.DocCommentDuo; 7979971Sobrienimport jdk.javadoc.internal.doclets.toolkit.Configuration; 8079971Sobrienimport jdk.javadoc.internal.doclets.toolkit.DocletException; 8179971Sobrienimport jdk.javadoc.internal.doclets.toolkit.Messages; 8279971Sobrienimport jdk.javadoc.internal.doclets.toolkit.Resources; 8379971Sobrienimport jdk.javadoc.internal.doclets.toolkit.WorkArounds; 8479971Sobrien 8579971Sobrienimport static javax.lang.model.element.ElementKind.*; 8679971Sobrienimport static javax.lang.model.element.Modifier.*; 8779971Sobrienimport static javax.lang.model.type.TypeKind.*; 8879971Sobrien 8979971Sobrienimport static com.sun.source.doctree.DocTree.Kind.*; 9079971Sobrienimport static jdk.javadoc.internal.doclets.toolkit.builders.ConstantsSummaryBuilder.MAX_CONSTANT_VALUE_INDEX_LENGTH; 91223328Sgavin 92223328Sgavin 93223328Sgavin/** 94223328Sgavin * Utilities Class for Doclets. 95116424Smikeh * 96116424Smikeh * <p><b>This is NOT part of any supported API. 97223328Sgavin * If you write code that depends on this, you do so at your own risk. 98223328Sgavin * This code and its internal interfaces are subject to change or 99223328Sgavin * deletion without notice.</b> 100116424Smikeh * 101116424Smikeh * @author Atul M Dambalkar 102116424Smikeh * @author Jamie Ho 103116424Smikeh */ 104116424Smikehpublic class Utils { 105116424Smikeh public final Configuration configuration; 106223328Sgavin public final Messages messages; 107116424Smikeh public final DocTrees docTrees; 108116424Smikeh public final Elements elementUtils; 109116424Smikeh public final Types typeUtils; 11079971Sobrien 11179971Sobrien public Utils(Configuration c) { 11279971Sobrien configuration = c; 113116424Smikeh messages = configuration.getMessages(); 114116424Smikeh elementUtils = c.docEnv.getElementUtils(); 11579971Sobrien typeUtils = c.docEnv.getTypeUtils(); 116116424Smikeh docTrees = c.docEnv.getDocTrees(); 117116424Smikeh } 118116424Smikeh 119116424Smikeh // our own little symbol table 120116424Smikeh private HashMap<String, TypeMirror> symtab = new HashMap<>(); 121142129Smikeh 122116424Smikeh public TypeMirror getSymbol(String signature) { 123116424Smikeh TypeMirror type = symtab.get(signature); 124116424Smikeh if (type == null) { 125223328Sgavin TypeElement typeElement = elementUtils.getTypeElement(signature); 126116424Smikeh if (typeElement == null) 127116424Smikeh return null; 12879971Sobrien type = typeElement.asType(); 129223328Sgavin if (type == null) 130223328Sgavin return null; 13179971Sobrien symtab.put(signature, type); 13279971Sobrien } 13379971Sobrien return type; 13479971Sobrien } 13579971Sobrien 13679971Sobrien public TypeMirror getObjectType() { 13779971Sobrien return getSymbol("java.lang.Object"); 13879971Sobrien } 139223328Sgavin 14079971Sobrien public TypeMirror getExceptionType() { 14179971Sobrien return getSymbol("java.lang.Exception"); 14279971Sobrien } 143223328Sgavin 14479971Sobrien public TypeMirror getErrorType() { 14579971Sobrien return getSymbol("java.lang.Error"); 14698247Smikeh } 147223328Sgavin 148223328Sgavin public TypeMirror getSerializableType() { 149232779Sgavin return getSymbol("java.io.Serializable"); 150223328Sgavin } 15179971Sobrien 152223328Sgavin public TypeMirror getExternalizableType() { 153223328Sgavin return getSymbol("java.io.Externalizable"); 15498247Smikeh } 155223328Sgavin 15698247Smikeh public TypeMirror getIllegalArgumentExceptionType() { 15779971Sobrien return getSymbol("java.lang.IllegalArgumentException"); 158142129Smikeh } 159142129Smikeh 16079971Sobrien public TypeMirror getNullPointerExceptionType() { 16179971Sobrien return getSymbol("java.lang.NullPointerException"); 16279971Sobrien } 16379971Sobrien 16479971Sobrien public TypeMirror getDeprecatedType() { 16579971Sobrien return getSymbol("java.lang.Deprecated"); 16679971Sobrien } 16779971Sobrien 16879971Sobrien public TypeMirror getFunctionalInterface() { 16979971Sobrien return getSymbol("java.lang.FunctionalInterface"); 17079971Sobrien } 17179971Sobrien 17279971Sobrien /** 17379971Sobrien * Return array of class members whose documentation is to be generated. 17479971Sobrien * If the member is deprecated do not include such a member in the 17579971Sobrien * returned array. 17679971Sobrien * 17779971Sobrien * @param members Array of members to choose from. 17879971Sobrien * @return List List of eligible members for whom 17979971Sobrien * documentation is getting generated. 18079971Sobrien */ 18179971Sobrien public List<Element> excludeDeprecatedMembers(List<? extends Element> members) { 18279971Sobrien List<Element> excludeList = members.stream() 18379971Sobrien .filter((member) -> (!isDeprecated(member))) 18479971Sobrien .sorted(makeGeneralPurposeComparator()) 18579971Sobrien .collect(Collectors.<Element, List<Element>>toCollection(ArrayList::new)); 18679971Sobrien return excludeList; 18779971Sobrien } 18879971Sobrien 18979971Sobrien /** 19079971Sobrien * Search for the given method in the given class. 19179971Sobrien * 19279971Sobrien * @param te Class to search into. 193223328Sgavin * @param method Method to be searched. 19479971Sobrien * @return ExecutableElement Method found, null otherwise. 19579971Sobrien */ 196223328Sgavin public ExecutableElement findMethod(TypeElement te, ExecutableElement method) { 19779971Sobrien for (Element m : getMethods(te)) { 19879971Sobrien if (executableMembersEqual(method, (ExecutableElement)m)) { 199223328Sgavin return (ExecutableElement)m; 200223328Sgavin } 20179971Sobrien } 20279971Sobrien return null; 20379971Sobrien } 204223328Sgavin 20595504Smikeh /** 206223328Sgavin * Test whether a class is a subclass of another class. 207223328Sgavin * 208223328Sgavin * @param t1 the candidate superclass. 20979971Sobrien * @param t2 the target 21098247Smikeh * @return true if t1 is a superclass of t2. 21198247Smikeh */ 21298247Smikeh public boolean isSubclassOf(TypeElement t1, TypeElement t2) { 21398247Smikeh return typeUtils.isSubtype(t1.asType(), t2.asType()); 21498247Smikeh } 215223328Sgavin 21679971Sobrien /** 21779971Sobrien * @param e1 the first method to compare. 21879971Sobrien * @param e2 the second method to compare. 21979971Sobrien * @return true if member1 overrides/hides or is overriden/hidden by member2. 22079971Sobrien */ 22179971Sobrien 22279971Sobrien public boolean executableMembersEqual(ExecutableElement e1, ExecutableElement e2) { 22379971Sobrien // TODO: investigate if Elements.hides(..) will work here. 22479971Sobrien if (isStatic(e1) && isStatic(e2)) { 22579971Sobrien List<? extends VariableElement> parameters1 = e1.getParameters(); 22679971Sobrien List<? extends VariableElement> parameters2 = e2.getParameters(); 22779971Sobrien if (e1.getSimpleName().equals(e2.getSimpleName()) && 22879971Sobrien parameters1.size() == parameters2.size()) { 22979971Sobrien int j; 23079971Sobrien for (j = 0 ; j < parameters1.size(); j++) { 23179971Sobrien VariableElement v1 = parameters1.get(j); 23279971Sobrien VariableElement v2 = parameters2.get(j); 233223328Sgavin String t1 = getTypeName(v1.asType(), true); 23479971Sobrien String t2 = getTypeName(v2.asType(), true); 23579971Sobrien if (!(t1.equals(t2) || 23698247Smikeh isTypeVariable(v1.asType()) || isTypeVariable(v2.asType()))) { 23779971Sobrien break; 23879971Sobrien } 23998247Smikeh } 24079971Sobrien if (j == parameters1.size()) { 24179971Sobrien return true; 24279971Sobrien } 24379971Sobrien } 24479971Sobrien return false; 24579971Sobrien } else { 24679971Sobrien return elementUtils.overrides(e1, e2, getEnclosingTypeElement(e1)) || 24779971Sobrien elementUtils.overrides(e2, e1, getEnclosingTypeElement(e2)) || 24879971Sobrien e1.equals(e2); 24979971Sobrien } 25079971Sobrien } 25179971Sobrien 25279971Sobrien /** 25379971Sobrien * According to 25479971Sobrien * <cite>The Java™ Language Specification</cite>, 25579971Sobrien * all the outer classes and static inner classes are core classes. 25679971Sobrien */ 25779971Sobrien public boolean isCoreClass(TypeElement e) { 25879971Sobrien return getEnclosingTypeElement(e) == null || isStatic(e); 25979971Sobrien } 26079971Sobrien 26179971Sobrien public boolean matches(Element e1, Element e2) { 26279971Sobrien if (isExecutableElement(e1) && isExecutableElement(e1)) { 26379971Sobrien return executableMembersEqual((ExecutableElement)e1, (ExecutableElement)e2); 26479971Sobrien } else { 26579971Sobrien return e1.getSimpleName().equals(e2.getSimpleName()); 26679971Sobrien } 26779971Sobrien } 26879971Sobrien 26979971Sobrien /** 27079971Sobrien * Copy doc-files directory and its contents from the source 27179971Sobrien * package directory to the generated documentation directory. 27279971Sobrien * For example, given a package java.lang, this method will copy 27379971Sobrien * the doc-files directory, found in the package directory to the 274223328Sgavin * generated documentation hierarchy. 27579971Sobrien * 27695504Smikeh * @param pe the package containing the doc files to be copied 27795504Smikeh * @throws DocFileIOException if there is a problem while copying 27895504Smikeh * the documentation files 27995504Smikeh */ 28095504Smikeh public void copyDocFiles(PackageElement pe) throws DocFileIOException { 28198247Smikeh Location sourceLoc = getLocationForPackage(pe); 28295504Smikeh copyDirectory(sourceLoc, DocPath.forPackage(pe).resolve(DocPaths.DOC_FILES)); 28398247Smikeh } 28498247Smikeh 28598247Smikeh /** 28695504Smikeh * Copy the given directory contents from the source package directory 28795504Smikeh * to the generated documentation directory. For example, given a package 28879971Sobrien * java.lang, this method will copy the entire directory, to the generated 28979971Sobrien * documentation hierarchy. 29079971Sobrien * 29179971Sobrien * @param pe the package containing the directory to be copied 29279971Sobrien * @param dir the directory to be copied 29379971Sobrien * @throws DocFileIOException if there is a problem while copying 29479971Sobrien * the documentation files 29579971Sobrien */ 29679971Sobrien public void copyDirectory(PackageElement pe, DocPath dir) throws DocFileIOException { 29779971Sobrien copyDirectory(getLocationForPackage(pe), dir); 29879971Sobrien } 299223328Sgavin 30079971Sobrien /** 30179971Sobrien * Copy the given directory and its contents from the source 30279971Sobrien * module directory to the generated documentation directory. 30379971Sobrien * For example, given a package java.lang, this method will 30479971Sobrien * copy the entire directory, to the generated documentation 30579971Sobrien * hierarchy. 30679971Sobrien * 30779971Sobrien * @param mdle the module containing the directory to be copied 30879971Sobrien * @param dir the directory to be copied 30979971Sobrien * @throws DocFileIOException if there is a problem while copying 31079971Sobrien * the documentation files 31179971Sobrien */ 31279971Sobrien public void copyDirectory(ModuleElement mdle, DocPath dir) throws DocFileIOException { 31379971Sobrien copyDirectory(getLocationForModule(mdle), dir); 31479971Sobrien } 31579971Sobrien 31679971Sobrien /** 31779971Sobrien * Copy files from a doc path location to the output. 31879971Sobrien * 31979971Sobrien * @param locn the location from which to read files 32079971Sobrien * @param dir the directory to be copied 32179971Sobrien * @throws DocFileIOException if there is a problem 32279971Sobrien * copying the files 32379971Sobrien */ 32498247Smikeh public void copyDirectory(Location locn, DocPath dir) throws DocFileIOException { 32598247Smikeh boolean first = true; 32698247Smikeh for (DocFile f : DocFile.list(configuration, locn, dir)) { 32798247Smikeh if (!f.isDirectory()) { 32898247Smikeh continue; 32998247Smikeh } 33098247Smikeh DocFile srcdir = f; 33179971Sobrien DocFile destdir = DocFile.createFileForOutput(configuration, dir); 33279971Sobrien if (srcdir.isSameFile(destdir)) { 33379971Sobrien continue; 33479971Sobrien } 33579971Sobrien 33679971Sobrien for (DocFile srcfile: srcdir.list()) { 33779971Sobrien DocFile destfile = destdir.resolve(srcfile.getName()); 33879971Sobrien if (srcfile.isFile()) { 33979971Sobrien if (destfile.exists() && !first) { 34079971Sobrien messages.warning("doclet.Copy_Overwrite_warning", 34179971Sobrien srcfile.getPath(), destdir.getPath()); 34279971Sobrien } else { 34379971Sobrien messages.notice("doclet.Copying_File_0_To_Dir_1", 34479971Sobrien srcfile.getPath(), destdir.getPath()); 34579971Sobrien destfile.copyFile(srcfile); 346116424Smikeh } 347116424Smikeh } else if (srcfile.isDirectory()) { 348116424Smikeh if (configuration.copydocfilesubdirs 349223328Sgavin && !configuration.shouldExcludeDocFileDir(srcfile.getName())) { 350116424Smikeh copyDirectory(locn, dir.resolve(srcfile.getName())); 351116424Smikeh } 35279971Sobrien } 35379971Sobrien } 35479971Sobrien 355223328Sgavin first = false; 35679971Sobrien } 35779971Sobrien } 35879971Sobrien 35979971Sobrien protected Location getLocationForPackage(PackageElement pd) { 36079971Sobrien return getLocationForModule(configuration.docEnv.getElementUtils().getModuleOf(pd)); 36179971Sobrien } 362223328Sgavin 363223328Sgavin protected Location getLocationForModule(ModuleElement mdle) { 364223328Sgavin Location loc = configuration.workArounds.getLocationForModule(mdle); 365223328Sgavin if (loc != null) 36679971Sobrien return loc; 36779971Sobrien 36879971Sobrien JavaFileManager fm = configuration.docEnv.getJavaFileManager(); 36979971Sobrien return fm.hasLocation(StandardLocation.SOURCE_PATH) 37079971Sobrien ? StandardLocation.SOURCE_PATH 37179971Sobrien : StandardLocation.CLASS_PATH; 37279971Sobrien } 37379971Sobrien 374223328Sgavin public boolean isAnnotated(TypeMirror e) { 37579971Sobrien return !e.getAnnotationMirrors().isEmpty(); 37679971Sobrien } 37779971Sobrien 378223328Sgavin public boolean isAnnotated(Element e) { 379223328Sgavin return !e.getAnnotationMirrors().isEmpty(); 380223328Sgavin } 38179971Sobrien 38279971Sobrien public boolean isAnnotationType(Element e) { 38379971Sobrien return new SimpleElementVisitor9<Boolean, Void>() { 384223328Sgavin @Override 385223328Sgavin public Boolean visitExecutable(ExecutableElement e, Void p) { 38679971Sobrien return visit(e.getEnclosingElement()); 38779971Sobrien } 38879971Sobrien 38979971Sobrien @Override 39079971Sobrien public Boolean visitUnknown(Element e, Void p) { 39179971Sobrien return false; 39279971Sobrien } 39379971Sobrien 39479971Sobrien @Override 39579971Sobrien protected Boolean defaultAction(Element e, Void p) { 39679971Sobrien return e.getKind() == ANNOTATION_TYPE; 39779971Sobrien } 39879971Sobrien }.visit(e); 39979971Sobrien } 40079971Sobrien 40179971Sobrien /** 40279971Sobrien * An Enum implementation is almost identical, thus this method returns if 403223328Sgavin * this element represents a CLASS or an ENUM 40479971Sobrien * @param e element 40579971Sobrien * @return true if class or enum 40679971Sobrien */ 40779971Sobrien public boolean isClass(Element e) { 40879971Sobrien return e.getKind().isClass(); 40979971Sobrien } 41079971Sobrien 41179971Sobrien public boolean isConstructor(Element e) { 41279971Sobrien return e.getKind() == CONSTRUCTOR; 41379971Sobrien } 41479971Sobrien 41579971Sobrien public boolean isEnum(Element e) { 41679971Sobrien return e.getKind() == ENUM; 41779971Sobrien } 41879971Sobrien 41979971Sobrien boolean isEnumConstant(Element e) { 42079971Sobrien return e.getKind() == ENUM_CONSTANT; 42179971Sobrien } 42279971Sobrien 42379971Sobrien public boolean isField(Element e) { 42479971Sobrien return e.getKind() == FIELD; 42579971Sobrien } 42679971Sobrien 42779971Sobrien public boolean isInterface(Element e) { 42879971Sobrien return e.getKind() == INTERFACE; 42998247Smikeh } 430223328Sgavin 431223328Sgavin public boolean isMethod(Element e) { 432223328Sgavin return e.getKind() == METHOD; 433223328Sgavin } 434223328Sgavin 435223328Sgavin public boolean isModule(Element e) { 436223328Sgavin return e.getKind() == ElementKind.MODULE; 437223328Sgavin } 438223328Sgavin 439223328Sgavin public boolean isPackage(Element e) { 440223328Sgavin return e.getKind() == ElementKind.PACKAGE; 441223328Sgavin } 442223328Sgavin 443223328Sgavin public boolean isAbstract(Element e) { 444223328Sgavin return e.getModifiers().contains(Modifier.ABSTRACT); 445223328Sgavin } 44679971Sobrien 44798247Smikeh public boolean isDefault(Element e) { 44879971Sobrien return e.getModifiers().contains(Modifier.DEFAULT); 44998247Smikeh } 45098247Smikeh 45198247Smikeh public boolean isPackagePrivate(Element e) { 45298247Smikeh return !(isPublic(e) || isPrivate(e) || isProtected(e)); 45398247Smikeh } 454223328Sgavin 45598247Smikeh public boolean isPrivate(Element e) { 45679971Sobrien return e.getModifiers().contains(Modifier.PRIVATE); 45779971Sobrien } 45879971Sobrien 45979971Sobrien public boolean isProtected(Element e) { 46079971Sobrien return e.getModifiers().contains(Modifier.PROTECTED); 46179971Sobrien } 46298247Smikeh 463223328Sgavin public boolean isPublic(Element e) { 464223328Sgavin return e.getModifiers().contains(Modifier.PUBLIC); 46598247Smikeh } 46679971Sobrien 46798247Smikeh public boolean isProperty(String name) { 46898247Smikeh return configuration.javafx && name.endsWith("Property"); 469146309Smikeh } 47098247Smikeh 47198247Smikeh public String getPropertyName(String name) { 47298247Smikeh return isProperty(name) 47398247Smikeh ? name.substring(0, name.length() - "Property".length()) 47498247Smikeh : name; 47598247Smikeh } 476223328Sgavin 47779971Sobrien public String getPropertyLabel(String name) { 47879971Sobrien return name.substring(0, name.lastIndexOf("Property")); 47979971Sobrien } 48079971Sobrien 48179971Sobrien public boolean isOverviewElement(Element e) { 48279971Sobrien return e.getKind() == ElementKind.OTHER; 483121966Smikeh } 48479971Sobrien 48579971Sobrien public boolean isStatic(Element e) { 486223328Sgavin return e.getModifiers().contains(Modifier.STATIC); 48779971Sobrien } 48879971Sobrien 48979971Sobrien public boolean isSerializable(TypeElement e) { 49079971Sobrien return typeUtils.isSubtype(e.asType(), getSerializableType()); 49179971Sobrien } 49279971Sobrien 49379971Sobrien public boolean isExternalizable(TypeElement e) { 49479971Sobrien return typeUtils.isSubtype(e.asType(), getExternalizableType()); 49579971Sobrien } 49679971Sobrien 49779971Sobrien public SortedSet<VariableElement> serializableFields(TypeElement aclass) { 49879971Sobrien return configuration.workArounds.getSerializableFields(this, aclass); 49979971Sobrien } 50079971Sobrien 50179971Sobrien public SortedSet<ExecutableElement> serializationMethods(TypeElement aclass) { 50279971Sobrien return configuration.workArounds.getSerializationMethods(this, aclass); 50379971Sobrien } 50479971Sobrien 50579971Sobrien public boolean definesSerializableFields(TypeElement aclass) { 50679971Sobrien return configuration.workArounds.definesSerializableFields(this, aclass); 50779971Sobrien } 50879971Sobrien 50979971Sobrien public String modifiersToString(Element e, boolean trailingSpace) { 51079971Sobrien SortedSet<Modifier> set = new TreeSet<>(e.getModifiers()); 51179971Sobrien set.remove(Modifier.NATIVE); 51279971Sobrien set.remove(Modifier.STRICTFP); 51379971Sobrien set.remove(Modifier.SYNCHRONIZED); 51479971Sobrien 51579971Sobrien return new ElementKindVisitor9<String, SortedSet<Modifier>>() { 516142129Smikeh final StringBuilder sb = new StringBuilder(); 517142129Smikeh 518142129Smikeh void addVisibilityModifier(Set<Modifier> modifiers) { 519142129Smikeh if (modifiers.contains(PUBLIC)) { 520142129Smikeh sb.append("public").append(" "); 52179971Sobrien } else if (modifiers.contains(PROTECTED)) { 52279971Sobrien sb.append("protected").append(" "); 52379971Sobrien } else if (modifiers.contains(PRIVATE)) { 52479971Sobrien sb.append("private").append(" "); 52579971Sobrien } 526142129Smikeh } 52779971Sobrien 528223328Sgavin void addStatic(Set<Modifier> modifiers) { 529223328Sgavin if (modifiers.contains(STATIC)) { 53079971Sobrien sb.append("static").append(" "); 531142129Smikeh } 532142129Smikeh } 53379971Sobrien 53479971Sobrien void addModifers(Set<Modifier> modifiers) { 535223328Sgavin String s = set.stream().map(m -> m.toString()).collect(Collectors.joining(" ")); 53679971Sobrien sb.append(s); 53779971Sobrien if (!s.isEmpty()) 53879971Sobrien sb.append(" "); 53979971Sobrien } 540223328Sgavin 54179971Sobrien String finalString(String s) { 54279971Sobrien sb.append(s); 543223328Sgavin if (trailingSpace) { 544223328Sgavin if (sb.lastIndexOf(" ") == sb.length() - 1) { 54579971Sobrien return sb.toString(); 54679971Sobrien } else { 54779971Sobrien return sb.append(" ").toString(); 54879971Sobrien } 54979971Sobrien } else { 55079971Sobrien return sb.toString().trim(); 55179971Sobrien } 552223328Sgavin } 55379971Sobrien 55479971Sobrien @Override 55579971Sobrien public String visitTypeAsInterface(TypeElement e, SortedSet<Modifier> p) { 55679971Sobrien addVisibilityModifier(p); 55779971Sobrien addStatic(p); 558223328Sgavin return finalString("interface"); 559223328Sgavin } 56079971Sobrien 56179971Sobrien @Override 56279971Sobrien public String visitTypeAsEnum(TypeElement e, SortedSet<Modifier> p) { 56379971Sobrien addVisibilityModifier(p); 56479971Sobrien addStatic(p); 56579971Sobrien return finalString("enum"); 56679971Sobrien } 56779971Sobrien 56879971Sobrien @Override 56979971Sobrien public String visitTypeAsAnnotationType(TypeElement e, SortedSet<Modifier> p) { 57079971Sobrien addVisibilityModifier(p); 57179971Sobrien addStatic(p); 57279971Sobrien return finalString("@interface"); 57379971Sobrien } 57479971Sobrien 57579971Sobrien @Override 57679971Sobrien public String visitTypeAsClass(TypeElement e, SortedSet<Modifier> p) { 57779971Sobrien addModifers(p); 57879971Sobrien return finalString("class"); 57979971Sobrien } 58079971Sobrien 58179971Sobrien @Override 58279971Sobrien protected String defaultAction(Element e, SortedSet<Modifier> p) { 58379971Sobrien addModifers(p); 58479971Sobrien return sb.toString().trim(); 58579971Sobrien } 58679971Sobrien 58779971Sobrien }.visit(e, set); 58879971Sobrien } 58979971Sobrien 59079971Sobrien public boolean isFunctionalInterface(AnnotationMirror amirror) { 59179971Sobrien return amirror.getAnnotationType().equals(getFunctionalInterface()) && 59279971Sobrien configuration.docEnv.getSourceVersion() 593223328Sgavin .compareTo(SourceVersion.RELEASE_8) >= 0; 59479971Sobrien } 59579971Sobrien 596223328Sgavin public boolean isNoType(TypeMirror t) { 59779971Sobrien return t.getKind() == NONE; 59879971Sobrien } 59979971Sobrien 60079971Sobrien public boolean isOrdinaryClass(TypeElement te) { 601223328Sgavin if (isEnum(te) || isInterface(te) || isAnnotationType(te)) { 602223328Sgavin return false; 60379971Sobrien } 604223328Sgavin if (isError(te) || isException(te)) { 60579971Sobrien return false; 60679971Sobrien } 60779971Sobrien return true; 60879971Sobrien } 60979971Sobrien 61079971Sobrien public boolean isError(TypeElement te) { 61179971Sobrien if (isEnum(te) || isInterface(te) || isAnnotationType(te)) { 61279971Sobrien return false; 61379971Sobrien } 614223328Sgavin return typeUtils.isSubtype(te.asType(), getErrorType()); 61579971Sobrien } 61679971Sobrien 617223328Sgavin public boolean isException(TypeElement te) { 61879971Sobrien if (isEnum(te) || isInterface(te) || isAnnotationType(te)) { 61979971Sobrien return false; 62079971Sobrien } 62179971Sobrien return typeUtils.isSubtype(te.asType(), getExceptionType()); 622223328Sgavin } 623223328Sgavin 62479971Sobrien public boolean isPrimitive(TypeMirror t) { 625223328Sgavin return new SimpleTypeVisitor9<Boolean, Void>() { 62679971Sobrien 62779971Sobrien @Override 62879971Sobrien public Boolean visitNoType(NoType t, Void p) { 62979971Sobrien return t.getKind() == VOID; 63079971Sobrien } 63179971Sobrien @Override 63279971Sobrien public Boolean visitPrimitive(PrimitiveType t, Void p) { 63379971Sobrien return true; 63479971Sobrien } 63579971Sobrien @Override 63679971Sobrien public Boolean visitArray(ArrayType t, Void p) { 637223328Sgavin return visit(t.getComponentType()); 638223328Sgavin } 639223328Sgavin @Override 640223328Sgavin protected Boolean defaultAction(TypeMirror e, Void p) { 641223328Sgavin return false; 642223328Sgavin } 64379971Sobrien }.visit(t); 64479971Sobrien } 64579971Sobrien 64679971Sobrien public boolean isExecutableElement(Element e) { 64779971Sobrien ElementKind kind = e.getKind(); 64879971Sobrien switch (kind) { 64979971Sobrien case CONSTRUCTOR: case METHOD: case INSTANCE_INIT: 65079971Sobrien return true; 65179971Sobrien default: 65279971Sobrien return false; 65379971Sobrien } 654223328Sgavin } 655223328Sgavin 656223328Sgavin public boolean isVariableElement(Element e) { 657223328Sgavin ElementKind kind = e.getKind(); 658223328Sgavin switch(kind) { 65979971Sobrien case ENUM_CONSTANT: case EXCEPTION_PARAMETER: case FIELD: 66079971Sobrien case LOCAL_VARIABLE: case PARAMETER: 66179971Sobrien case RESOURCE_VARIABLE: 662223328Sgavin return true; 663223328Sgavin default: 664146309Smikeh return false; 665146309Smikeh } 666223328Sgavin } 667223328Sgavin 668223328Sgavin public boolean isTypeElement(Element e) { 669223328Sgavin switch (e.getKind()) { 67079971Sobrien case CLASS: case ENUM: case INTERFACE: case ANNOTATION_TYPE: 671223328Sgavin return true; 67279971Sobrien default: 67379971Sobrien return false; 67479971Sobrien } 67579971Sobrien } 67679971Sobrien 67779971Sobrien /** 678223328Sgavin * Get the signature. It is the parameter list, type is qualified. 679223328Sgavin * For instance, for a method {@code mymethod(String x, int y)}, 680146309Smikeh * it will return {@code(java.lang.String,int)}. 68179971Sobrien * @param e 68279971Sobrien * @return String 68379971Sobrien */ 68479971Sobrien public String signature(ExecutableElement e) { 685146309Smikeh return makeSignature(e, true); 686146309Smikeh } 687146309Smikeh 688146309Smikeh /** 689146309Smikeh * Get flat signature. All types are not qualified. 690146309Smikeh * Return a String, which is the flat signature of this member. 691146309Smikeh * It is the parameter list, type is not qualified. 692146309Smikeh * For instance, for a method {@code mymethod(String x, int y)}, 69379971Sobrien * it will return {@code (String, int)}. 69479971Sobrien */ 69579971Sobrien public String flatSignature(ExecutableElement e) { 69679971Sobrien return makeSignature(e, false); 69779971Sobrien } 69879971Sobrien 69979971Sobrien public String makeSignature(ExecutableElement e, boolean full) { 70079971Sobrien return makeSignature(e, full, false); 70179971Sobrien } 70279971Sobrien 70379971Sobrien public String makeSignature(ExecutableElement e, boolean full, boolean ignoreTypeParameters) { 70479971Sobrien StringBuilder result = new StringBuilder(); 70579971Sobrien result.append("("); 70679971Sobrien Iterator<? extends VariableElement> iterator = e.getParameters().iterator(); 70779971Sobrien while (iterator.hasNext()) { 70879971Sobrien VariableElement next = iterator.next(); 70979971Sobrien TypeMirror type = next.asType(); 71079971Sobrien result.append(getTypeSignature(type, full, ignoreTypeParameters)); 71179971Sobrien if (iterator.hasNext()) { 71279971Sobrien result.append(", "); 71379971Sobrien } 71479971Sobrien } 71579971Sobrien if (e.isVarArgs()) { 71679971Sobrien int len = result.length(); 71779971Sobrien result.replace(len - 2, len, "..."); 718219081Sbrucec } 71998247Smikeh result.append(")"); 72079971Sobrien return result.toString(); 72179971Sobrien } 72279971Sobrien 72379971Sobrien private String getTypeSignature(TypeMirror t, boolean qualifiedName, boolean noTypeParameters) { 72479971Sobrien return new SimpleTypeVisitor9<StringBuilder, Void>() { 72579971Sobrien final StringBuilder sb = new StringBuilder(); 72679971Sobrien 72779971Sobrien @Override 72879971Sobrien public StringBuilder visitArray(ArrayType t, Void p) { 729223328Sgavin TypeMirror componentType = t.getComponentType(); 730223328Sgavin visit(componentType); 73179971Sobrien sb.append("[]"); 73279971Sobrien return sb; 73379971Sobrien } 73479971Sobrien 73579971Sobrien @Override 73679971Sobrien public StringBuilder visitDeclared(DeclaredType t, Void p) { 73779971Sobrien Element e = t.asElement(); 73879971Sobrien sb.append(qualifiedName ? getFullyQualifiedName(e) : getSimpleName(e)); 73979971Sobrien List<? extends TypeMirror> typeArguments = t.getTypeArguments(); 74079971Sobrien if (typeArguments.isEmpty() || noTypeParameters) { 74179971Sobrien return sb; 74279971Sobrien } 74379971Sobrien sb.append("<"); 74479971Sobrien Iterator<? extends TypeMirror> iterator = typeArguments.iterator(); 74579971Sobrien while (iterator.hasNext()) { 74679971Sobrien TypeMirror ta = iterator.next(); 74779971Sobrien visit(ta); 74879971Sobrien if (iterator.hasNext()) { 74979971Sobrien sb.append(", "); 75079971Sobrien } 75179971Sobrien } 75279971Sobrien sb.append(">"); 75379971Sobrien return sb; 75479971Sobrien } 75579971Sobrien 75679971Sobrien @Override 75779971Sobrien public StringBuilder visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 75879971Sobrien Element e = t.asElement(); 75979971Sobrien sb.append(qualifiedName ? getFullyQualifiedName(e, false) : getSimpleName(e)); 76079971Sobrien return sb; 76179971Sobrien } 76279971Sobrien 76379971Sobrien @Override 76479971Sobrien public StringBuilder visitWildcard(javax.lang.model.type.WildcardType t, Void p) { 76579971Sobrien sb.append("?"); 76679971Sobrien TypeMirror upperBound = t.getExtendsBound(); 76779971Sobrien if (upperBound != null) { 76879971Sobrien sb.append(" extends "); 76979971Sobrien visit(upperBound); 77079971Sobrien } 77179971Sobrien TypeMirror superBound = t.getSuperBound(); 77279971Sobrien if (superBound != null) { 77379971Sobrien sb.append(" super "); 77479971Sobrien visit(superBound); 77579971Sobrien } 77679971Sobrien return sb; 77779971Sobrien } 77879971Sobrien 77979971Sobrien @Override 78079971Sobrien protected StringBuilder defaultAction(TypeMirror e, Void p) { 78179971Sobrien return sb.append(e); 78279971Sobrien } 78379971Sobrien }.visit(t).toString(); 78479971Sobrien } 78579971Sobrien 78679971Sobrien public boolean isArrayType(TypeMirror t) { 78779971Sobrien return t.getKind() == ARRAY; 78879971Sobrien } 789223328Sgavin 79079971Sobrien public boolean isDeclaredType(TypeMirror t) { 79179971Sobrien return t.getKind() == DECLARED; 79279971Sobrien } 79379971Sobrien 79479971Sobrien public boolean isErrorType(TypeMirror t) { 79579971Sobrien return t.getKind() == ERROR; 79679971Sobrien } 79779971Sobrien 79879971Sobrien public boolean isIntersectionType(TypeMirror t) { 79979971Sobrien return t.getKind() == INTERSECTION; 80079971Sobrien } 80179971Sobrien 80279971Sobrien public boolean isTypeParameterElement(Element e) { 80379971Sobrien return e.getKind() == TYPE_PARAMETER; 80479971Sobrien } 80579971Sobrien 80679971Sobrien public boolean isTypeVariable(TypeMirror t) { 80779971Sobrien return t.getKind() == TYPEVAR; 80879971Sobrien } 80979971Sobrien 81079971Sobrien public boolean isVoid(TypeMirror t) { 81179971Sobrien return t.getKind() == VOID; 81279971Sobrien } 81379971Sobrien 81479971Sobrien public boolean isWildCard(TypeMirror t) { 81579971Sobrien return t.getKind() == WILDCARD; 81679971Sobrien } 81779971Sobrien 81879971Sobrien public boolean ignoreBounds(TypeMirror bound) { 81979971Sobrien return bound.equals(getObjectType()) && !isAnnotated(bound); 82079971Sobrien } 82179971Sobrien 82279971Sobrien /* 82379971Sobrien * a direct port of TypeVariable.getBounds 824223328Sgavin */ 825223328Sgavin public List<? extends TypeMirror> getBounds(TypeParameterElement tpe) { 82679971Sobrien List<? extends TypeMirror> bounds = tpe.getBounds(); 82779971Sobrien if (!bounds.isEmpty()) { 82879971Sobrien TypeMirror upperBound = bounds.get(bounds.size() - 1); 82979971Sobrien if (ignoreBounds(upperBound)) { 83079971Sobrien return Collections.emptyList(); 83179971Sobrien } 83279971Sobrien } 83379971Sobrien return bounds; 83479971Sobrien } 83579971Sobrien 836223328Sgavin /** 83779971Sobrien * Returns the TypeMirror of the ExecutableElement for all methods, 83879971Sobrien * a null if constructor. 83979971Sobrien * @param ee the ExecutableElement 84079971Sobrien * @return 84179971Sobrien */ 84279971Sobrien public TypeMirror getReturnType(ExecutableElement ee) { 84379971Sobrien return ee.getKind() == CONSTRUCTOR ? null : ee.getReturnType(); 84479971Sobrien } 84579971Sobrien 84679971Sobrien /** 84779971Sobrien * Return the type containing the method that this method overrides. 84879971Sobrien * It may be a {@code TypeElement} or a {@code TypeParameterElement}. 84979971Sobrien */ 85079971Sobrien public TypeMirror overriddenType(ExecutableElement method) { 85179971Sobrien return configuration.workArounds.overriddenType(method); 85279971Sobrien } 85379971Sobrien 85479971Sobrien private TypeMirror getType(TypeMirror t) { 85579971Sobrien return (isNoType(t)) ? getObjectType() : t; 85679971Sobrien } 85779971Sobrien 85879971Sobrien public TypeMirror getSuperType(TypeElement te) { 85979971Sobrien TypeMirror t = te.getSuperclass(); 86079971Sobrien return getType(t); 86179971Sobrien } 86279971Sobrien 86379971Sobrien /** 86479971Sobrien * Return the class that originally defined the method that 86579971Sobrien * is overridden by the current definition, or null if no 86679971Sobrien * such class exists. 86779971Sobrien * 86879971Sobrien * @return a TypeElement representing the superclass that 86979971Sobrien * originally defined this method, null if this method does 87079971Sobrien * not override a definition in a superclass. 87179971Sobrien */ 87279971Sobrien public TypeElement overriddenClass(ExecutableElement ee) { 87379971Sobrien TypeMirror type = overriddenType(ee); 87479971Sobrien return (type != null) ? asTypeElement(type) : null; 87579971Sobrien } 87679971Sobrien 87779971Sobrien public ExecutableElement overriddenMethod(ExecutableElement method) { 87879971Sobrien if (isStatic(method)) { 87979971Sobrien return null; 88079971Sobrien } 88179971Sobrien final TypeElement origin = getEnclosingTypeElement(method); 88279971Sobrien for (TypeMirror t = getSuperType(origin); 88379971Sobrien t.getKind() == DECLARED; 88479971Sobrien t = getSuperType(asTypeElement(t))) { 88579971Sobrien TypeElement te = asTypeElement(t); 88679971Sobrien if (te == null) { 88779971Sobrien return null; 88879971Sobrien } 88979971Sobrien List<? extends Element> methods = te.getEnclosedElements(); 89079971Sobrien for (ExecutableElement ee : ElementFilter.methodsIn(methods)) { 89179971Sobrien if (elementUtils.overrides(method, ee, origin)) { 89279971Sobrien return ee; 89379971Sobrien } 89479971Sobrien } 89579971Sobrien if (t.equals(getObjectType())) 89679971Sobrien return null; 89779971Sobrien } 89879971Sobrien return null; 89979971Sobrien } 90079971Sobrien 90179971Sobrien public SortedSet<TypeElement> getTypeElementsAsSortedSet(Iterable<TypeElement> typeElements) { 90279971Sobrien SortedSet<TypeElement> set = new TreeSet<>(makeGeneralPurposeComparator()); 90379971Sobrien for (TypeElement te : typeElements) { 90479971Sobrien set.add(te); 90579971Sobrien } 90679971Sobrien return set; 90779971Sobrien } 90879971Sobrien 90979971Sobrien public List<? extends DocTree> getSerialDataTrees(ExecutableElement member) { 91079971Sobrien return getBlockTags(member, SERIAL_DATA); 91179971Sobrien } 91279971Sobrien 91379971Sobrien public FileObject getFileObject(TypeElement te) { 91479971Sobrien return docTrees.getPath(te).getCompilationUnit().getSourceFile(); 91579971Sobrien } 91679971Sobrien 91779971Sobrien public TypeMirror getDeclaredType(TypeElement enclosing, TypeMirror target) { 91879971Sobrien return getDeclaredType(Collections.emptyList(), enclosing, target); 91979971Sobrien } 92079971Sobrien 92179971Sobrien /** 92279971Sobrien * Finds the declaration of the enclosing's type parameter. 92379971Sobrien * 92479971Sobrien * @param values 92579971Sobrien * @param enclosing a TypeElement whose type arguments we desire 92679971Sobrien * @param target the TypeMirror of the type as described by the enclosing 92779971Sobrien * @return 92879971Sobrien */ 92979971Sobrien public TypeMirror getDeclaredType(Collection<TypeMirror> values, 93079971Sobrien TypeElement enclosing, TypeMirror target) { 93179971Sobrien TypeElement targetElement = asTypeElement(target); 93279971Sobrien List<? extends TypeParameterElement> targetTypeArgs = targetElement.getTypeParameters(); 93379971Sobrien if (targetTypeArgs.isEmpty()) { 93479971Sobrien return target; 93579971Sobrien } 93679971Sobrien 93779971Sobrien List<? extends TypeParameterElement> enclosingTypeArgs = enclosing.getTypeParameters(); 93879971Sobrien List<TypeMirror> targetTypeArgTypes = new ArrayList<>(targetTypeArgs.size()); 93979971Sobrien 94079971Sobrien if (enclosingTypeArgs.isEmpty()) { 94179971Sobrien for (TypeMirror te : values) { 94279971Sobrien List<? extends TypeMirror> typeArguments = ((DeclaredType)te).getTypeArguments(); 94379971Sobrien if (typeArguments.size() >= targetTypeArgs.size()) { 94479971Sobrien for (int i = 0 ; i < targetTypeArgs.size(); i++) { 94579971Sobrien targetTypeArgTypes.add(typeArguments.get(i)); 94679971Sobrien } 94779971Sobrien break; 94879971Sobrien } 94979971Sobrien } 95079971Sobrien // we found no matches in the hierarchy 95179971Sobrien if (targetTypeArgTypes.isEmpty()) { 95279971Sobrien return target; 95379971Sobrien } 95479971Sobrien } else { 95579971Sobrien if (targetTypeArgs.size() > enclosingTypeArgs.size()) { 95679971Sobrien return target; 95779971Sobrien } 95879971Sobrien for (int i = 0; i < targetTypeArgs.size(); i++) { 959223328Sgavin TypeParameterElement tpe = enclosingTypeArgs.get(i); 960223328Sgavin targetTypeArgTypes.add(tpe.asType()); 96179971Sobrien } 96279971Sobrien } 96379971Sobrien TypeMirror dt = typeUtils.getDeclaredType(targetElement, 96479971Sobrien targetTypeArgTypes.toArray(new TypeMirror[targetTypeArgTypes.size()])); 96579971Sobrien return dt; 966223328Sgavin } 96779971Sobrien 96879971Sobrien /** 96979971Sobrien * For the class return all implemented interfaces including the 97079971Sobrien * superinterfaces of the implementing interfaces, also iterate over for 97179971Sobrien * all the superclasses. For interface return all the extended interfaces 972223328Sgavin * as well as superinterfaces for those extended interfaces. 97379971Sobrien * 97479971Sobrien * @param te the class to get the interfaces for 97579971Sobrien * @return List of all the required interfaces. 97679971Sobrien */ 97779971Sobrien public Set<TypeMirror> getAllInterfaces(TypeElement te) { 978223328Sgavin Set<TypeMirror> results = new LinkedHashSet<>(); 97979971Sobrien 980223328Sgavin List<? extends TypeMirror> interfaceTypes = te.getInterfaces(); 98179971Sobrien 98279971Sobrien for (TypeMirror interfaceType : interfaceTypes) { 98379971Sobrien TypeElement intfc = asTypeElement(interfaceType); 98479971Sobrien 98579971Sobrien if (isPublic(intfc) || isLinkable(intfc)) { 98679971Sobrien results.add(interfaceType); 98779971Sobrien TypeElement klass = asTypeElement(interfaceType); 988223328Sgavin for (TypeMirror t : getAllInterfaces(klass)) { 98979971Sobrien t = getDeclaredType(results, te, t); 99079971Sobrien results.add(t); 99179971Sobrien } 99279971Sobrien } 99379971Sobrien } 99479971Sobrien // TypeMirror contains the modified TypeParameterElement's types represented 99579971Sobrien // in the local Class'es elements types. ex: Foo<E> implements Bar<V> and the 99679971Sobrien // class being considered is Foo then TypeParameters will be represented as <E> 99779971Sobrien // note that any conversion might revert back to the old signature. For this 99879971Sobrien // very reason we get the superType, and find its interfaces. 99979971Sobrien TypeMirror superType = getSuperType(te); 1000223328Sgavin if (superType == getObjectType()) 1001223328Sgavin return results; 100279971Sobrien // Try walking the tree 100379971Sobrien addAllInterfaceTypes(results, te, superType, 100479971Sobrien configuration.workArounds.interfaceTypesOf(superType)); 100579971Sobrien return results; 100679971Sobrien } 100779971Sobrien 100879971Sobrien private void findAllInterfaceTypes(Set<TypeMirror> results, final TypeElement baseClass, 100979971Sobrien TypeMirror p) { 101079971Sobrien TypeMirror superType = getSuperType(asTypeElement(p)); 101179971Sobrien if (superType == p) { 101279971Sobrien return; 101379971Sobrien } 101479971Sobrien addAllInterfaceTypes(results, baseClass, superType, 101579971Sobrien configuration.workArounds.interfaceTypesOf(superType)); 101679971Sobrien } 101779971Sobrien 101879971Sobrien private void addAllInterfaceTypes(Set<TypeMirror> results, 101979971Sobrien final TypeElement baseClass, TypeMirror type, 102079971Sobrien List<TypeMirror> interfaceTypes) { 102179971Sobrien for (TypeMirror interfaceType : interfaceTypes) { 102279971Sobrien TypeElement iElement = asTypeElement(interfaceType); 102379971Sobrien if (isPublic(iElement) && isLinkable(iElement)) { 102479971Sobrien interfaceType = getDeclaredType(results, baseClass, interfaceType); 102579971Sobrien results.add(interfaceType); 102679971Sobrien Set<TypeMirror> superInterfaces = getAllInterfaces(iElement); 102779971Sobrien for (TypeMirror superInterface : superInterfaces) { 102879971Sobrien superInterface = getDeclaredType(results, baseClass, superInterface); 102979971Sobrien results.add(superInterface); 103079971Sobrien } 1031223328Sgavin } 103279971Sobrien } 103379971Sobrien findAllInterfaceTypes(results, baseClass, type); 103479971Sobrien } 1035223328Sgavin 103679971Sobrien /** 103779971Sobrien * Lookup for a class within this package. 103879971Sobrien * 103979971Sobrien * @return TypeElement of found class, or null if not found. 1040223328Sgavin */ 104179971Sobrien public TypeElement findClassInPackageElement(PackageElement pkg, String className) { 1042223328Sgavin for (TypeElement c : getAllClasses(pkg)) { 104379971Sobrien if (getSimpleName(c).equals(className)) { 104479971Sobrien return c; 104579971Sobrien } 104679971Sobrien } 104779971Sobrien return null; 104898247Smikeh } 104998247Smikeh 105079971Sobrien /** 1051223328Sgavin * TODO: FIXME: port to javax.lang.model 1052223328Sgavin * Find a class within the context of this class. Search order: qualified name, in this class 1053223328Sgavin * (inner), in this package, in the class imports, in the package imports. Return the 1054223328Sgavin * TypeElement if found, null if not found. 105579971Sobrien */ 1056116424Smikeh //### The specified search order is not the normal rule the 105779971Sobrien //### compiler would use. Leave as specified or change it? 105879971Sobrien public TypeElement findClass(Element element, String className) { 1059 TypeElement encl = getEnclosingTypeElement(element); 1060 TypeElement searchResult = configuration.workArounds.searchClass(encl, className); 1061 if (searchResult == null) { 1062 encl = getEnclosingTypeElement(encl); 1063 //Expand search space to include enclosing class. 1064 while (encl != null && getEnclosingTypeElement(encl) != null) { 1065 encl = getEnclosingTypeElement(encl); 1066 } 1067 searchResult = encl == null 1068 ? null 1069 : configuration.workArounds.searchClass(encl, className); 1070 } 1071 return searchResult; 1072 } 1073 1074 /** 1075 * Enclose in quotes, used for paths and filenames that contains spaces 1076 */ 1077 public String quote(String filepath) { 1078 return ("\"" + filepath + "\""); 1079 } 1080 1081 /** 1082 * Parse the package name. We only want to display package name up to 1083 * 2 levels. 1084 */ 1085 public String parsePackageName(PackageElement p) { 1086 String pkgname = p.isUnnamed() ? "" : getPackageName(p); 1087 int index = -1; 1088 for (int j = 0; j < MAX_CONSTANT_VALUE_INDEX_LENGTH; j++) { 1089 index = pkgname.indexOf(".", index + 1); 1090 } 1091 if (index != -1) { 1092 pkgname = pkgname.substring(0, index); 1093 } 1094 return pkgname; 1095 } 1096 1097 /** 1098 * Given a string, replace all occurrences of 'newStr' with 'oldStr'. 1099 * @param originalStr the string to modify. 1100 * @param oldStr the string to replace. 1101 * @param newStr the string to insert in place of the old string. 1102 */ 1103 public String replaceText(String originalStr, String oldStr, 1104 String newStr) { 1105 if (oldStr == null || newStr == null || oldStr.equals(newStr)) { 1106 return originalStr; 1107 } 1108 return originalStr.replace(oldStr, newStr); 1109 } 1110 1111 /** 1112 * Given an annotation, return true if it should be documented and false 1113 * otherwise. 1114 * 1115 * @param annotation the annotation to check. 1116 * 1117 * @return true return true if it should be documented and false otherwise. 1118 */ 1119 public boolean isDocumentedAnnotation(TypeElement annotation) { 1120 for (AnnotationMirror anno : annotation.getAnnotationMirrors()) { 1121 if (getFullyQualifiedName(anno.getAnnotationType().asElement()).equals( 1122 Documented.class.getName())) { 1123 return true; 1124 } 1125 } 1126 return false; 1127 } 1128 1129 /** 1130 * Return true if this class is linkable and false if we can't link to the 1131 * desired class. 1132 * <br> 1133 * <b>NOTE:</b> You can only link to external classes if they are public or 1134 * protected. 1135 * 1136 * @return true if this class is linkable and false if we can't link to the 1137 * desired class. 1138 */ 1139 public boolean isLinkable(TypeElement typeElem) { 1140 return 1141 (typeElem != null && 1142 (isIncluded(typeElem) && configuration.isGeneratedDoc(typeElem))) || 1143 (configuration.extern.isExternal(typeElem) && 1144 (isPublic(typeElem) || isProtected(typeElem))); 1145 } 1146 1147 List<TypeMirror> asErasureTypes(Collection<TypeElement> inList) { 1148 List<TypeMirror> out = new ArrayList<>(inList.size()); 1149 inList.stream().forEach((te) -> { 1150 out.add(typeUtils.erasure(te.asType())); 1151 }); 1152 return out; 1153 } 1154 1155 List<TypeMirror> asTypes(Collection<TypeElement> inList) { 1156 List<TypeMirror> out = new ArrayList<>(inList.size()); 1157 inList.stream().forEach((te) -> { 1158 out.add(te.asType()); 1159 }); 1160 return out; 1161 } 1162 1163 /** 1164 * Return this type as a {@code TypeElement} if it represents a class 1165 * interface or annotation. Array dimensions are ignored. 1166 * If this type {@code ParameterizedType} or {@code WildcardType}, return 1167 * the {@code TypeElement} of the type's erasure. If this is an 1168 * annotation, return this as a {@code TypeElement}. 1169 * If this is a primitive type, return null. 1170 * 1171 * @return the {@code TypeElement} of this type, 1172 * or null if it is a primitive type. 1173 */ 1174 public TypeElement asTypeElement(TypeMirror t) { 1175 return new SimpleTypeVisitor9<TypeElement, Void>() { 1176 1177 @Override 1178 public TypeElement visitDeclared(DeclaredType t, Void p) { 1179 return (TypeElement) t.asElement(); 1180 } 1181 1182 @Override 1183 public TypeElement visitArray(ArrayType t, Void p) { 1184 return visit(t.getComponentType()); 1185 } 1186 1187 @Override 1188 public TypeElement visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 1189 /* 1190 * TODO: Check with JJG. 1191 * if we have an annotated type @A $B T, then erasure returns a 1192 * none, in this case we use asElement instead. 1193 */ 1194 if (isAnnotated(t)) { 1195 return visit(typeUtils.asElement(t).asType()); 1196 } 1197 return visit(typeUtils.erasure(t)); 1198 } 1199 1200 @Override 1201 public TypeElement visitWildcard(javax.lang.model.type.WildcardType t, Void p) { 1202 return visit(typeUtils.erasure(t)); 1203 } 1204 1205 @Override 1206 public TypeElement visitError(ErrorType t, Void p) { 1207 return (TypeElement)t.asElement(); 1208 } 1209 1210 @Override 1211 protected TypeElement defaultAction(TypeMirror e, Void p) { 1212 return super.defaultAction(e, p); 1213 } 1214 }.visit(t); 1215 } 1216 1217 public TypeMirror getComponentType(TypeMirror t) { 1218 while (isArrayType(t)) { 1219 t = ((ArrayType) t).getComponentType(); 1220 } 1221 return t; 1222 } 1223 1224 /** 1225 * Return the type's dimension information, as a string. 1226 * <p> 1227 * For example, a two dimensional array of String returns "{@code [][]}". 1228 * 1229 * @return the type's dimension information as a string. 1230 */ 1231 public String getDimension(TypeMirror t) { 1232 return new SimpleTypeVisitor9<String, Void>() { 1233 StringBuilder dimension = new StringBuilder(""); 1234 @Override 1235 public String visitArray(ArrayType t, Void p) { 1236 dimension.append("[]"); 1237 return visit(t.getComponentType()); 1238 } 1239 1240 @Override 1241 protected String defaultAction(TypeMirror e, Void p) { 1242 return dimension.toString(); 1243 } 1244 1245 }.visit(t); 1246 } 1247 1248 public TypeElement getSuperClass(TypeElement te) { 1249 if (isInterface(te) || isAnnotationType(te) || 1250 te.asType().equals(getObjectType())) { 1251 return null; 1252 } 1253 TypeMirror superclass = te.getSuperclass(); 1254 if (isNoType(superclass) && isClass(te)) { 1255 superclass = getObjectType(); 1256 } 1257 return asTypeElement(superclass); 1258 } 1259 1260 public TypeElement getFirstVisibleSuperClassAsTypeElement(TypeElement te) { 1261 if (isAnnotationType(te) || isInterface(te) || 1262 te.asType().equals(getObjectType())) { 1263 return null; 1264 } 1265 TypeMirror firstVisibleSuperClass = getFirstVisibleSuperClass(te); 1266 return firstVisibleSuperClass == null ? null : asTypeElement(firstVisibleSuperClass); 1267 } 1268 1269 /** 1270 * Given a class, return the closest visible super class. 1271 * @param type the TypeMirror to be interrogated 1272 * @return the closest visible super class. Return null if it cannot 1273 * be found. 1274 */ 1275 1276 public TypeMirror getFirstVisibleSuperClass(TypeMirror type) { 1277 return getFirstVisibleSuperClass(asTypeElement(type)); 1278 } 1279 1280 1281 /** 1282 * Given a class, return the closest visible super class. 1283 * 1284 * @param te the TypeElement to be interrogated 1285 * @return the closest visible super class. Return null if it cannot 1286 * be found.. 1287 */ 1288 public TypeMirror getFirstVisibleSuperClass(TypeElement te) { 1289 TypeMirror superType = te.getSuperclass(); 1290 if (isNoType(superType)) { 1291 superType = getObjectType(); 1292 } 1293 TypeElement superClass = asTypeElement(superType); 1294 // skip "hidden" classes 1295 while ((superClass != null && isHidden(superClass)) 1296 || (superClass != null && !isPublic(superClass) && !isLinkable(superClass))) { 1297 TypeMirror supersuperType = superClass.getSuperclass(); 1298 TypeElement supersuperClass = asTypeElement(supersuperType); 1299 if (supersuperClass == null 1300 || supersuperClass.getQualifiedName().equals(superClass.getQualifiedName())) { 1301 break; 1302 } 1303 superType = supersuperType; 1304 superClass = supersuperClass; 1305 } 1306 if (te.asType().equals(superType)) { 1307 return null; 1308 } 1309 return superType; 1310 } 1311 1312 /** 1313 * Given a TypeElement, return the name of its type (Class, Interface, etc.). 1314 * 1315 * @param te the TypeElement to check. 1316 * @param lowerCaseOnly true if you want the name returned in lower case. 1317 * If false, the first letter of the name is capitalized. 1318 * @return 1319 */ 1320 1321 public String getTypeElementName(TypeElement te, boolean lowerCaseOnly) { 1322 String typeName = ""; 1323 if (isInterface(te)) { 1324 typeName = "doclet.Interface"; 1325 } else if (isException(te)) { 1326 typeName = "doclet.Exception"; 1327 } else if (isError(te)) { 1328 typeName = "doclet.Error"; 1329 } else if (isAnnotationType(te)) { 1330 typeName = "doclet.AnnotationType"; 1331 } else if (isEnum(te)) { 1332 typeName = "doclet.Enum"; 1333 } else if (isOrdinaryClass(te)) { 1334 typeName = "doclet.Class"; 1335 } 1336 typeName = lowerCaseOnly ? toLowerCase(typeName) : typeName; 1337 return typeNameMap.computeIfAbsent(typeName, configuration :: getText); 1338 } 1339 1340 private final Map<String, String> typeNameMap = new HashMap<>(); 1341 1342 public String getTypeName(TypeMirror t, boolean fullyQualified) { 1343 return new SimpleTypeVisitor9<String, Void>() { 1344 1345 @Override 1346 public String visitArray(ArrayType t, Void p) { 1347 return visit(t.getComponentType()); 1348 } 1349 1350 @Override 1351 public String visitDeclared(DeclaredType t, Void p) { 1352 TypeElement te = asTypeElement(t); 1353 return fullyQualified 1354 ? te.getQualifiedName().toString() 1355 : getSimpleName(te); 1356 } 1357 1358 @Override 1359 public String visitExecutable(ExecutableType t, Void p) { 1360 return t.toString(); 1361 } 1362 1363 @Override 1364 public String visitPrimitive(PrimitiveType t, Void p) { 1365 return t.toString(); 1366 } 1367 1368 @Override 1369 public String visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 1370 return getSimpleName(t.asElement()); 1371 } 1372 1373 @Override 1374 public String visitWildcard(javax.lang.model.type.WildcardType t, Void p) { 1375 return t.toString(); 1376 } 1377 1378 @Override 1379 protected String defaultAction(TypeMirror e, Void p) { 1380 return e.toString(); 1381 } 1382 }.visit(t); 1383 } 1384 1385 /** 1386 * Replace all tabs in a string with the appropriate number of spaces. 1387 * The string may be a multi-line string. 1388 * @param text the text for which the tabs should be expanded 1389 * @return the text with all tabs expanded 1390 */ 1391 public String replaceTabs(String text) { 1392 if (!text.contains("\t")) 1393 return text; 1394 1395 final int tabLength = configuration.sourcetab; 1396 final String whitespace = configuration.tabSpaces; 1397 final int textLength = text.length(); 1398 StringBuilder result = new StringBuilder(textLength); 1399 int pos = 0; 1400 int lineLength = 0; 1401 for (int i = 0; i < textLength; i++) { 1402 char ch = text.charAt(i); 1403 switch (ch) { 1404 case '\n': case '\r': 1405 lineLength = 0; 1406 break; 1407 case '\t': 1408 result.append(text, pos, i); 1409 int spaceCount = tabLength - lineLength % tabLength; 1410 result.append(whitespace, 0, spaceCount); 1411 lineLength += spaceCount; 1412 pos = i + 1; 1413 break; 1414 default: 1415 lineLength++; 1416 } 1417 } 1418 result.append(text, pos, textLength); 1419 return result.toString(); 1420 } 1421 1422 public CharSequence normalizeNewlines(CharSequence text) { 1423 StringBuilder sb = new StringBuilder(); 1424 final int textLength = text.length(); 1425 final String NL = DocletConstants.NL; 1426 int pos = 0; 1427 for (int i = 0; i < textLength; i++) { 1428 char ch = text.charAt(i); 1429 switch (ch) { 1430 case '\n': 1431 sb.append(text, pos, i); 1432 sb.append(NL); 1433 pos = i + 1; 1434 break; 1435 case '\r': 1436 sb.append(text, pos, i); 1437 sb.append(NL); 1438 if (i + 1 < textLength && text.charAt(i + 1) == '\n') 1439 i++; 1440 pos = i + 1; 1441 break; 1442 } 1443 } 1444 sb.append(text, pos, textLength); 1445 return sb; 1446 } 1447 1448 /** 1449 * The documentation for values() and valueOf() in Enums are set by the 1450 * doclet only iff the user or overridden methods are missing. 1451 * @param elem 1452 */ 1453 public void setEnumDocumentation(TypeElement elem) { 1454 for (Element e : getMethods(elem)) { 1455 ExecutableElement ee = (ExecutableElement)e; 1456 if (!getFullBody(e).isEmpty()) // ignore if already set 1457 continue; 1458 if (ee.getSimpleName().contentEquals("values") && ee.getParameters().isEmpty()) { 1459 configuration.cmtUtils.setEnumValuesTree(configuration, e); 1460 } 1461 if (ee.getSimpleName().contentEquals("valueOf") && ee.getParameters().size() == 1) { 1462 configuration.cmtUtils.setEnumValueOfTree(configuration, e); 1463 } 1464 } 1465 } 1466 1467 /** 1468 * Returns a locale independent lower cased String. That is, it 1469 * always uses US locale, this is a clone of the one in StringUtils. 1470 * @param s to convert 1471 * @return converted String 1472 */ 1473 public static String toLowerCase(String s) { 1474 return s.toLowerCase(Locale.US); 1475 } 1476 1477 /** 1478 * Return true if the given Element is deprecated. 1479 * 1480 * @param e the Element to check. 1481 * @return true if the given Element is deprecated. 1482 */ 1483 public boolean isDeprecated(Element e) { 1484 if (isPackage(e)) { 1485 return configuration.workArounds.isDeprecated0(e); 1486 } 1487 return elementUtils.isDeprecated(e); 1488 } 1489 1490 /** 1491 * A convenience method to get property name from the name of the 1492 * getter or setter method. 1493 * @param e the input method. 1494 * @return the name of the property of the given setter of getter. 1495 */ 1496 public String propertyName(ExecutableElement e) { 1497 String name = getSimpleName(e); 1498 String propertyName = null; 1499 if (name.startsWith("get") || name.startsWith("set")) { 1500 propertyName = name.substring(3); 1501 } else if (name.startsWith("is")) { 1502 propertyName = name.substring(2); 1503 } 1504 if ((propertyName == null) || propertyName.isEmpty()){ 1505 return ""; 1506 } 1507 return propertyName.substring(0, 1).toLowerCase(configuration.getLocale()) 1508 + propertyName.substring(1); 1509 } 1510 1511 /** 1512 * Returns true if the element is included, contains @hidden tag, 1513 * or if javafx flag is present and element contains @treatAsPrivate 1514 * tag. 1515 * @param e the queried element 1516 * @return true if it exists, false otherwise 1517 */ 1518 public boolean isHidden(Element e) { 1519 // prevent needless tests on elements which are not included 1520 if (!isIncluded(e)) { 1521 return false; 1522 } 1523 if (configuration.javafx && 1524 hasBlockTag(e, DocTree.Kind.UNKNOWN_BLOCK_TAG, "treatAsPrivate")) { 1525 return true; 1526 } 1527 return hasBlockTag(e, DocTree.Kind.HIDDEN); 1528 } 1529 1530 /** 1531 * In case of JavaFX mode on, filters out classes that are private, 1532 * package private, these are not documented in JavaFX mode, also 1533 * remove those classes that have @hidden or @treatAsPrivate comment tag. 1534 * 1535 * @param classlist a collection of TypeElements 1536 * @param javafx set to true if in JavaFX mode. 1537 * @return list of filtered classes. 1538 */ 1539 public SortedSet<TypeElement> filterOutPrivateClasses(Iterable<TypeElement> classlist, 1540 boolean javafx) { 1541 SortedSet<TypeElement> filteredOutClasses = 1542 new TreeSet<>(makeGeneralPurposeComparator()); 1543 if (!javafx) { 1544 for (Element te : classlist) { 1545 if (!isHidden(te)) { 1546 filteredOutClasses.add((TypeElement)te); 1547 } 1548 } 1549 return filteredOutClasses; 1550 } 1551 for (Element e : classlist) { 1552 if (isPrivate(e) || isPackagePrivate(e) || isHidden(e)) { 1553 continue; 1554 } 1555 filteredOutClasses.add((TypeElement)e); 1556 } 1557 return filteredOutClasses; 1558 } 1559 1560 /** 1561 * Compares two elements. 1562 * @param e1 first Element 1563 * @param e2 second Element 1564 * @return a true if they are the same, false otherwise. 1565 */ 1566 public boolean elementsEqual(Element e1, Element e2) { 1567 if (e1.getKind() != e2.getKind()) { 1568 return false; 1569 } 1570 String s1 = getSimpleName(e1); 1571 String s2 = getSimpleName(e2); 1572 if (compareStrings(s1, s2) == 0) { 1573 String f1 = getFullyQualifiedName(e1, true); 1574 String f2 = getFullyQualifiedName(e2, true); 1575 return compareStrings(f1, f2) == 0; 1576 } 1577 return false; 1578 } 1579 1580 /** 1581 * A general purpose case insensitive String comparator, which compares 1582 * two Strings using a Collator strength of "TERTIARY". 1583 * 1584 * @param s1 first String to compare. 1585 * @param s2 second String to compare. 1586 * @return a negative integer, zero, or a positive integer as the first 1587 * argument is less than, equal to, or greater than the second. 1588 */ 1589 public int compareStrings(String s1, String s2) { 1590 return compareStrings(true, s1, s2); 1591 } 1592 1593 /** 1594 * A general purpose case sensitive String comparator, which 1595 * compares two Strings using a Collator strength of "SECONDARY". 1596 * 1597 * @param s1 first String to compare. 1598 * @param s2 second String to compare. 1599 * @return a negative integer, zero, or a positive integer as the first 1600 * argument is less than, equal to, or greater than the second. 1601 */ 1602 public int compareCaseCompare(String s1, String s2) { 1603 return compareStrings(false, s1, s2); 1604 } 1605 1606 private DocCollator tertiaryCollator = null; 1607 private DocCollator secondaryCollator = null; 1608 1609 private int compareStrings(boolean caseSensitive, String s1, String s2) { 1610 if (caseSensitive) { 1611 if (tertiaryCollator == null) { 1612 tertiaryCollator = new DocCollator(configuration.locale, Collator.TERTIARY); 1613 } 1614 return tertiaryCollator.compare(s1, s2); 1615 } 1616 if (secondaryCollator == null) { 1617 secondaryCollator = new DocCollator(configuration.locale, Collator.SECONDARY); 1618 } 1619 return secondaryCollator.compare(s1, s2); 1620 } 1621 1622 private static class DocCollator { 1623 private final Map<String, CollationKey> keys; 1624 private final Collator instance; 1625 private final int MAX_SIZE = 1000; 1626 private DocCollator(Locale locale, int strength) { 1627 instance = Collator.getInstance(locale); 1628 instance.setStrength(strength); 1629 1630 keys = new LinkedHashMap<String, CollationKey>(MAX_SIZE + 1, 0.75f, true) { 1631 private static final long serialVersionUID = 1L; 1632 @Override 1633 protected boolean removeEldestEntry(Entry<String, CollationKey> eldest) { 1634 return size() > MAX_SIZE; 1635 } 1636 }; 1637 } 1638 1639 CollationKey getKey(String s) { 1640 return keys.computeIfAbsent(s, instance :: getCollationKey); 1641 } 1642 1643 public int compare(String s1, String s2) { 1644 return getKey(s1).compareTo(getKey(s2)); 1645 } 1646 } 1647 1648 /** 1649 * Comparator for ModuleElements, simply compares the fully qualified names 1650 * @return a Comparator 1651 */ 1652 public Comparator<Element> makeModuleComparator() { 1653 return new Utils.ElementComparator<Element>() { 1654 @Override 1655 public int compare(Element mod1, Element mod2) { 1656 return compareFullyQualifiedNames(mod1, mod2); 1657 } 1658 }; 1659 } 1660 1661 /** 1662 * Returns a Comparator for all classes, compares the simple names of 1663 * TypeElement, if equal then the fully qualified names. 1664 * 1665 * @return Comparator 1666 */ 1667 public Comparator<Element> makeAllClassesComparator() { 1668 return new Utils.ElementComparator<Element>() { 1669 @Override 1670 public int compare(Element e1, Element e2) { 1671 int result = compareNames(e1, e2); 1672 if (result == 0) 1673 result = compareFullyQualifiedNames(e1, e2); 1674 1675 return result; 1676 } 1677 }; 1678 } 1679 1680 /** 1681 * Returns a Comparator for packages, by comparing the fully qualified names. 1682 * 1683 * @return a Comparator 1684 */ 1685 public Comparator<Element> makePackageComparator() { 1686 return new Utils.ElementComparator<Element>() { 1687 @Override 1688 public int compare(Element pkg1, Element pkg2) { 1689 return compareFullyQualifiedNames(pkg1, pkg2); 1690 } 1691 }; 1692 } 1693 1694 /** 1695 * Returns a Comparator for SerialFieldTree. 1696 * @return a Comparator 1697 */ 1698 public Comparator<SerialFieldTree> makeSerialFieldTreeComparator() { 1699 return (SerialFieldTree o1, SerialFieldTree o2) -> { 1700 String s1 = o1.getName().toString(); 1701 String s2 = o2.getName().toString(); 1702 return s1.compareTo(s2); 1703 }; 1704 } 1705 1706 /** 1707 * Returns a general purpose comparator. 1708 * @return a Comparator 1709 */ 1710 public Comparator<Element> makeGeneralPurposeComparator() { 1711 return makeClassUseComparator(); 1712 } 1713 1714 /** 1715 * Returns a Comparator for overrides and implements, 1716 * used primarily on methods, compares the name first, 1717 * then compares the simple names of the enclosing 1718 * TypeElement and the fully qualified name of the enclosing TypeElement. 1719 * @return a Comparator 1720 */ 1721 public Comparator<Element> makeOverrideUseComparator() { 1722 return new Utils.ElementComparator<Element>() { 1723 @Override 1724 public int compare(Element o1, Element o2) { 1725 int result = compareStrings(getSimpleName(o1), getSimpleName(o2)); 1726 if (result != 0) { 1727 return result; 1728 } 1729 if (!isTypeElement(o1) && !isTypeElement(o2) && !isPackage(o1) && !isPackage(o2)) { 1730 TypeElement t1 = getEnclosingTypeElement(o1); 1731 TypeElement t2 = getEnclosingTypeElement(o2); 1732 result = compareStrings(getSimpleName(t1), getSimpleName(t2)); 1733 if (result != 0) 1734 return result; 1735 } 1736 result = compareStrings(getFullyQualifiedName(o1), getFullyQualifiedName(o2)); 1737 if (result != 0) 1738 return result; 1739 return compareElementTypeKinds(o1, o2); 1740 } 1741 }; 1742 } 1743 1744 /** 1745 * Returns a Comparator for index file presentations, and are sorted as follows. 1746 * If comparing modules then simply compare the simple names, 1747 * comparing packages then simply compare the qualified names, otherwise 1748 * 1. if equal, then compare the ElementKind ex: Module, Package, Interface etc. 1749 * 2. sort on simple names of entities 1750 * 3a. if equal and if the type is of ExecutableElement(Constructor, Methods), 1751 * a case insensitive comparison of parameter the type signatures 1752 * 3b. if equal, case sensitive comparison of the type signatures 1753 * 4. finally, if equal, compare the FQNs of the entities 1754 * Iff comparing packages then simply sort on qualified names. 1755 * @return a comparator for index file use 1756 */ 1757 public Comparator<Element> makeIndexUseComparator() { 1758 return new Utils.ElementComparator<Element>() { 1759 /** 1760 * Compare two given elements, if comparing two modules, return the 1761 * comparison of SimpleName, if comparing two packages, return the 1762 * comparison of FullyQualifiedName, first sort on kinds, then on the 1763 * names, then on the parameters only if the type is an ExecutableElement, 1764 * the parameters are compared and finally the qualified names. 1765 * 1766 * @param e1 - an element. 1767 * @param e2 - an element. 1768 * @return a negative integer, zero, or a positive integer as the first 1769 * argument is less than, equal to, or greater than the second. 1770 */ 1771 @Override 1772 public int compare(Element e1, Element e2) { 1773 int result = 0; 1774 if (isModule(e1) && isModule(e2)) { 1775 return compareNames(e1, e2); 1776 } 1777 if (isPackage(e1) && isPackage(e2)) { 1778 return compareFullyQualifiedNames(e1, e2); 1779 } 1780 result = compareElementTypeKinds(e1, e2); 1781 if (result != 0) { 1782 return result; 1783 } 1784 result = compareNames(e1, e2); 1785 if (result != 0) { 1786 return result; 1787 } 1788 if (hasParameters(e1)) { 1789 List<? extends VariableElement> parameters1 = ((ExecutableElement)e1).getParameters(); 1790 List<? extends VariableElement> parameters2 = ((ExecutableElement)e2).getParameters(); 1791 result = compareParameters(false, parameters1, parameters2); 1792 if (result != 0) { 1793 return result; 1794 } 1795 result = compareParameters(true, parameters1, parameters2); 1796 if (result != 0) { 1797 return result; 1798 } 1799 } 1800 return compareFullyQualifiedNames(e1, e2); 1801 } 1802 }; 1803 } 1804 1805 /** 1806 * Compares the FullyQualifiedNames of two TypeMirrors 1807 * @return 1808 */ 1809 public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() { 1810 return (TypeMirror type1, TypeMirror type2) -> { 1811 String s1 = getQualifiedTypeName(type1); 1812 String s2 = getQualifiedTypeName(type2); 1813 return compareStrings(s1, s2); 1814 }; 1815 } 1816 1817 /** 1818 * Compares the SimpleNames of TypeMirrors if equal then the 1819 * FullyQualifiedNames of TypeMirrors. 1820 * 1821 * @return 1822 */ 1823 public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() { 1824 return (TypeMirror t1, TypeMirror t2) -> { 1825 int result = compareStrings(getTypeName(t1, false), getTypeName(t2, false)); 1826 if (result != 0) 1827 return result; 1828 return compareStrings(getQualifiedTypeName(t1), getQualifiedTypeName(t2)); 1829 }; 1830 } 1831 1832 /** 1833 * Get the qualified type name of a TypeMiror compatible with the Element's 1834 * getQualified name, returns the qualified name of the Reference type 1835 * otherwise the primitive name. 1836 * @param t the type whose name is to be obtained. 1837 * @return the fully qualified name of Reference type or the primitive name 1838 */ 1839 public String getQualifiedTypeName(TypeMirror t) { 1840 return new SimpleTypeVisitor9<String, Void>() { 1841 @Override 1842 public String visitDeclared(DeclaredType t, Void p) { 1843 return getFullyQualifiedName(t.asElement()); 1844 } 1845 1846 @Override 1847 public String visitArray(ArrayType t, Void p) { 1848 return visit(t.getComponentType()); 1849 } 1850 1851 @Override 1852 public String visitPrimitive(PrimitiveType t, Void p) { 1853 return t.toString(); 1854 } 1855 1856 @Override 1857 public String visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 1858 // The knee jerk reaction is to do this but don't!, as we would like 1859 // it to be compatible with the old world, now if we decide to do so 1860 // care must be taken to avoid collisions. 1861 // return getFullyQualifiedName(t.asElement()); 1862 return t.toString(); 1863 } 1864 1865 @Override 1866 protected String defaultAction(TypeMirror e, Void p) { 1867 throw new UnsupportedOperationException("should not happen"); 1868 } 1869 }.visit(t); 1870 } 1871 1872 /** 1873 * A generic utility which returns the fully qualified names of an entity, 1874 * if the entity is not qualifiable then its enclosing entity, it is upto 1875 * the caller to add the elements name as required. 1876 * @param e the element to get FQN for. 1877 * @return the name 1878 */ 1879 public String getFullyQualifiedName(Element e) { 1880 return getFullyQualifiedName(e, true); 1881 } 1882 1883 public String getFullyQualifiedName(Element e, final boolean outer) { 1884 return new SimpleElementVisitor9<String, Void>() { 1885 @Override 1886 public String visitPackage(PackageElement e, Void p) { 1887 return e.getQualifiedName().toString(); 1888 } 1889 1890 @Override 1891 public String visitType(TypeElement e, Void p) { 1892 return e.getQualifiedName().toString(); 1893 } 1894 1895 @Override 1896 protected String defaultAction(Element e, Void p) { 1897 return outer ? visit(e.getEnclosingElement()) : e.getSimpleName().toString(); 1898 } 1899 }.visit(e); 1900 } 1901 1902 /** 1903 * Comparator for ClassUse presentations, and sorts as follows: 1904 * 1. member names 1905 * 2. then fully qualified member names 1906 * 3. then parameter types if applicable 1907 * 4. finally the element kinds ie. package, class, interface etc. 1908 * @return a comparator to sort classes and members for class use 1909 */ 1910 public Comparator<Element> makeClassUseComparator() { 1911 return new Utils.ElementComparator<Element>() { 1912 /** 1913 * Compare two Elements, first sort on simple name, and if 1914 * applicable on the fully qualified name, and finally if applicable 1915 * on the parameter types. 1916 * @param e1 - an element. 1917 * @param e2 - an element. 1918 * @return a negative integer, zero, or a positive integer as the first 1919 * argument is less than, equal to, or greater than the second. 1920 */ 1921 @Override 1922 public int compare(Element e1, Element e2) { 1923 int result = compareNames(e1, e2); 1924 if (result != 0) { 1925 return result; 1926 } 1927 result = compareFullyQualifiedNames(e1, e2); 1928 if (result != 0) { 1929 return result; 1930 } 1931 if (hasParameters(e1) && hasParameters(e2)) { 1932 @SuppressWarnings("unchecked") 1933 List<VariableElement> parameters1 = (List<VariableElement>) ((ExecutableElement)e1).getParameters(); 1934 @SuppressWarnings("unchecked") 1935 List<VariableElement> parameters2 = (List<VariableElement>) ((ExecutableElement)e2).getParameters(); 1936 result = compareParameters(false, parameters1, parameters2); 1937 if (result != 0) { 1938 return result; 1939 } 1940 result = compareParameters(true, parameters1, parameters2); 1941 } 1942 if (result != 0) { 1943 return result; 1944 } 1945 return compareElementTypeKinds(e1, e2); 1946 } 1947 }; 1948 } 1949 1950 /** 1951 * A general purpose comparator to sort Element entities, basically provides the building blocks 1952 * for creating specific comparators for an use-case. 1953 * @param <T> an Element 1954 */ 1955 private abstract class ElementComparator<T extends Element> implements Comparator<Element> { 1956 /** 1957 * compares two parameter arrays by first comparing the length of the arrays, and 1958 * then each Type of the parameter in the array. 1959 * @param params1 the first parameter array. 1960 * @param params2 the first parameter array. 1961 * @return a negative integer, zero, or a positive integer as the first 1962 * argument is less than, equal to, or greater than the second. 1963 */ 1964 final EnumMap<ElementKind, Integer> elementKindOrder; 1965 public ElementComparator() { 1966 elementKindOrder = new EnumMap<>(ElementKind.class); 1967 elementKindOrder.put(ElementKind.MODULE, 0); 1968 elementKindOrder.put(ElementKind.PACKAGE, 1); 1969 elementKindOrder.put(ElementKind.CLASS, 2); 1970 elementKindOrder.put(ElementKind.ENUM, 3); 1971 elementKindOrder.put(ElementKind.ENUM_CONSTANT, 4); 1972 elementKindOrder.put(ElementKind.INTERFACE, 5); 1973 elementKindOrder.put(ElementKind.ANNOTATION_TYPE, 6); 1974 elementKindOrder.put(ElementKind.FIELD, 7); 1975 elementKindOrder.put(ElementKind.CONSTRUCTOR, 8); 1976 elementKindOrder.put(ElementKind.METHOD, 9); 1977 } 1978 1979 protected int compareParameters(boolean caseSensitive, List<? extends VariableElement> params1, 1980 List<? extends VariableElement> params2) { 1981 1982 return compareStrings(caseSensitive, getParametersAsString(params1), 1983 getParametersAsString(params2)); 1984 } 1985 1986 String getParametersAsString(List<? extends VariableElement> params) { 1987 StringBuilder sb = new StringBuilder(); 1988 for (VariableElement param : params) { 1989 TypeMirror t = param.asType(); 1990 // prefix P for primitive and R for reference types, thus items will 1991 // be ordered lexically and correctly. 1992 sb.append(getTypeCode(t)).append("-").append(t).append("-"); 1993 } 1994 return sb.toString(); 1995 } 1996 1997 private String getTypeCode(TypeMirror t) { 1998 return new SimpleTypeVisitor9<String, Void>() { 1999 2000 @Override 2001 public String visitPrimitive(PrimitiveType t, Void p) { 2002 return "P"; 2003 } 2004 @Override 2005 public String visitArray(ArrayType t, Void p) { 2006 return visit(t.getComponentType()); 2007 } 2008 @Override 2009 protected String defaultAction(TypeMirror e, Void p) { 2010 return "R"; 2011 } 2012 2013 }.visit(t); 2014 } 2015 2016 /** 2017 * Compares two Elements, typically the name of a method, 2018 * field or constructor. 2019 * @param e1 the first Element. 2020 * @param e2 the second Element. 2021 * @return a negative integer, zero, or a positive integer as the first 2022 * argument is less than, equal to, or greater than the second. 2023 */ 2024 protected int compareNames(Element e1, Element e2) { 2025 return compareStrings(getSimpleName(e1), getSimpleName(e2)); 2026 } 2027 2028 /** 2029 * Compares the fully qualified names of the entities 2030 * @param e1 the first Element. 2031 * @param e2 the first Element. 2032 * @return a negative integer, zero, or a positive integer as the first 2033 * argument is less than, equal to, or greater than the second. 2034 */ 2035 protected int compareFullyQualifiedNames(Element e1, Element e2) { 2036 // add simplename to be compatible 2037 String thisElement = getFullyQualifiedName(e1); 2038 String thatElement = getFullyQualifiedName(e2); 2039 return compareStrings(thisElement, thatElement); 2040 } 2041 protected int compareElementTypeKinds(Element e1, Element e2) { 2042 return Integer.compare(elementKindOrder.get(e1.getKind()), 2043 elementKindOrder.get(e2.getKind())); 2044 } 2045 boolean hasParameters(Element e) { 2046 return new SimpleElementVisitor9<Boolean, Void>() { 2047 @Override 2048 public Boolean visitExecutable(ExecutableElement e, Void p) { 2049 return true; 2050 } 2051 2052 @Override 2053 protected Boolean defaultAction(Element e, Void p) { 2054 return false; 2055 } 2056 2057 }.visit(e); 2058 } 2059 2060 /** 2061 * The fully qualified names of the entities, used solely by the comparator. 2062 * 2063 * @param p1 the first Element. 2064 * @param p2 the first Element. 2065 * @return a negative integer, zero, or a positive integer as the first argument is less 2066 * than, equal to, or greater than the second. 2067 */ 2068 private String getFullyQualifiedName(Element e) { 2069 return new SimpleElementVisitor9<String, Void>() { 2070 @Override 2071 public String visitModule(ModuleElement e, Void p) { 2072 return e.getQualifiedName().toString(); 2073 } 2074 2075 @Override 2076 public String visitPackage(PackageElement e, Void p) { 2077 return e.getQualifiedName().toString(); 2078 } 2079 2080 @Override 2081 public String visitExecutable(ExecutableElement e, Void p) { 2082 // For backward compatibility 2083 return getFullyQualifiedName(e.getEnclosingElement()) 2084 + "." + e.getSimpleName().toString(); 2085 } 2086 2087 @Override 2088 public String visitType(TypeElement e, Void p) { 2089 return e.getQualifiedName().toString(); 2090 } 2091 2092 @Override 2093 protected String defaultAction(Element e, Void p) { 2094 return getEnclosingTypeElement(e).getQualifiedName().toString() 2095 + "." + e.getSimpleName().toString(); 2096 } 2097 }.visit(e); 2098 } 2099 } 2100 2101 public Iterable<TypeElement> getEnclosedTypeElements(PackageElement pkg) { 2102 List<TypeElement> out = getInterfaces(pkg); 2103 out.addAll(getClasses(pkg)); 2104 out.addAll(getEnums(pkg)); 2105 out.addAll(getAnnotationTypes(pkg)); 2106 return out; 2107 } 2108 2109 // Element related methods 2110 public List<Element> getAnnotationMembers(TypeElement aClass) { 2111 List<Element> members = getAnnotationFields(aClass); 2112 members.addAll(getAnnotationMethods(aClass)); 2113 return members; 2114 } 2115 2116 public List<Element> getAnnotationFields(TypeElement aClass) { 2117 return getItems0(aClass, true, FIELD); 2118 } 2119 2120 List<Element> getAnnotationFieldsUnfiltered(TypeElement aClass) { 2121 return getItems0(aClass, true, FIELD); 2122 } 2123 2124 public List<Element> getAnnotationMethods(TypeElement aClass) { 2125 return getItems0(aClass, true, METHOD); 2126 } 2127 2128 public List<TypeElement> getAnnotationTypes(Element e) { 2129 return convertToTypeElement(getItems(e, true, ANNOTATION_TYPE)); 2130 } 2131 2132 public List<TypeElement> getAnnotationTypesUnfiltered(Element e) { 2133 return convertToTypeElement(getItems(e, false, ANNOTATION_TYPE)); 2134 } 2135 2136 public List<VariableElement> getFields(Element e) { 2137 return convertToVariableElement(getItems(e, true, FIELD)); 2138 } 2139 2140 public List<VariableElement> getFieldsUnfiltered(Element e) { 2141 return convertToVariableElement(getItems(e, false, FIELD)); 2142 } 2143 2144 public List<TypeElement> getClasses(Element e) { 2145 return convertToTypeElement(getItems(e, true, CLASS)); 2146 } 2147 2148 public List<TypeElement> getClassesUnfiltered(Element e) { 2149 return convertToTypeElement(getItems(e, false, CLASS)); 2150 } 2151 2152 public List<ExecutableElement> getConstructors(Element e) { 2153 return convertToExecutableElement(getItems(e, true, CONSTRUCTOR)); 2154 } 2155 2156 public List<ExecutableElement> getMethods(Element e) { 2157 return convertToExecutableElement(getItems(e, true, METHOD)); 2158 } 2159 2160 List<ExecutableElement> getMethodsUnfiltered(Element e) { 2161 return convertToExecutableElement(getItems(e, false, METHOD)); 2162 } 2163 2164 public long getLineNumber(Element e) { 2165 TreePath path = getTreePath(e); 2166 if (path == null) { // maybe null if synthesized 2167 TypeElement encl = getEnclosingTypeElement(e); 2168 path = getTreePath(encl); 2169 } 2170 CompilationUnitTree cu = path.getCompilationUnit(); 2171 LineMap lineMap = cu.getLineMap(); 2172 DocSourcePositions spos = docTrees.getSourcePositions(); 2173 long pos = spos.getStartPosition(cu, path.getLeaf()); 2174 return lineMap.getLineNumber(pos); 2175 } 2176 2177 public List<ExecutableElement> convertToExecutableElement(List<Element> list) { 2178 List<ExecutableElement> out = new ArrayList<>(list.size()); 2179 for (Element e : list) { 2180 out.add((ExecutableElement)e); 2181 } 2182 return out; 2183 } 2184 2185 public List<TypeElement> convertToTypeElement(List<Element> list) { 2186 List<TypeElement> out = new ArrayList<>(list.size()); 2187 for (Element e : list) { 2188 out.add((TypeElement)e); 2189 } 2190 return out; 2191 } 2192 2193 public List<VariableElement> convertToVariableElement(List<Element> list) { 2194 List<VariableElement> out = new ArrayList<>(list.size()); 2195 for (Element e : list) { 2196 out.add((VariableElement) e); 2197 } 2198 return out; 2199 } 2200 2201 public List<TypeElement> getInterfaces(Element e) { 2202 return convertToTypeElement(getItems(e, true, INTERFACE)); 2203 } 2204 2205 public List<TypeElement> getInterfacesUnfiltered(Element e) { 2206 return convertToTypeElement(getItems(e, false, INTERFACE)); 2207 } 2208 2209 List<Element> getNestedClasses(TypeElement e) { 2210 List<Element> result = new ArrayList<>(); 2211 recursiveGetItems(result, e, true, CLASS); 2212 return result; 2213 } 2214 2215 List<Element> getNestedClassesUnfiltered(TypeElement e) { 2216 List<Element> result = new ArrayList<>(); 2217 recursiveGetItems(result, e, false, CLASS); 2218 return result; 2219 } 2220 2221 public List<Element> getEnumConstants(Element e) { 2222 return getItems(e, true, ENUM_CONSTANT); 2223 } 2224 2225 public List<TypeElement> getEnums(Element e) { 2226 return convertToTypeElement(getItems(e, true, ENUM)); 2227 } 2228 2229 public List<TypeElement> getEnumsUnfiltered(Element e) { 2230 return convertToTypeElement(getItems(e, false, ENUM)); 2231 } 2232 2233 public SortedSet<TypeElement> getAllClassesUnfiltered(Element e) { 2234 List<TypeElement> clist = getClassesUnfiltered(e); 2235 clist.addAll(getInterfacesUnfiltered(e)); 2236 clist.addAll(getAnnotationTypesUnfiltered(e)); 2237 SortedSet<TypeElement> oset = new TreeSet<>(makeGeneralPurposeComparator()); 2238 oset.addAll(clist); 2239 return oset; 2240 } 2241 2242 private final HashMap<Element, SortedSet<TypeElement>> cachedClasses = new HashMap<>(); 2243 /** 2244 * Returns a list containing classes and interfaces, 2245 * including annotation types. 2246 * @param e Element 2247 * @return List 2248 */ 2249 public SortedSet<TypeElement> getAllClasses(Element e) { 2250 SortedSet<TypeElement> oset = cachedClasses.get(e); 2251 if (oset != null) 2252 return oset; 2253 List<TypeElement> clist = getClasses(e); 2254 clist.addAll(getInterfaces(e)); 2255 clist.addAll(getAnnotationTypes(e)); 2256 clist.addAll(getEnums(e)); 2257 oset = new TreeSet<>(makeGeneralPurposeComparator()); 2258 oset.addAll(clist); 2259 cachedClasses.put(e, oset); 2260 return oset; 2261 } 2262 2263 /* 2264 * Get all the elements unfiltered and filter them finally based 2265 * on its visibility, this works differently from the other getters. 2266 */ 2267 private List<TypeElement> getInnerClasses(Element e, boolean filter) { 2268 List<TypeElement> olist = new ArrayList<>(); 2269 for (TypeElement te : getClassesUnfiltered(e)) { 2270 if (!filter || configuration.workArounds.isVisible(te)) { 2271 olist.add(te); 2272 } 2273 } 2274 for (TypeElement te : getInterfacesUnfiltered(e)) { 2275 if (!filter || configuration.workArounds.isVisible(te)) { 2276 olist.add(te); 2277 } 2278 } 2279 for (TypeElement te : getAnnotationTypesUnfiltered(e)) { 2280 if (!filter || configuration.workArounds.isVisible(te)) { 2281 olist.add(te); 2282 } 2283 } 2284 for (TypeElement te : getEnumsUnfiltered(e)) { 2285 if (!filter || configuration.workArounds.isVisible(te)) { 2286 olist.add(te); 2287 } 2288 } 2289 return olist; 2290 } 2291 2292 public List<TypeElement> getInnerClasses(Element e) { 2293 return getInnerClasses(e, true); 2294 } 2295 2296 public List<TypeElement> getInnerClassesUnfiltered(Element e) { 2297 return getInnerClasses(e, false); 2298 } 2299 2300 /** 2301 * Returns a list of classes that are not errors or exceptions 2302 * @param e Element 2303 * @return List 2304 */ 2305 public List<TypeElement> getOrdinaryClasses(Element e) { 2306 return getClasses(e).stream() 2307 .filter(te -> (!isException(te) && !isError(te))) 2308 .collect(Collectors.toList()); 2309 } 2310 2311 public List<TypeElement> getErrors(Element e) { 2312 return getClasses(e) 2313 .stream() 2314 .filter(this::isError) 2315 .collect(Collectors.toList()); 2316 } 2317 2318 public List<TypeElement> getExceptions(Element e) { 2319 return getClasses(e) 2320 .stream() 2321 .filter(this::isException) 2322 .collect(Collectors.toList()); 2323 } 2324 2325 List<Element> getItems(Element e, boolean filter, ElementKind select) { 2326 List<Element> elements = new ArrayList<>(); 2327 // maintain backward compatibility by returning a null list, see AnnotationDocType.methods(). 2328 if (configuration.backwardCompatibility && e.getKind() == ANNOTATION_TYPE) 2329 return elements; 2330 return new SimpleElementVisitor9<List<Element>, Void>() { 2331 2332 @Override 2333 public List<Element> visitPackage(PackageElement e, Void p) { 2334 recursiveGetItems(elements, e, filter, select); 2335 return elements; 2336 } 2337 2338 @Override 2339 protected List<Element> defaultAction(Element e0, Void p) { 2340 return getItems0(e0, filter, select); 2341 } 2342 2343 }.visit(e); 2344 } 2345 2346 EnumSet<ElementKind> nestedKinds = EnumSet.of(ANNOTATION_TYPE, CLASS, ENUM, INTERFACE); 2347 2348 void recursiveGetItems(Collection<Element> list, Element e, boolean filter, ElementKind... select) { 2349 list.addAll(getItems0(e, filter, select)); 2350 List<Element> classes = getItems0(e, filter, nestedKinds); 2351 for (Element c : classes) { 2352 list.addAll(getItems0(c, filter, select)); 2353 if (isTypeElement(c)) { 2354 recursiveGetItems(list, c, filter, select); 2355 } 2356 } 2357 } 2358 2359 private List<Element> getItems0(Element te, boolean filter, ElementKind... select) { 2360 EnumSet<ElementKind> kinds = EnumSet.copyOf(Arrays.asList(select)); 2361 return getItems0(te, filter, kinds); 2362 } 2363 2364 private List<Element> getItems0(Element te, boolean filter, Set<ElementKind> kinds) { 2365 List<Element> elements = new ArrayList<>(); 2366 for (Element e : te.getEnclosedElements()) { 2367 if (kinds.contains(e.getKind())) { 2368 if (!filter || configuration.workArounds.shouldDocument(e)) { 2369 elements.add(e); 2370 } 2371 } 2372 } 2373 return elements; 2374 } 2375 2376 /* 2377 * nameCache is maintained for improving the comparator 2378 * performance, noting that the Collator used by the comparators 2379 * use Strings, as of this writing. 2380 * TODO: when those APIs handle charSequences, the use of 2381 * this nameCache must be re-investigated and removed. 2382 */ 2383 private final Map<Element, String> nameCache = new LinkedHashMap<>(); 2384 2385 /** 2386 * Returns the name of the element after the last dot of the package name. 2387 * This emulates the behavior of the old doclet. 2388 * @param e an element whose name is required 2389 * @return the name 2390 */ 2391 public String getSimpleName(Element e) { 2392 return nameCache.computeIfAbsent(e, this::getSimpleName0); 2393 } 2394 2395 private SimpleElementVisitor9<String, Void> snvisitor = null; 2396 2397 private String getSimpleName0(Element e) { 2398 if (snvisitor == null) { 2399 snvisitor = new SimpleElementVisitor9<String, Void>() { 2400 @Override 2401 public String visitModule(ModuleElement e, Void p) { 2402 return e.getSimpleName().toString(); 2403 } 2404 2405 @Override 2406 public String visitType(TypeElement e, Void p) { 2407 StringBuilder sb = new StringBuilder(e.getSimpleName()); 2408 Element enclosed = e.getEnclosingElement(); 2409 while (enclosed != null 2410 && (enclosed.getKind().isClass() || enclosed.getKind().isInterface())) { 2411 sb.insert(0, enclosed.getSimpleName() + "."); 2412 enclosed = enclosed.getEnclosingElement(); 2413 } 2414 return sb.toString(); 2415 } 2416 2417 @Override 2418 public String visitExecutable(ExecutableElement e, Void p) { 2419 if (e.getKind() == CONSTRUCTOR || e.getKind() == STATIC_INIT) { 2420 return e.getEnclosingElement().getSimpleName().toString(); 2421 } 2422 return e.getSimpleName().toString(); 2423 } 2424 2425 @Override 2426 protected String defaultAction(Element e, Void p) { 2427 return e.getSimpleName().toString(); 2428 } 2429 }; 2430 } 2431 return snvisitor.visit(e); 2432 } 2433 2434 public TypeElement getEnclosingTypeElement(Element e) { 2435 if (e.getKind() == ElementKind.PACKAGE) 2436 return null; 2437 Element encl = e.getEnclosingElement(); 2438 ElementKind kind = encl.getKind(); 2439 if (kind == ElementKind.PACKAGE) 2440 return null; 2441 while (!(kind.isClass() || kind.isInterface())) { 2442 encl = encl.getEnclosingElement(); 2443 } 2444 return (TypeElement)encl; 2445 } 2446 2447 private ConstantValueExpression cve = null; 2448 2449 public String constantValueExpresion(VariableElement ve) { 2450 if (cve == null) 2451 cve = new ConstantValueExpression(); 2452 return cve.constantValueExpression(configuration.workArounds, ve); 2453 } 2454 2455 private static class ConstantValueExpression { 2456 public String constantValueExpression(WorkArounds workArounds, VariableElement ve) { 2457 return new TypeKindVisitor9<String, Object>() { 2458 /* TODO: we need to fix this correctly. 2459 * we have a discrepancy here, note the use of getConstValue 2460 * vs. getConstantValue, at some point we need to use 2461 * getConstantValue. 2462 * In the legacy world byte and char primitives appear as Integer values, 2463 * thus a byte value of 127 will appear as 127, but in the new world, 2464 * a byte value appears as Byte thus 0x7f will be printed, similarly 2465 * chars will be translated to \n, \r etc. however, in the new world, 2466 * they will be printed as decimal values. The new world is correct, 2467 * and we should fix this by using getConstantValue and the visitor to 2468 * address this in the future. 2469 */ 2470 @Override 2471 public String visitPrimitiveAsBoolean(PrimitiveType t, Object val) { 2472 return (int)val == 0 ? "false" : "true"; 2473 } 2474 2475 @Override 2476 public String visitPrimitiveAsDouble(PrimitiveType t, Object val) { 2477 return sourceForm(((Double)val), 'd'); 2478 } 2479 2480 @Override 2481 public String visitPrimitiveAsFloat(PrimitiveType t, Object val) { 2482 return sourceForm(((Float)val).doubleValue(), 'f'); 2483 } 2484 2485 @Override 2486 public String visitPrimitiveAsLong(PrimitiveType t, Object val) { 2487 return val + "L"; 2488 } 2489 2490 @Override 2491 protected String defaultAction(TypeMirror e, Object val) { 2492 if (val == null) 2493 return null; 2494 else if (val instanceof Character) 2495 return sourceForm(((Character)val)); 2496 else if (val instanceof Byte) 2497 return sourceForm(((Byte)val)); 2498 else if (val instanceof String) 2499 return sourceForm((String)val); 2500 return val.toString(); // covers int, short 2501 } 2502 }.visit(ve.asType(), workArounds.getConstValue(ve)); 2503 } 2504 2505 // where 2506 private String sourceForm(double v, char suffix) { 2507 if (Double.isNaN(v)) 2508 return "0" + suffix + "/0" + suffix; 2509 if (v == Double.POSITIVE_INFINITY) 2510 return "1" + suffix + "/0" + suffix; 2511 if (v == Double.NEGATIVE_INFINITY) 2512 return "-1" + suffix + "/0" + suffix; 2513 return v + (suffix == 'f' || suffix == 'F' ? "" + suffix : ""); 2514 } 2515 2516 private String sourceForm(char c) { 2517 StringBuilder buf = new StringBuilder(8); 2518 buf.append('\''); 2519 sourceChar(c, buf); 2520 buf.append('\''); 2521 return buf.toString(); 2522 } 2523 2524 private String sourceForm(byte c) { 2525 return "0x" + Integer.toString(c & 0xff, 16); 2526 } 2527 2528 private String sourceForm(String s) { 2529 StringBuilder buf = new StringBuilder(s.length() + 5); 2530 buf.append('\"'); 2531 for (int i=0; i<s.length(); i++) { 2532 char c = s.charAt(i); 2533 sourceChar(c, buf); 2534 } 2535 buf.append('\"'); 2536 return buf.toString(); 2537 } 2538 2539 private void sourceChar(char c, StringBuilder buf) { 2540 switch (c) { 2541 case '\b': buf.append("\\b"); return; 2542 case '\t': buf.append("\\t"); return; 2543 case '\n': buf.append("\\n"); return; 2544 case '\f': buf.append("\\f"); return; 2545 case '\r': buf.append("\\r"); return; 2546 case '\"': buf.append("\\\""); return; 2547 case '\'': buf.append("\\\'"); return; 2548 case '\\': buf.append("\\\\"); return; 2549 default: 2550 if (isPrintableAscii(c)) { 2551 buf.append(c); return; 2552 } 2553 unicodeEscape(c, buf); 2554 return; 2555 } 2556 } 2557 2558 private void unicodeEscape(char c, StringBuilder buf) { 2559 final String chars = "0123456789abcdef"; 2560 buf.append("\\u"); 2561 buf.append(chars.charAt(15 & (c>>12))); 2562 buf.append(chars.charAt(15 & (c>>8))); 2563 buf.append(chars.charAt(15 & (c>>4))); 2564 buf.append(chars.charAt(15 & (c>>0))); 2565 } 2566 private boolean isPrintableAscii(char c) { 2567 return c >= ' ' && c <= '~'; 2568 } 2569 } 2570 2571 public boolean isEnclosingPackageIncluded(TypeElement te) { 2572 return isIncluded(containingPackage(te)); 2573 } 2574 2575 public boolean isIncluded(Element e) { 2576 return configuration.docEnv.isIncluded(e); 2577 } 2578 2579 private SimpleElementVisitor9<Boolean, Void> specifiedVisitor = null; 2580 public boolean isSpecified(Element e) { 2581 if (specifiedVisitor == null) { 2582 specifiedVisitor = new SimpleElementVisitor9<Boolean, Void>() { 2583 @Override 2584 public Boolean visitModule(ModuleElement e, Void p) { 2585 return configuration.getSpecifiedModules().contains(e); 2586 } 2587 2588 @Override 2589 public Boolean visitPackage(PackageElement e, Void p) { 2590 return configuration.getSpecifiedPackages().contains(e); 2591 } 2592 2593 @Override 2594 public Boolean visitType(TypeElement e, Void p) { 2595 return configuration.getSpecifiedClasses().contains(e); 2596 } 2597 2598 @Override 2599 protected Boolean defaultAction(Element e, Void p) { 2600 return false; 2601 } 2602 }; 2603 } 2604 return specifiedVisitor.visit(e); 2605 } 2606 2607 /** 2608 * package name, an unnamed package is returned as <Unnamed> 2609 * @param pkg 2610 * @return 2611 */ 2612 public String getPackageName(PackageElement pkg) { 2613 if (pkg == null || pkg.isUnnamed()) { 2614 return DocletConstants.DEFAULT_PACKAGE_NAME; 2615 } 2616 return pkg.getQualifiedName().toString(); 2617 } 2618 2619 public boolean isAttribute(DocTree doctree) { 2620 return isKind(doctree, ATTRIBUTE); 2621 } 2622 2623 public boolean isAuthor(DocTree doctree) { 2624 return isKind(doctree, AUTHOR); 2625 } 2626 2627 public boolean isComment(DocTree doctree) { 2628 return isKind(doctree, COMMENT); 2629 } 2630 2631 public boolean isDeprecated(DocTree doctree) { 2632 return isKind(doctree, DEPRECATED); 2633 } 2634 2635 public boolean isDocComment(DocTree doctree) { 2636 return isKind(doctree, DOC_COMMENT); 2637 } 2638 2639 public boolean isDocRoot(DocTree doctree) { 2640 return isKind(doctree, DOC_ROOT); 2641 } 2642 2643 public boolean isEndElement(DocTree doctree) { 2644 return isKind(doctree, END_ELEMENT); 2645 } 2646 2647 public boolean isEntity(DocTree doctree) { 2648 return isKind(doctree, ENTITY); 2649 } 2650 2651 public boolean isErroneous(DocTree doctree) { 2652 return isKind(doctree, ERRONEOUS); 2653 } 2654 2655 public boolean isException(DocTree doctree) { 2656 return isKind(doctree, EXCEPTION); 2657 } 2658 2659 public boolean isIdentifier(DocTree doctree) { 2660 return isKind(doctree, IDENTIFIER); 2661 } 2662 2663 public boolean isInheritDoc(DocTree doctree) { 2664 return isKind(doctree, INHERIT_DOC); 2665 } 2666 2667 public boolean isLink(DocTree doctree) { 2668 return isKind(doctree, LINK); 2669 } 2670 2671 public boolean isLinkPlain(DocTree doctree) { 2672 return isKind(doctree, LINK_PLAIN); 2673 } 2674 2675 public boolean isLiteral(DocTree doctree) { 2676 return isKind(doctree, LITERAL); 2677 } 2678 2679 public boolean isOther(DocTree doctree) { 2680 return doctree.getKind() == DocTree.Kind.OTHER; 2681 } 2682 2683 public boolean isParam(DocTree doctree) { 2684 return isKind(doctree, PARAM); 2685 } 2686 2687 public boolean isReference(DocTree doctree) { 2688 return isKind(doctree, REFERENCE); 2689 } 2690 2691 public boolean isReturn(DocTree doctree) { 2692 return isKind(doctree, RETURN); 2693 } 2694 2695 public boolean isSee(DocTree doctree) { 2696 return isKind(doctree, SEE); 2697 } 2698 2699 public boolean isSerial(DocTree doctree) { 2700 return isKind(doctree, SERIAL); 2701 } 2702 2703 public boolean isSerialData(DocTree doctree) { 2704 return isKind(doctree, SERIAL_DATA); 2705 } 2706 2707 public boolean isSerialField(DocTree doctree) { 2708 return isKind(doctree, SERIAL_FIELD); 2709 } 2710 2711 public boolean isSince(DocTree doctree) { 2712 return isKind(doctree, SINCE); 2713 } 2714 2715 public boolean isStartElement(DocTree doctree) { 2716 return isKind(doctree, START_ELEMENT); 2717 } 2718 2719 public boolean isText(DocTree doctree) { 2720 return isKind(doctree, TEXT); 2721 } 2722 2723 public boolean isThrows(DocTree doctree) { 2724 return isKind(doctree, THROWS); 2725 } 2726 2727 public boolean isUnknownBlockTag(DocTree doctree) { 2728 return isKind(doctree, UNKNOWN_BLOCK_TAG); 2729 } 2730 2731 public boolean isUnknownInlineTag(DocTree doctree) { 2732 return isKind(doctree, UNKNOWN_INLINE_TAG); 2733 } 2734 2735 public boolean isValue(DocTree doctree) { 2736 return isKind(doctree, VALUE); 2737 } 2738 2739 public boolean isVersion(DocTree doctree) { 2740 return isKind(doctree, VERSION); 2741 } 2742 2743 private boolean isKind(DocTree doctree, DocTree.Kind match) { 2744 return doctree.getKind() == match; 2745 } 2746 2747 private final WeakSoftHashMap wksMap = new WeakSoftHashMap(this); 2748 2749 public CommentHelper getCommentHelper(Element element) { 2750 return wksMap.computeIfAbsent(element); 2751 } 2752 2753 public void removeCommentHelper(Element element) { 2754 wksMap.remove(element); 2755 } 2756 2757 public List<? extends DocTree> filteredList(List<? extends DocTree> dlist, DocTree.Kind... select) { 2758 List<DocTree> list = new ArrayList<>(dlist.size()); 2759 if (select == null) 2760 return dlist; 2761 for (DocTree dt : dlist) { 2762 if (dt.getKind() != ERRONEOUS) { 2763 for (DocTree.Kind kind : select) { 2764 if (dt.getKind() == kind) { 2765 list.add(dt); 2766 } 2767 } 2768 } 2769 } 2770 return list; 2771 } 2772 2773 private List<? extends DocTree> getBlockTags0(Element element, DocTree.Kind... kinds) { 2774 DocCommentTree dcTree = getDocCommentTree(element); 2775 if (dcTree == null) 2776 return Collections.emptyList(); 2777 2778 return filteredList(dcTree.getBlockTags(), kinds); 2779 } 2780 2781 public List<? extends DocTree> getBlockTags(Element element) { 2782 return getBlockTags0(element, (Kind[]) null); 2783 } 2784 2785 public List<? extends DocTree> getBlockTags(Element element, DocTree.Kind... kinds) { 2786 return getBlockTags0(element, kinds); 2787 } 2788 2789 public List<? extends DocTree> getBlockTags(Element element, String tagName) { 2790 DocTree.Kind kind = null; 2791 switch (tagName) { 2792 case "author": 2793 case "deprecated": 2794 case "hidden": 2795 case "param": 2796 case "return": 2797 case "see": 2798 case "serial": 2799 case "since": 2800 case "throws": 2801 case "exception": 2802 case "version": 2803 kind = DocTree.Kind.valueOf(tagName.toUpperCase()); 2804 return getBlockTags(element, kind); 2805 case "serialData": 2806 kind = SERIAL_DATA; 2807 return getBlockTags(element, kind); 2808 case "serialField": 2809 kind = SERIAL_FIELD; 2810 return getBlockTags(element, kind); 2811 default: 2812 kind = DocTree.Kind.UNKNOWN_BLOCK_TAG; 2813 break; 2814 } 2815 List<? extends DocTree> blockTags = getBlockTags(element, kind); 2816 List<DocTree> out = new ArrayList<>(); 2817 String tname = tagName.startsWith("@") ? tagName.substring(1) : tagName; 2818 CommentHelper ch = getCommentHelper(element); 2819 for (DocTree dt : blockTags) { 2820 if (ch.getTagName(dt).equals(tname)) { 2821 out.add(dt); 2822 } 2823 } 2824 return out; 2825 } 2826 2827 public boolean hasBlockTag(Element element, DocTree.Kind kind) { 2828 return hasBlockTag(element, kind, null); 2829 } 2830 2831 public boolean hasBlockTag(Element element, DocTree.Kind kind, final String tagName) { 2832 CommentHelper ch = getCommentHelper(element); 2833 String tname = tagName != null && tagName.startsWith("@") 2834 ? tagName.substring(1) 2835 : tagName; 2836 for (DocTree dt : getBlockTags(element, kind)) { 2837 if (dt.getKind() == kind) { 2838 if (tname == null || ch.getTagName(dt).equals(tname)) { 2839 return true; 2840 } 2841 } 2842 } 2843 return false; 2844 } 2845 2846 /** 2847 * Gets a TreePath for an Element. Note this method is called very 2848 * frequently, care must be taken to ensure this method is lithe 2849 * and efficient. 2850 * @param e an Element 2851 * @return TreePath 2852 */ 2853 public TreePath getTreePath(Element e) { 2854 DocCommentDuo duo = dcTreeCache.get(e); 2855 if (isValidDuo(duo) && duo.treePath != null) { 2856 return duo.treePath; 2857 } 2858 duo = configuration.cmtUtils.getSyntheticCommentDuo(e); 2859 if (isValidDuo(duo) && duo.treePath != null) { 2860 return duo.treePath; 2861 } 2862 Map<Element, TreePath> elementToTreePath = configuration.workArounds.getElementToTreePath(); 2863 TreePath path = elementToTreePath.get(e); 2864 if (path != null || elementToTreePath.containsKey(e)) { 2865 // expedite the path and one that is a null 2866 return path; 2867 } 2868 return elementToTreePath.computeIfAbsent(e, docTrees::getPath); 2869 } 2870 2871 private final Map<Element, DocCommentDuo> dcTreeCache = new LinkedHashMap<>(); 2872 2873 /** 2874 * Retrieves the doc comments for a given element. 2875 * @param element 2876 * @return DocCommentTree for the Element 2877 */ 2878 public DocCommentTree getDocCommentTree0(Element element) { 2879 2880 DocCommentDuo duo = null; 2881 2882 ElementKind kind = element.getKind(); 2883 if (kind == ElementKind.PACKAGE || kind == ElementKind.OTHER) { 2884 duo = dcTreeCache.get(element); // local cache 2885 if (!isValidDuo(duo) && kind == ElementKind.PACKAGE) { 2886 // package-info.java 2887 duo = getDocCommentTuple(element); 2888 } 2889 if (!isValidDuo(duo)) { 2890 // package.html or overview.html 2891 duo = configuration.cmtUtils.getHtmlCommentDuo(element); // html source 2892 } 2893 } else { 2894 duo = configuration.cmtUtils.getSyntheticCommentDuo(element); 2895 if (!isValidDuo(duo)) { 2896 duo = dcTreeCache.get(element); // local cache 2897 } 2898 if (!isValidDuo(duo)) { 2899 duo = getDocCommentTuple(element); // get the real mccoy 2900 } 2901 } 2902 2903 DocCommentTree docCommentTree = isValidDuo(duo) ? duo.dcTree : null; 2904 TreePath path = isValidDuo(duo) ? duo.treePath : null; 2905 if (!dcTreeCache.containsKey(element)) { 2906 if (docCommentTree != null && path != null) { 2907 configuration.workArounds.runDocLint(path); 2908 } 2909 dcTreeCache.put(element, duo); 2910 } 2911 return docCommentTree; 2912 } 2913 2914 private DocCommentDuo getDocCommentTuple(Element element) { 2915 // prevent nasty things downstream with overview element 2916 if (element.getKind() != ElementKind.OTHER) { 2917 TreePath path = getTreePath(element); 2918 if (path != null) { 2919 DocCommentTree docCommentTree = docTrees.getDocCommentTree(path); 2920 return new DocCommentDuo(path, docCommentTree); 2921 } 2922 } 2923 return null; 2924 } 2925 2926 boolean isValidDuo(DocCommentDuo duo) { 2927 return duo != null && duo.dcTree != null; 2928 } 2929 2930 public DocCommentTree getDocCommentTree(Element element) { 2931 CommentHelper ch = wksMap.get(element); 2932 if (ch != null) { 2933 return ch.dctree; 2934 } 2935 DocCommentTree dcTree = getDocCommentTree0(element); 2936 if (dcTree != null) { 2937 wksMap.put(element, new CommentHelper(configuration, element, getTreePath(element), dcTree)); 2938 } 2939 return dcTree; 2940 } 2941 2942 public List<? extends DocTree> getFullBody(Element element) { 2943 DocCommentTree docCommentTree = getDocCommentTree(element); 2944 return (docCommentTree == null) 2945 ? Collections.emptyList() 2946 : docCommentTree.getFullBody(); 2947 } 2948 2949 public List<? extends DocTree> getBody(Element element) { 2950 DocCommentTree docCommentTree = getDocCommentTree(element); 2951 return (docCommentTree == null) 2952 ? Collections.emptyList() 2953 : docCommentTree.getFullBody(); 2954 } 2955 2956 public List<? extends DocTree> getDeprecatedTrees(Element element) { 2957 return getBlockTags(element, DEPRECATED); 2958 } 2959 2960 public List<? extends DocTree> getSeeTrees(Element element) { 2961 return getBlockTags(element, SEE); 2962 } 2963 2964 public List<? extends DocTree> getSerialTrees(Element element) { 2965 return getBlockTags(element, SERIAL); 2966 } 2967 2968 public List<? extends DocTree> getSerialFieldTrees(VariableElement field) { 2969 return getBlockTags(field, DocTree.Kind.SERIAL_FIELD); 2970 } 2971 2972 public List<? extends DocTree> getThrowsTrees(Element element) { 2973 return getBlockTags(element, DocTree.Kind.EXCEPTION, DocTree.Kind.THROWS); 2974 } 2975 2976 public List<? extends DocTree> getTypeParamTrees(Element element) { 2977 return getParamTrees(element, true); 2978 } 2979 2980 public List<? extends DocTree> getParamTrees(Element element) { 2981 return getParamTrees(element, false); 2982 } 2983 2984 private List<? extends DocTree> getParamTrees(Element element, boolean isTypeParameters) { 2985 List<DocTree> out = new ArrayList<>(); 2986 for (DocTree dt : getBlockTags(element, PARAM)) { 2987 ParamTree pt = (ParamTree) dt; 2988 if (pt.isTypeParameter() == isTypeParameters) { 2989 out.add(dt); 2990 } 2991 } 2992 return out; 2993 } 2994 2995 public List<? extends DocTree> getReturnTrees(Element element) { 2996 List<DocTree> out = new ArrayList<>(); 2997 for (DocTree dt : getBlockTags(element, RETURN)) { 2998 out.add(dt); 2999 } 3000 return out; 3001 } 3002 3003 public List<? extends DocTree> getFirstSentenceTrees(Element element) { 3004 DocCommentTree dcTree = getDocCommentTree(element); 3005 if (dcTree == null) { 3006 return Collections.emptyList(); 3007 } 3008 List<DocTree> out = new ArrayList<>(); 3009 for (DocTree dt : dcTree.getFirstSentence()) { 3010 out.add(dt); 3011 } 3012 return out; 3013 } 3014 3015 public ModuleElement containingModule(Element e) { 3016 return elementUtils.getModuleOf(e); 3017 } 3018 3019 public PackageElement containingPackage(Element e) { 3020 return elementUtils.getPackageOf(e); 3021 } 3022 3023 public TypeElement getTopMostContainingTypeElement(Element e) { 3024 if (isPackage(e)) { 3025 return null; 3026 } 3027 TypeElement outer = getEnclosingTypeElement(e); 3028 if (outer == null) 3029 return (TypeElement)e; 3030 while (outer != null && outer.getNestingKind().isNested()) { 3031 outer = getEnclosingTypeElement(outer); 3032 } 3033 return outer; 3034 } 3035 3036 static class WeakSoftHashMap implements Map<Element, CommentHelper> { 3037 3038 private final WeakHashMap<Element, SoftReference<CommentHelper>> wkMap; 3039 private final Utils utils; 3040 public WeakSoftHashMap(Utils utils) { 3041 wkMap = new WeakHashMap<>(); 3042 this.utils = utils; 3043 } 3044 3045 @Override 3046 public boolean containsKey(Object key) { 3047 return wkMap.containsKey(key); 3048 } 3049 3050 @Override 3051 public Collection<CommentHelper> values() { 3052 Set<CommentHelper> out = new LinkedHashSet<>(); 3053 for (SoftReference<CommentHelper> v : wkMap.values()) { 3054 out.add(v.get()); 3055 } 3056 return out; 3057 } 3058 3059 @Override 3060 public boolean containsValue(Object value) { 3061 return wkMap.containsValue(new SoftReference<>((CommentHelper)value)); 3062 } 3063 3064 @Override 3065 public CommentHelper remove(Object key) { 3066 SoftReference<CommentHelper> value = wkMap.remove(key); 3067 return value == null ? null : value.get(); 3068 } 3069 3070 3071 @Override 3072 public CommentHelper put(Element key, CommentHelper value) { 3073 SoftReference<CommentHelper> nvalue = wkMap.put(key, new SoftReference<>(value)); 3074 return nvalue == null ? null : nvalue.get(); 3075 } 3076 3077 @Override 3078 public CommentHelper get(Object key) { 3079 SoftReference<CommentHelper> value = wkMap.get(key); 3080 return value == null ? null : value.get(); 3081 } 3082 3083 @Override 3084 public int size() { 3085 return wkMap.size(); 3086 } 3087 3088 @Override 3089 public boolean isEmpty() { 3090 return wkMap.isEmpty(); 3091 } 3092 3093 @Override 3094 public void clear() { 3095 wkMap.clear(); 3096 } 3097 3098 public CommentHelper computeIfAbsent(Element key) { 3099 if (wkMap.containsKey(key)) { 3100 SoftReference<CommentHelper> value = wkMap.get(key); 3101 if (value != null) { 3102 CommentHelper cvalue = value.get(); 3103 if (cvalue != null) { 3104 return cvalue; 3105 } 3106 } 3107 } 3108 CommentHelper newValue = new CommentHelper(utils.configuration, key, utils.getTreePath(key), 3109 utils.getDocCommentTree(key)); 3110 wkMap.put(key, new SoftReference<>(newValue)); 3111 return newValue; 3112 } 3113 3114 3115 @Override 3116 public void putAll(Map<? extends Element, ? extends CommentHelper> map) { 3117 for (Map.Entry<? extends Element, ? extends CommentHelper> entry : map.entrySet()) { 3118 put(entry.getKey(), entry.getValue()); 3119 } 3120 } 3121 3122 @Override 3123 public Set<Element> keySet() { 3124 return wkMap.keySet(); 3125 } 3126 3127 @Override 3128 public Set<Entry<Element, CommentHelper>> entrySet() { 3129 Set<Entry<Element, CommentHelper>> out = new LinkedHashSet<>(); 3130 for (Element e : wkMap.keySet()) { 3131 SimpleEntry<Element, CommentHelper> n = new SimpleEntry<>(e, get(e)); 3132 out.add(n); 3133 } 3134 return out; 3135 } 3136 } 3137} 3138