aoqi@0: /* aoqi@0: * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.doclets.internal.toolkit.util; aoqi@0: aoqi@0: import java.io.*; aoqi@0: import java.lang.annotation.ElementType; aoqi@0: import java.util.*; aoqi@0: import javax.tools.StandardLocation; aoqi@0: aoqi@0: import com.sun.javadoc.*; aoqi@0: import com.sun.javadoc.AnnotationDesc.ElementValuePair; aoqi@0: import com.sun.tools.doclets.internal.toolkit.*; aoqi@0: import com.sun.tools.javac.util.StringUtils; aoqi@0: aoqi@0: /** aoqi@0: * Utilities Class for Doclets. aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own risk. aoqi@0: * This code and its internal interfaces are subject to change or aoqi@0: * deletion without notice. aoqi@0: * aoqi@0: * @author Atul M Dambalkar aoqi@0: * @author Jamie Ho aoqi@0: */ aoqi@0: public class Util { aoqi@0: aoqi@0: /** aoqi@0: * Return array of class members whose documentation is to be generated. aoqi@0: * If the member is deprecated do not include such a member in the aoqi@0: * returned array. aoqi@0: * aoqi@0: * @param members Array of members to choose from. aoqi@0: * @return ProgramElementDoc[] Array of eligible members for whom aoqi@0: * documentation is getting generated. aoqi@0: */ aoqi@0: public static ProgramElementDoc[] excludeDeprecatedMembers( aoqi@0: ProgramElementDoc[] members) { aoqi@0: return aoqi@0: toProgramElementDocArray(excludeDeprecatedMembersAsList(members)); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return array of class members whose documentation is to be generated. aoqi@0: * If the member is deprecated do not include such a member in the aoqi@0: * returned array. aoqi@0: * aoqi@0: * @param members Array of members to choose from. aoqi@0: * @return List List of eligible members for whom aoqi@0: * documentation is getting generated. aoqi@0: */ aoqi@0: public static List excludeDeprecatedMembersAsList( aoqi@0: ProgramElementDoc[] members) { aoqi@0: List list = new ArrayList(); aoqi@0: for (int i = 0; i < members.length; i++) { aoqi@0: if (members[i].tags("deprecated").length == 0) { aoqi@0: list.add(members[i]); aoqi@0: } aoqi@0: } aoqi@0: Collections.sort(list); aoqi@0: return list; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return the list of ProgramElementDoc objects as Array. aoqi@0: */ aoqi@0: public static ProgramElementDoc[] toProgramElementDocArray(List list) { aoqi@0: ProgramElementDoc[] pgmarr = new ProgramElementDoc[list.size()]; aoqi@0: for (int i = 0; i < list.size(); i++) { aoqi@0: pgmarr[i] = list.get(i); aoqi@0: } aoqi@0: return pgmarr; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return true if a non-public member found in the given array. aoqi@0: * aoqi@0: * @param members Array of members to look into. aoqi@0: * @return boolean True if non-public member found, false otherwise. aoqi@0: */ aoqi@0: public static boolean nonPublicMemberFound(ProgramElementDoc[] members) { aoqi@0: for (int i = 0; i < members.length; i++) { aoqi@0: if (!members[i].isPublic()) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Search for the given method in the given class. aoqi@0: * aoqi@0: * @param cd Class to search into. aoqi@0: * @param method Method to be searched. aoqi@0: * @return MethodDoc Method found, null otherwise. aoqi@0: */ aoqi@0: public static MethodDoc findMethod(ClassDoc cd, MethodDoc method) { aoqi@0: MethodDoc[] methods = cd.methods(); aoqi@0: for (int i = 0; i < methods.length; i++) { aoqi@0: if (executableMembersEqual(method, methods[i])) { aoqi@0: return methods[i]; aoqi@0: aoqi@0: } aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * @param member1 the first method to compare. aoqi@0: * @param member2 the second method to compare. aoqi@0: * @return true if member1 overrides/hides or is overriden/hidden by member2. aoqi@0: */ aoqi@0: public static boolean executableMembersEqual(ExecutableMemberDoc member1, aoqi@0: ExecutableMemberDoc member2) { aoqi@0: if (! (member1 instanceof MethodDoc && member2 instanceof MethodDoc)) aoqi@0: return false; aoqi@0: aoqi@0: MethodDoc method1 = (MethodDoc) member1; aoqi@0: MethodDoc method2 = (MethodDoc) member2; aoqi@0: if (method1.isStatic() && method2.isStatic()) { aoqi@0: Parameter[] targetParams = method1.parameters(); aoqi@0: Parameter[] currentParams; aoqi@0: if (method1.name().equals(method2.name()) && aoqi@0: (currentParams = method2.parameters()).length == aoqi@0: targetParams.length) { aoqi@0: int j; aoqi@0: for (j = 0; j < targetParams.length; j++) { aoqi@0: if (! (targetParams[j].typeName().equals( aoqi@0: currentParams[j].typeName()) || aoqi@0: currentParams[j].type() instanceof TypeVariable || aoqi@0: targetParams[j].type() instanceof TypeVariable)) { aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: if (j == targetParams.length) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } else { aoqi@0: return method1.overrides(method2) || aoqi@0: method2.overrides(method1) || aoqi@0: member1 == member2; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * According to aoqi@0: * The Java™ Language Specification, aoqi@0: * all the outer classes and static inner classes are core classes. aoqi@0: */ aoqi@0: public static boolean isCoreClass(ClassDoc cd) { aoqi@0: return cd.containingClass() == null || cd.isStatic(); aoqi@0: } aoqi@0: aoqi@0: public static boolean matches(ProgramElementDoc doc1, aoqi@0: ProgramElementDoc doc2) { aoqi@0: if (doc1 instanceof ExecutableMemberDoc && aoqi@0: doc2 instanceof ExecutableMemberDoc) { aoqi@0: ExecutableMemberDoc ed1 = (ExecutableMemberDoc)doc1; aoqi@0: ExecutableMemberDoc ed2 = (ExecutableMemberDoc)doc2; aoqi@0: return executableMembersEqual(ed1, ed2); aoqi@0: } else { aoqi@0: return doc1.name().equals(doc2.name()); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Copy the given directory contents from the source package directory aoqi@0: * to the generated documentation directory. For example for a package aoqi@0: * java.lang this method find out the source location of the package using aoqi@0: * {@link SourcePath} and if given directory is found in the source aoqi@0: * directory structure, copy the entire directory, to the generated aoqi@0: * documentation hierarchy. aoqi@0: * aoqi@0: * @param configuration The configuration of the current doclet. aoqi@0: * @param path The relative path to the directory to be copied. aoqi@0: * @param dir The original directory name to copy from. aoqi@0: * @param overwrite Overwrite files if true. aoqi@0: */ aoqi@0: public static void copyDocFiles(Configuration configuration, PackageDoc pd) { aoqi@0: copyDocFiles(configuration, DocPath.forPackage(pd).resolve(DocPaths.DOC_FILES)); aoqi@0: } aoqi@0: aoqi@0: public static void copyDocFiles(Configuration configuration, DocPath dir) { aoqi@0: try { aoqi@0: boolean first = true; aoqi@0: for (DocFile f : DocFile.list(configuration, StandardLocation.SOURCE_PATH, dir)) { aoqi@0: if (!f.isDirectory()) { aoqi@0: continue; aoqi@0: } aoqi@0: DocFile srcdir = f; aoqi@0: DocFile destdir = DocFile.createFileForOutput(configuration, dir); aoqi@0: if (srcdir.isSameFile(destdir)) { aoqi@0: continue; aoqi@0: } aoqi@0: aoqi@0: for (DocFile srcfile: srcdir.list()) { aoqi@0: DocFile destfile = destdir.resolve(srcfile.getName()); aoqi@0: if (srcfile.isFile()) { aoqi@0: if (destfile.exists() && !first) { aoqi@0: configuration.message.warning((SourcePosition) null, aoqi@0: "doclet.Copy_Overwrite_warning", aoqi@0: srcfile.getPath(), destdir.getPath()); aoqi@0: } else { aoqi@0: configuration.message.notice( aoqi@0: "doclet.Copying_File_0_To_Dir_1", aoqi@0: srcfile.getPath(), destdir.getPath()); aoqi@0: destfile.copyFile(srcfile); aoqi@0: } aoqi@0: } else if (srcfile.isDirectory()) { aoqi@0: if (configuration.copydocfilesubdirs aoqi@0: && !configuration.shouldExcludeDocFileDir(srcfile.getName())) { aoqi@0: copyDocFiles(configuration, dir.resolve(srcfile.getName())); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: first = false; aoqi@0: } aoqi@0: } catch (SecurityException exc) { aoqi@0: throw new DocletAbortException(exc); aoqi@0: } catch (IOException exc) { aoqi@0: throw new DocletAbortException(exc); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * We want the list of types in alphabetical order. However, types are not aoqi@0: * comparable. We need a comparator for now. aoqi@0: */ aoqi@0: private static class TypeComparator implements Comparator { aoqi@0: public int compare(Type type1, Type type2) { aoqi@0: return type1.qualifiedTypeName().compareToIgnoreCase( aoqi@0: type2.qualifiedTypeName()); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * For the class return all implemented interfaces including the aoqi@0: * superinterfaces of the implementing interfaces, also iterate over for aoqi@0: * all the superclasses. For interface return all the extended interfaces aoqi@0: * as well as superinterfaces for those extended interfaces. aoqi@0: * aoqi@0: * @param type type whose implemented or aoqi@0: * super interfaces are sought. aoqi@0: * @param configuration the current configuration of the doclet. aoqi@0: * @param sort if true, return list of interfaces sorted alphabetically. aoqi@0: * @return List of all the required interfaces. aoqi@0: */ aoqi@0: public static List getAllInterfaces(Type type, aoqi@0: Configuration configuration, boolean sort) { aoqi@0: Map results = sort ? new TreeMap() : new LinkedHashMap(); aoqi@0: Type[] interfaceTypes = null; aoqi@0: Type superType = null; aoqi@0: if (type instanceof ParameterizedType) { aoqi@0: interfaceTypes = ((ParameterizedType) type).interfaceTypes(); aoqi@0: superType = ((ParameterizedType) type).superclassType(); aoqi@0: } else if (type instanceof ClassDoc) { aoqi@0: interfaceTypes = ((ClassDoc) type).interfaceTypes(); aoqi@0: superType = ((ClassDoc) type).superclassType(); aoqi@0: } else { aoqi@0: interfaceTypes = type.asClassDoc().interfaceTypes(); aoqi@0: superType = type.asClassDoc().superclassType(); aoqi@0: } aoqi@0: aoqi@0: for (int i = 0; i < interfaceTypes.length; i++) { aoqi@0: Type interfaceType = interfaceTypes[i]; aoqi@0: ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); aoqi@0: if (! (interfaceClassDoc.isPublic() || aoqi@0: (configuration == null || aoqi@0: isLinkable(interfaceClassDoc, configuration)))) { aoqi@0: continue; aoqi@0: } aoqi@0: results.put(interfaceClassDoc, interfaceType); aoqi@0: List superInterfaces = getAllInterfaces(interfaceType, configuration, sort); aoqi@0: for (Iterator iter = superInterfaces.iterator(); iter.hasNext(); ) { aoqi@0: Type t = iter.next(); aoqi@0: results.put(t.asClassDoc(), t); aoqi@0: } aoqi@0: } aoqi@0: if (superType == null) aoqi@0: return new ArrayList(results.values()); aoqi@0: //Try walking the tree. aoqi@0: addAllInterfaceTypes(results, aoqi@0: superType, aoqi@0: interfaceTypesOf(superType), aoqi@0: false, configuration); aoqi@0: List resultsList = new ArrayList(results.values()); aoqi@0: if (sort) { aoqi@0: Collections.sort(resultsList, new TypeComparator()); aoqi@0: } aoqi@0: return resultsList; aoqi@0: } aoqi@0: aoqi@0: private static Type[] interfaceTypesOf(Type type) { aoqi@0: if (type instanceof AnnotatedType) aoqi@0: type = ((AnnotatedType)type).underlyingType(); aoqi@0: return type instanceof ClassDoc ? aoqi@0: ((ClassDoc)type).interfaceTypes() : aoqi@0: ((ParameterizedType)type).interfaceTypes(); aoqi@0: } aoqi@0: aoqi@0: public static List getAllInterfaces(Type type, Configuration configuration) { aoqi@0: return getAllInterfaces(type, configuration, true); aoqi@0: } aoqi@0: aoqi@0: private static void findAllInterfaceTypes(Map results, ClassDoc c, boolean raw, aoqi@0: Configuration configuration) { aoqi@0: Type superType = c.superclassType(); aoqi@0: if (superType == null) aoqi@0: return; aoqi@0: addAllInterfaceTypes(results, superType, aoqi@0: interfaceTypesOf(superType), aoqi@0: raw, configuration); aoqi@0: } aoqi@0: aoqi@0: private static void findAllInterfaceTypes(Map results, ParameterizedType p, aoqi@0: Configuration configuration) { aoqi@0: Type superType = p.superclassType(); aoqi@0: if (superType == null) aoqi@0: return; aoqi@0: addAllInterfaceTypes(results, superType, aoqi@0: interfaceTypesOf(superType), aoqi@0: false, configuration); aoqi@0: } aoqi@0: aoqi@0: private static void addAllInterfaceTypes(Map results, Type type, aoqi@0: Type[] interfaceTypes, boolean raw, aoqi@0: Configuration configuration) { aoqi@0: for (int i = 0; i < interfaceTypes.length; i++) { aoqi@0: Type interfaceType = interfaceTypes[i]; aoqi@0: ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); aoqi@0: if (! (interfaceClassDoc.isPublic() || aoqi@0: (configuration != null && aoqi@0: isLinkable(interfaceClassDoc, configuration)))) { aoqi@0: continue; aoqi@0: } aoqi@0: if (raw) aoqi@0: interfaceType = interfaceType.asClassDoc(); aoqi@0: results.put(interfaceClassDoc, interfaceType); aoqi@0: List superInterfaces = getAllInterfaces(interfaceType, configuration); aoqi@0: for (Iterator iter = superInterfaces.iterator(); iter.hasNext(); ) { aoqi@0: Type superInterface = iter.next(); aoqi@0: results.put(superInterface.asClassDoc(), superInterface); aoqi@0: } aoqi@0: } aoqi@0: if (type instanceof AnnotatedType) aoqi@0: type = ((AnnotatedType)type).underlyingType(); aoqi@0: aoqi@0: if (type instanceof ParameterizedType) aoqi@0: findAllInterfaceTypes(results, (ParameterizedType) type, configuration); aoqi@0: else if (((ClassDoc) type).typeParameters().length == 0) aoqi@0: findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration); aoqi@0: else aoqi@0: findAllInterfaceTypes(results, (ClassDoc) type, true, configuration); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Enclose in quotes, used for paths and filenames that contains spaces aoqi@0: */ aoqi@0: public static String quote(String filepath) { aoqi@0: return ("\"" + filepath + "\""); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Given a package, return its name. aoqi@0: * @param packageDoc the package to check. aoqi@0: * @return the name of the given package. aoqi@0: */ aoqi@0: public static String getPackageName(PackageDoc packageDoc) { aoqi@0: return packageDoc == null || packageDoc.name().length() == 0 ? aoqi@0: DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Given a package, return its file name without the extension. aoqi@0: * @param packageDoc the package to check. aoqi@0: * @return the file name of the given package. aoqi@0: */ aoqi@0: public static String getPackageFileHeadName(PackageDoc packageDoc) { aoqi@0: return packageDoc == null || packageDoc.name().length() == 0 ? aoqi@0: DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Given a string, replace all occurrences of 'newStr' with 'oldStr'. aoqi@0: * @param originalStr the string to modify. aoqi@0: * @param oldStr the string to replace. aoqi@0: * @param newStr the string to insert in place of the old string. aoqi@0: */ aoqi@0: public static String replaceText(String originalStr, String oldStr, aoqi@0: String newStr) { aoqi@0: if (oldStr == null || newStr == null || oldStr.equals(newStr)) { aoqi@0: return originalStr; aoqi@0: } aoqi@0: return originalStr.replace(oldStr, newStr); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Given an annotation, return true if it should be documented and false aoqi@0: * otherwise. aoqi@0: * aoqi@0: * @param annotationDoc the annotation to check. aoqi@0: * aoqi@0: * @return true return true if it should be documented and false otherwise. aoqi@0: */ aoqi@0: public static boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) { aoqi@0: AnnotationDesc[] annotationDescList = annotationDoc.annotations(); aoqi@0: for (int i = 0; i < annotationDescList.length; i++) { aoqi@0: if (annotationDescList[i].annotationType().qualifiedName().equals( aoqi@0: java.lang.annotation.Documented.class.getName())){ aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: private static boolean isDeclarationTarget(AnnotationDesc targetAnno) { aoqi@0: // The error recovery steps here are analogous to TypeAnnotations aoqi@0: ElementValuePair[] elems = targetAnno.elementValues(); aoqi@0: if (elems == null aoqi@0: || elems.length != 1 aoqi@0: || !"value".equals(elems[0].element().name()) aoqi@0: || !(elems[0].value().value() instanceof AnnotationValue[])) aoqi@0: return true; // error recovery aoqi@0: aoqi@0: AnnotationValue[] values = (AnnotationValue[])elems[0].value().value(); aoqi@0: for (int i = 0; i < values.length; i++) { aoqi@0: Object value = values[i].value(); aoqi@0: if (!(value instanceof FieldDoc)) aoqi@0: return true; // error recovery aoqi@0: aoqi@0: FieldDoc eValue = (FieldDoc)value; aoqi@0: if (Util.isJava5DeclarationElementType(eValue)) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns true if the {@code annotationDoc} is to be treated aoqi@0: * as a declaration annotation, when targeting the aoqi@0: * {@code elemType} element type. aoqi@0: * aoqi@0: * @param annotationDoc the annotationDoc to check aoqi@0: * @param elemType the targeted elemType aoqi@0: * @return true if annotationDoc is a declaration annotation aoqi@0: */ aoqi@0: public static boolean isDeclarationAnnotation(AnnotationTypeDoc annotationDoc, aoqi@0: boolean isJava5DeclarationLocation) { aoqi@0: if (!isJava5DeclarationLocation) aoqi@0: return false; aoqi@0: AnnotationDesc[] annotationDescList = annotationDoc.annotations(); aoqi@0: // Annotations with no target are treated as declaration as well aoqi@0: if (annotationDescList.length==0) aoqi@0: return true; aoqi@0: for (int i = 0; i < annotationDescList.length; i++) { aoqi@0: if (annotationDescList[i].annotationType().qualifiedName().equals( aoqi@0: java.lang.annotation.Target.class.getName())) { aoqi@0: if (isDeclarationTarget(annotationDescList[i])) aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return true if this class is linkable and false if we can't link to the aoqi@0: * desired class. aoqi@0: *
aoqi@0: * NOTE: You can only link to external classes if they are public or aoqi@0: * protected. aoqi@0: * aoqi@0: * @param classDoc the class to check. aoqi@0: * @param configuration the current configuration of the doclet. aoqi@0: * aoqi@0: * @return true if this class is linkable and false if we can't link to the aoqi@0: * desired class. aoqi@0: */ aoqi@0: public static boolean isLinkable(ClassDoc classDoc, aoqi@0: Configuration configuration) { aoqi@0: return aoqi@0: ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) || aoqi@0: (configuration.extern.isExternal(classDoc) && aoqi@0: (classDoc.isPublic() || classDoc.isProtected())); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Given a class, return the closest visible super class. aoqi@0: * aoqi@0: * @param classDoc the class we are searching the parent for. aoqi@0: * @param configuration the current configuration of the doclet. aoqi@0: * @return the closest visible super class. Return null if it cannot aoqi@0: * be found (i.e. classDoc is java.lang.Object). aoqi@0: */ aoqi@0: public static Type getFirstVisibleSuperClass(ClassDoc classDoc, aoqi@0: Configuration configuration) { aoqi@0: if (classDoc == null) { aoqi@0: return null; aoqi@0: } aoqi@0: Type sup = classDoc.superclassType(); aoqi@0: ClassDoc supClassDoc = classDoc.superclass(); aoqi@0: while (sup != null && aoqi@0: (! (supClassDoc.isPublic() || aoqi@0: isLinkable(supClassDoc, configuration))) ) { aoqi@0: if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName())) aoqi@0: break; aoqi@0: sup = supClassDoc.superclassType(); aoqi@0: supClassDoc = supClassDoc.superclass(); aoqi@0: } aoqi@0: if (classDoc.equals(supClassDoc)) { aoqi@0: return null; aoqi@0: } aoqi@0: return sup; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Given a class, return the closest visible super class. aoqi@0: * aoqi@0: * @param classDoc the class we are searching the parent for. aoqi@0: * @param configuration the current configuration of the doclet. aoqi@0: * @return the closest visible super class. Return null if it cannot aoqi@0: * be found (i.e. classDoc is java.lang.Object). aoqi@0: */ aoqi@0: public static ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc, aoqi@0: Configuration configuration) { aoqi@0: if (classDoc == null) { aoqi@0: return null; aoqi@0: } aoqi@0: ClassDoc supClassDoc = classDoc.superclass(); aoqi@0: while (supClassDoc != null && aoqi@0: (! (supClassDoc.isPublic() || aoqi@0: isLinkable(supClassDoc, configuration))) ) { aoqi@0: supClassDoc = supClassDoc.superclass(); aoqi@0: } aoqi@0: if (classDoc.equals(supClassDoc)) { aoqi@0: return null; aoqi@0: } aoqi@0: return supClassDoc; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Given a ClassDoc, return the name of its type (Class, Interface, etc.). aoqi@0: * aoqi@0: * @param cd the ClassDoc to check. aoqi@0: * @param lowerCaseOnly true if you want the name returned in lower case. aoqi@0: * If false, the first letter of the name is capitalized. aoqi@0: * @return aoqi@0: */ aoqi@0: public static String getTypeName(Configuration config, aoqi@0: ClassDoc cd, boolean lowerCaseOnly) { aoqi@0: String typeName = ""; aoqi@0: if (cd.isOrdinaryClass()) { aoqi@0: typeName = "doclet.Class"; aoqi@0: } else if (cd.isInterface()) { aoqi@0: typeName = "doclet.Interface"; aoqi@0: } else if (cd.isException()) { aoqi@0: typeName = "doclet.Exception"; aoqi@0: } else if (cd.isError()) { aoqi@0: typeName = "doclet.Error"; aoqi@0: } else if (cd.isAnnotationType()) { aoqi@0: typeName = "doclet.AnnotationType"; aoqi@0: } else if (cd.isEnum()) { aoqi@0: typeName = "doclet.Enum"; aoqi@0: } aoqi@0: return config.getText( aoqi@0: lowerCaseOnly ? StringUtils.toLowerCase(typeName) : typeName); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Replace all tabs in a string with the appropriate number of spaces. aoqi@0: * The string may be a multi-line string. aoqi@0: * @param configuration the doclet configuration defining the setting for the aoqi@0: * tab length. aoqi@0: * @param text the text for which the tabs should be expanded aoqi@0: * @return the text with all tabs expanded aoqi@0: */ aoqi@0: public static String replaceTabs(Configuration configuration, String text) { aoqi@0: if (text.indexOf("\t") == -1) aoqi@0: return text; aoqi@0: aoqi@0: final int tabLength = configuration.sourcetab; aoqi@0: final String whitespace = configuration.tabSpaces; aoqi@0: final int textLength = text.length(); aoqi@0: StringBuilder result = new StringBuilder(textLength); aoqi@0: int pos = 0; aoqi@0: int lineLength = 0; aoqi@0: for (int i = 0; i < textLength; i++) { aoqi@0: char ch = text.charAt(i); aoqi@0: switch (ch) { aoqi@0: case '\n': case '\r': aoqi@0: lineLength = 0; aoqi@0: break; aoqi@0: case '\t': aoqi@0: result.append(text, pos, i); aoqi@0: int spaceCount = tabLength - lineLength % tabLength; aoqi@0: result.append(whitespace, 0, spaceCount); aoqi@0: lineLength += spaceCount; aoqi@0: pos = i + 1; aoqi@0: break; aoqi@0: default: aoqi@0: lineLength++; aoqi@0: } aoqi@0: } aoqi@0: result.append(text, pos, textLength); aoqi@0: return result.toString(); aoqi@0: } aoqi@0: aoqi@0: public static String normalizeNewlines(String text) { aoqi@0: StringBuilder sb = new StringBuilder(); aoqi@0: final int textLength = text.length(); aoqi@0: final String NL = DocletConstants.NL; aoqi@0: int pos = 0; aoqi@0: for (int i = 0; i < textLength; i++) { aoqi@0: char ch = text.charAt(i); aoqi@0: switch (ch) { aoqi@0: case '\n': aoqi@0: sb.append(text, pos, i); aoqi@0: sb.append(NL); aoqi@0: pos = i + 1; aoqi@0: break; aoqi@0: case '\r': aoqi@0: sb.append(text, pos, i); aoqi@0: sb.append(NL); aoqi@0: if (i + 1 < textLength && text.charAt(i + 1) == '\n') aoqi@0: i++; aoqi@0: pos = i + 1; aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: sb.append(text, pos, textLength); aoqi@0: return sb.toString(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * The documentation for values() and valueOf() in Enums are set by the aoqi@0: * doclet. aoqi@0: */ aoqi@0: public static void setEnumDocumentation(Configuration configuration, aoqi@0: ClassDoc classDoc) { aoqi@0: MethodDoc[] methods = classDoc.methods(); aoqi@0: for (int j = 0; j < methods.length; j++) { aoqi@0: MethodDoc currentMethod = methods[j]; aoqi@0: if (currentMethod.name().equals("values") && aoqi@0: currentMethod.parameters().length == 0) { aoqi@0: StringBuilder sb = new StringBuilder(); aoqi@0: sb.append(configuration.getText("doclet.enum_values_doc.main", classDoc.name())); aoqi@0: sb.append("\n@return "); aoqi@0: sb.append(configuration.getText("doclet.enum_values_doc.return")); aoqi@0: currentMethod.setRawCommentText(sb.toString()); aoqi@0: } else if (currentMethod.name().equals("valueOf") && aoqi@0: currentMethod.parameters().length == 1) { aoqi@0: Type paramType = currentMethod.parameters()[0].type(); aoqi@0: if (paramType != null && aoqi@0: paramType.qualifiedTypeName().equals(String.class.getName())) { aoqi@0: StringBuilder sb = new StringBuilder(); aoqi@0: sb.append(configuration.getText("doclet.enum_valueof_doc.main", classDoc.name())); aoqi@0: sb.append("\n@param name "); aoqi@0: sb.append(configuration.getText("doclet.enum_valueof_doc.param_name")); aoqi@0: sb.append("\n@return "); aoqi@0: sb.append(configuration.getText("doclet.enum_valueof_doc.return")); aoqi@0: sb.append("\n@throws IllegalArgumentException "); aoqi@0: sb.append(configuration.getText("doclet.enum_valueof_doc.throws_ila")); aoqi@0: sb.append("\n@throws NullPointerException "); aoqi@0: sb.append(configuration.getText("doclet.enum_valueof_doc.throws_npe")); aoqi@0: currentMethod.setRawCommentText(sb.toString()); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return true if the given Doc is deprecated. aoqi@0: * aoqi@0: * @param doc the Doc to check. aoqi@0: * @return true if the given Doc is deprecated. aoqi@0: */ aoqi@0: public static boolean isDeprecated(Doc doc) { aoqi@0: if (doc.tags("deprecated").length > 0) { aoqi@0: return true; aoqi@0: } aoqi@0: AnnotationDesc[] annotationDescList; aoqi@0: if (doc instanceof PackageDoc) aoqi@0: annotationDescList = ((PackageDoc)doc).annotations(); aoqi@0: else aoqi@0: annotationDescList = ((ProgramElementDoc)doc).annotations(); aoqi@0: for (int i = 0; i < annotationDescList.length; i++) { aoqi@0: if (annotationDescList[i].annotationType().qualifiedName().equals( aoqi@0: java.lang.Deprecated.class.getName())){ aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A convenience method to get property name from the name of the aoqi@0: * getter or setter method. aoqi@0: * @param name name of the getter or setter method. aoqi@0: * @return the name of the property of the given setter of getter. aoqi@0: */ aoqi@0: public static String propertyNameFromMethodName(Configuration configuration, String name) { aoqi@0: String propertyName = null; aoqi@0: if (name.startsWith("get") || name.startsWith("set")) { aoqi@0: propertyName = name.substring(3); aoqi@0: } else if (name.startsWith("is")) { aoqi@0: propertyName = name.substring(2); aoqi@0: } aoqi@0: if ((propertyName == null) || propertyName.isEmpty()){ aoqi@0: return ""; aoqi@0: } aoqi@0: return propertyName.substring(0, 1).toLowerCase(configuration.getLocale()) aoqi@0: + propertyName.substring(1); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * In case of JavaFX mode on, filters out classes that are private, aoqi@0: * package private or having the @treatAsPrivate annotation. Those are not aoqi@0: * documented in JavaFX mode. aoqi@0: * aoqi@0: * @param classes array of classes to be filtered. aoqi@0: * @param javafx set to true if in JavaFX mode. aoqi@0: * @return list of filtered classes. aoqi@0: */ aoqi@0: public static ClassDoc[] filterOutPrivateClasses(final ClassDoc[] classes, aoqi@0: boolean javafx) { aoqi@0: if (!javafx) { aoqi@0: return classes; aoqi@0: } aoqi@0: final List filteredOutClasses = aoqi@0: new ArrayList(classes.length); aoqi@0: for (ClassDoc classDoc : classes) { aoqi@0: if (classDoc.isPrivate() || classDoc.isPackagePrivate()) { aoqi@0: continue; aoqi@0: } aoqi@0: Tag[] aspTags = classDoc.tags("treatAsPrivate"); aoqi@0: if (aspTags != null && aspTags.length > 0) { aoqi@0: continue; aoqi@0: } aoqi@0: filteredOutClasses.add(classDoc); aoqi@0: } aoqi@0: aoqi@0: return filteredOutClasses.toArray(new ClassDoc[0]); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Test whether the given FieldDoc is one of the declaration annotation ElementTypes aoqi@0: * defined in Java 5. aoqi@0: * Instead of testing for one of the new enum constants added in Java 8, test for aoqi@0: * the old constants. This prevents bootstrapping problems. aoqi@0: * aoqi@0: * @param elt The FieldDoc to test aoqi@0: * @return true, iff the given ElementType is one of the constants defined in Java 5 aoqi@0: * @since 1.8 aoqi@0: */ aoqi@0: public static boolean isJava5DeclarationElementType(FieldDoc elt) { aoqi@0: return elt.name().contentEquals(ElementType.ANNOTATION_TYPE.name()) || aoqi@0: elt.name().contentEquals(ElementType.CONSTRUCTOR.name()) || aoqi@0: elt.name().contentEquals(ElementType.FIELD.name()) || aoqi@0: elt.name().contentEquals(ElementType.LOCAL_VARIABLE.name()) || aoqi@0: elt.name().contentEquals(ElementType.METHOD.name()) || aoqi@0: elt.name().contentEquals(ElementType.PACKAGE.name()) || aoqi@0: elt.name().contentEquals(ElementType.PARAMETER.name()) || aoqi@0: elt.name().contentEquals(ElementType.TYPE.name()); aoqi@0: } aoqi@0: }