duke@1: /* xdono@117: * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.doclets.internal.toolkit.util; duke@1: jjg@197: import java.io.*; jjg@197: import java.util.*; jjg@197: duke@1: import com.sun.javadoc.*; duke@1: import com.sun.tools.doclets.internal.toolkit.*; duke@1: duke@1: /** duke@1: * Utilities Class for Doclets. duke@1: * duke@1: * This code is not part of an API. duke@1: * It is implementation that is subject to change. duke@1: * Do not use it as an API duke@1: * duke@1: * @author Atul M Dambalkar duke@1: * @author Jamie Ho duke@1: */ duke@1: public class Util { duke@1: duke@1: /** duke@1: * A mapping between characters and their duke@1: * corresponding HTML escape character. duke@1: */ duke@1: public static final String[][] HTML_ESCAPE_CHARS = duke@1: {{"&", "&"}, {"<", "<"}, {">", ">"}}; duke@1: duke@1: /** duke@1: * Return array of class members whose documentation is to be generated. duke@1: * If the member is deprecated do not include such a member in the duke@1: * returned array. duke@1: * duke@1: * @param members Array of members to choose from. duke@1: * @return ProgramElementDoc[] Array of eligible members for whom duke@1: * documentation is getting generated. duke@1: */ duke@1: public static ProgramElementDoc[] excludeDeprecatedMembers( duke@1: ProgramElementDoc[] members) { duke@1: return duke@1: toProgramElementDocArray(excludeDeprecatedMembersAsList(members)); duke@1: } duke@1: duke@1: /** duke@1: * Return array of class members whose documentation is to be generated. duke@1: * If the member is deprecated do not include such a member in the duke@1: * returned array. duke@1: * duke@1: * @param members Array of members to choose from. duke@1: * @return List List of eligible members for whom duke@1: * documentation is getting generated. duke@1: */ jjg@74: public static List excludeDeprecatedMembersAsList( duke@1: ProgramElementDoc[] members) { jjg@74: List list = new ArrayList(); duke@1: for (int i = 0; i < members.length; i++) { duke@1: if (members[i].tags("deprecated").length == 0) { duke@1: list.add(members[i]); duke@1: } duke@1: } duke@1: Collections.sort(list); duke@1: return list; duke@1: } duke@1: duke@1: /** duke@1: * Return the list of ProgramElementDoc objects as Array. duke@1: */ mcimadamore@184: public static ProgramElementDoc[] toProgramElementDocArray(List list) { duke@1: ProgramElementDoc[] pgmarr = new ProgramElementDoc[list.size()]; duke@1: for (int i = 0; i < list.size(); i++) { mcimadamore@184: pgmarr[i] = list.get(i); duke@1: } duke@1: return pgmarr; duke@1: } duke@1: duke@1: /** duke@1: * Return true if a non-public member found in the given array. duke@1: * duke@1: * @param members Array of members to look into. duke@1: * @return boolean True if non-public member found, false otherwise. duke@1: */ duke@1: public static boolean nonPublicMemberFound(ProgramElementDoc[] members) { duke@1: for (int i = 0; i < members.length; i++) { duke@1: if (!members[i].isPublic()) { duke@1: return true; duke@1: } duke@1: } duke@1: return false; duke@1: } duke@1: duke@1: /** duke@1: * Search for the given method in the given class. duke@1: * duke@1: * @param cd Class to search into. duke@1: * @param method Method to be searched. duke@1: * @return MethodDoc Method found, null otherwise. duke@1: */ duke@1: public static MethodDoc findMethod(ClassDoc cd, MethodDoc method) { duke@1: MethodDoc[] methods = cd.methods(); duke@1: for (int i = 0; i < methods.length; i++) { duke@1: if (executableMembersEqual(method, methods[i])) { duke@1: return methods[i]; duke@1: duke@1: } duke@1: } duke@1: return null; duke@1: } duke@1: duke@1: /** duke@1: * @param member1 the first method to compare. duke@1: * @param member2 the second method to compare. duke@1: * @return true if member1 overrides/hides or is overriden/hidden by member2. duke@1: */ duke@1: public static boolean executableMembersEqual(ExecutableMemberDoc member1, duke@1: ExecutableMemberDoc member2) { duke@1: if (! (member1 instanceof MethodDoc && member2 instanceof MethodDoc)) duke@1: return false; duke@1: duke@1: MethodDoc method1 = (MethodDoc) member1; duke@1: MethodDoc method2 = (MethodDoc) member2; duke@1: if (method1.isStatic() && method2.isStatic()) { duke@1: Parameter[] targetParams = method1.parameters(); duke@1: Parameter[] currentParams; duke@1: if (method1.name().equals(method2.name()) && duke@1: (currentParams = method2.parameters()).length == duke@1: targetParams.length) { duke@1: int j; duke@1: for (j = 0; j < targetParams.length; j++) { duke@1: if (! (targetParams[j].typeName().equals( duke@1: currentParams[j].typeName()) || duke@1: currentParams[j].type() instanceof TypeVariable || duke@1: targetParams[j].type() instanceof TypeVariable)) { duke@1: break; duke@1: } duke@1: } duke@1: if (j == targetParams.length) { duke@1: return true; duke@1: } duke@1: } duke@1: return false; duke@1: } else { duke@1: return method1.overrides(method2) || duke@1: method2.overrides(method1) || duke@1: member1 == member2; duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * According to the Java Language Specifications, all the outer classes duke@1: * and static inner classes are core classes. duke@1: */ duke@1: public static boolean isCoreClass(ClassDoc cd) { duke@1: return cd.containingClass() == null || cd.isStatic(); duke@1: } duke@1: duke@1: public static boolean matches(ProgramElementDoc doc1, duke@1: ProgramElementDoc doc2) { duke@1: if (doc1 instanceof ExecutableMemberDoc && duke@1: doc2 instanceof ExecutableMemberDoc) { duke@1: ExecutableMemberDoc ed1 = (ExecutableMemberDoc)doc1; duke@1: ExecutableMemberDoc ed2 = (ExecutableMemberDoc)doc2; duke@1: return executableMembersEqual(ed1, ed2); duke@1: } else { duke@1: return doc1.name().equals(doc2.name()); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Copy source file to destination file. duke@1: * duke@1: * @throws SecurityException duke@1: * @throws IOException duke@1: */ duke@1: public static void copyFile(File destfile, File srcfile) duke@1: throws IOException { duke@1: byte[] bytearr = new byte[512]; duke@1: int len = 0; duke@1: FileInputStream input = new FileInputStream(srcfile); duke@1: File destDir = destfile.getParentFile(); duke@1: destDir.mkdirs(); duke@1: FileOutputStream output = new FileOutputStream(destfile); duke@1: try { duke@1: while ((len = input.read(bytearr)) != -1) { duke@1: output.write(bytearr, 0, len); duke@1: } duke@1: } catch (FileNotFoundException exc) { duke@1: } catch (SecurityException exc) { duke@1: } finally { duke@1: input.close(); duke@1: output.close(); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Copy the given directory contents from the source package directory duke@1: * to the generated documentation directory. For example for a package duke@1: * java.lang this method find out the source location of the package using duke@1: * {@link SourcePath} and if given directory is found in the source duke@1: * directory structure, copy the entire directory, to the generated duke@1: * documentation hierarchy. duke@1: * duke@1: * @param configuration The configuration of the current doclet. duke@1: * @param path The relative path to the directory to be copied. duke@1: * @param dir The original directory name to copy from. duke@1: * @param overwrite Overwrite files if true. duke@1: */ duke@1: public static void copyDocFiles(Configuration configuration, duke@1: String path, String dir, boolean overwrite) { duke@1: if (checkCopyDocFilesErrors(configuration, path, dir)) { duke@1: return; duke@1: } duke@1: String destname = configuration.docFileDestDirName; duke@1: File srcdir = new File(path + dir); duke@1: if (destname.length() > 0 && !destname.endsWith( duke@1: DirectoryManager.URL_FILE_SEPERATOR)) { duke@1: destname += DirectoryManager.URL_FILE_SEPERATOR; duke@1: } duke@1: String dest = destname + dir; duke@1: try { duke@1: File destdir = new File(dest); duke@1: DirectoryManager.createDirectory(configuration, dest); duke@1: String[] files = srcdir.list(); duke@1: for (int i = 0; i < files.length; i++) { duke@1: File srcfile = new File(srcdir, files[i]); duke@1: File destfile = new File(destdir, files[i]); duke@1: if (srcfile.isFile()) { duke@1: if(destfile.exists() && ! overwrite) { duke@1: configuration.message.warning((SourcePosition) null, duke@1: "doclet.Copy_Overwrite_warning", duke@1: srcfile.toString(), destdir.toString()); duke@1: } else { duke@1: configuration.message.notice( duke@1: "doclet.Copying_File_0_To_Dir_1", duke@1: srcfile.toString(), destdir.toString()); duke@1: Util.copyFile(destfile, srcfile); duke@1: } duke@1: } else if(srcfile.isDirectory()) { duke@1: if(configuration.copydocfilesubdirs duke@1: && ! configuration.shouldExcludeDocFileDir( duke@1: srcfile.getName())){ duke@1: copyDocFiles(configuration, path, dir + duke@1: DirectoryManager.URL_FILE_SEPERATOR + srcfile.getName(), duke@1: overwrite); duke@1: } duke@1: } duke@1: } duke@1: } catch (SecurityException exc) { duke@1: throw new DocletAbortException(); duke@1: } catch (IOException exc) { duke@1: throw new DocletAbortException(); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Given the parameters for copying doc-files, check for errors. duke@1: * duke@1: * @param configuration The configuration of the current doclet. duke@1: * @param path The relative path to the directory to be copied. duke@1: * @param dirName The original directory name to copy from. duke@1: */ duke@1: private static boolean checkCopyDocFilesErrors (Configuration configuration, duke@1: String path, String dirName) { duke@1: if ((configuration.sourcepath == null || configuration.sourcepath.length() == 0) && duke@1: (configuration.destDirName == null || configuration.destDirName.length() == 0)) { duke@1: //The destination path and source path are definitely equal. duke@1: return true; duke@1: } duke@1: File sourcePath, destPath = new File(configuration.destDirName); duke@1: StringTokenizer pathTokens = new StringTokenizer( duke@1: configuration.sourcepath == null ? "" : configuration.sourcepath, duke@1: File.pathSeparator); duke@1: //Check if the destination path is equal to the source path. If yes, duke@1: //do not copy doc-file directories. duke@1: while(pathTokens.hasMoreTokens()){ duke@1: sourcePath = new File(pathTokens.nextToken()); duke@1: if(destPath.equals(sourcePath)){ duke@1: return true; duke@1: } duke@1: } duke@1: //Make sure the doc-file being copied exists. duke@1: File srcdir = new File(path + dirName); duke@1: if (! srcdir.exists()) { duke@1: return true; duke@1: } duke@1: return false; duke@1: } duke@1: duke@1: /** duke@1: * Copy a file in the resources directory to the destination duke@1: * directory (if it is not there already). If duke@1: * overwrite is true and the destination file duke@1: * already exists, overwrite it. duke@1: * duke@1: * @param configuration Holds the destination directory and error message duke@1: * @param resourcefile The name of the resource file to copy duke@1: * @param overwrite A flag to indicate whether the file in the duke@1: * destination directory will be overwritten if duke@1: * it already exists. duke@1: */ duke@1: public static void copyResourceFile(Configuration configuration, duke@1: String resourcefile, duke@1: boolean overwrite) { duke@1: String destdir = configuration.destDirName; duke@1: String destresourcesdir = destdir + "resources"; duke@1: DirectoryManager.createDirectory(configuration, destresourcesdir); duke@1: File destfile = new File(destresourcesdir, resourcefile); duke@1: if(destfile.exists() && (! overwrite)) return; duke@1: try { duke@1: duke@1: InputStream in = Configuration.class.getResourceAsStream( duke@1: "resources/" + resourcefile); duke@1: duke@1: if(in==null) return; duke@1: duke@1: OutputStream out = new FileOutputStream(destfile); duke@1: byte[] buf = new byte[2048]; duke@1: int n; duke@1: while((n = in.read(buf))>0) out.write(buf,0,n); duke@1: duke@1: in.close(); duke@1: out.close(); duke@1: } catch(Throwable t) {} duke@1: } duke@1: duke@1: /** duke@1: * Given a PackageDoc, return the source path for that package. duke@1: * @param configuration The Configuration for the current Doclet. duke@1: * @param pkgDoc The package to seach the path for. duke@1: * @return A string representing the path to the given package. duke@1: */ duke@1: public static String getPackageSourcePath(Configuration configuration, duke@1: PackageDoc pkgDoc){ duke@1: try{ duke@1: String pkgPath = DirectoryManager.getDirectoryPath(pkgDoc); duke@1: String completePath = new SourcePath(configuration.sourcepath). duke@1: getDirectory(pkgPath) + DirectoryManager.URL_FILE_SEPERATOR; duke@1: //Make sure that both paths are using the same seperators. duke@1: completePath = Util.replaceText(completePath, File.separator, duke@1: DirectoryManager.URL_FILE_SEPERATOR); duke@1: pkgPath = Util.replaceText(pkgPath, File.separator, duke@1: DirectoryManager.URL_FILE_SEPERATOR); duke@1: return completePath.substring(0, completePath.indexOf(pkgPath)); duke@1: } catch (Exception e){ duke@1: return ""; duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * We want the list of types in alphabetical order. However, types are not duke@1: * comparable. We need a comparator for now. duke@1: */ jjg@74: private static class TypeComparator implements Comparator { jjg@74: public int compare(Type type1, Type type2) { jjg@74: return type1.qualifiedTypeName().toLowerCase().compareTo( jjg@74: type2.qualifiedTypeName().toLowerCase()); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * For the class return all implemented interfaces including the duke@1: * superinterfaces of the implementing interfaces, also iterate over for duke@1: * all the superclasses. For interface return all the extended interfaces duke@1: * as well as superinterfaces for those extended interfaces. duke@1: * duke@1: * @param type type whose implemented or duke@1: * super interfaces are sought. duke@1: * @param configuration the current configuration of the doclet. duke@1: * @param sort if true, return list of interfaces sorted alphabetically. duke@1: * @return List of all the required interfaces. duke@1: */ jjg@74: public static List getAllInterfaces(Type type, duke@1: Configuration configuration, boolean sort) { jjg@74: Map results = sort ? new TreeMap() : new LinkedHashMap(); duke@1: Type[] interfaceTypes = null; duke@1: Type superType = null; duke@1: if (type instanceof ParameterizedType) { duke@1: interfaceTypes = ((ParameterizedType) type).interfaceTypes(); duke@1: superType = ((ParameterizedType) type).superclassType(); duke@1: } else if (type instanceof ClassDoc) { duke@1: interfaceTypes = ((ClassDoc) type).interfaceTypes(); duke@1: superType = ((ClassDoc) type).superclassType(); duke@1: } else { duke@1: interfaceTypes = type.asClassDoc().interfaceTypes(); duke@1: superType = type.asClassDoc().superclassType(); duke@1: } duke@1: duke@1: for (int i = 0; i < interfaceTypes.length; i++) { duke@1: Type interfaceType = interfaceTypes[i]; duke@1: ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); duke@1: if (! (interfaceClassDoc.isPublic() || duke@1: (configuration == null || duke@1: isLinkable(interfaceClassDoc, configuration)))) { duke@1: continue; duke@1: } duke@1: results.put(interfaceClassDoc, interfaceType); mcimadamore@184: List superInterfaces = getAllInterfaces(interfaceType, configuration, sort); mcimadamore@184: for (Iterator iter = superInterfaces.iterator(); iter.hasNext(); ) { mcimadamore@184: Type t = iter.next(); duke@1: results.put(t.asClassDoc(), t); duke@1: } duke@1: } duke@1: if (superType == null) jjg@74: return new ArrayList(results.values()); duke@1: //Try walking the tree. duke@1: addAllInterfaceTypes(results, duke@1: superType, duke@1: superType instanceof ClassDoc ? duke@1: ((ClassDoc) superType).interfaceTypes() : duke@1: ((ParameterizedType) superType).interfaceTypes(), duke@1: false, configuration); jjg@74: List resultsList = new ArrayList(results.values()); duke@1: if (sort) { duke@1: Collections.sort(resultsList, new TypeComparator()); duke@1: } duke@1: return resultsList; duke@1: } duke@1: mcimadamore@184: public static List getAllInterfaces(Type type, Configuration configuration) { duke@1: return getAllInterfaces(type, configuration, true); duke@1: } duke@1: jjg@74: private static void findAllInterfaceTypes(Map results, ClassDoc c, boolean raw, duke@1: Configuration configuration) { duke@1: Type superType = c.superclassType(); duke@1: if (superType == null) duke@1: return; duke@1: addAllInterfaceTypes(results, superType, duke@1: superType instanceof ClassDoc ? duke@1: ((ClassDoc) superType).interfaceTypes() : duke@1: ((ParameterizedType) superType).interfaceTypes(), duke@1: raw, configuration); duke@1: } duke@1: jjg@74: private static void findAllInterfaceTypes(Map results, ParameterizedType p, duke@1: Configuration configuration) { duke@1: Type superType = p.superclassType(); duke@1: if (superType == null) duke@1: return; duke@1: addAllInterfaceTypes(results, superType, duke@1: superType instanceof ClassDoc ? duke@1: ((ClassDoc) superType).interfaceTypes() : duke@1: ((ParameterizedType) superType).interfaceTypes(), duke@1: false, configuration); duke@1: } duke@1: jjg@74: private static void addAllInterfaceTypes(Map results, Type type, duke@1: Type[] interfaceTypes, boolean raw, duke@1: Configuration configuration) { duke@1: for (int i = 0; i < interfaceTypes.length; i++) { duke@1: Type interfaceType = interfaceTypes[i]; duke@1: ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); duke@1: if (! (interfaceClassDoc.isPublic() || duke@1: (configuration != null && duke@1: isLinkable(interfaceClassDoc, configuration)))) { duke@1: continue; duke@1: } duke@1: if (raw) duke@1: interfaceType = interfaceType.asClassDoc(); duke@1: results.put(interfaceClassDoc, interfaceType); mcimadamore@184: List superInterfaces = getAllInterfaces(interfaceType, configuration); mcimadamore@184: for (Iterator iter = superInterfaces.iterator(); iter.hasNext(); ) { mcimadamore@184: Type superInterface = iter.next(); duke@1: results.put(superInterface.asClassDoc(), superInterface); duke@1: } duke@1: } duke@1: if (type instanceof ParameterizedType) duke@1: findAllInterfaceTypes(results, (ParameterizedType) type, configuration); duke@1: else if (((ClassDoc) type).typeParameters().length == 0) duke@1: findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration); duke@1: else duke@1: findAllInterfaceTypes(results, (ClassDoc) type, true, configuration); duke@1: } duke@1: duke@1: mcimadamore@184: public static List asList(T[] members) { mcimadamore@184: List list = new ArrayList(); duke@1: for (int i = 0; i < members.length; i++) { duke@1: list.add(members[i]); duke@1: } duke@1: return list; duke@1: } duke@1: duke@1: /** duke@1: * Enclose in quotes, used for paths and filenames that contains spaces duke@1: */ duke@1: public static String quote(String filepath) { duke@1: return ("\"" + filepath + "\""); duke@1: } duke@1: duke@1: /** duke@1: * Given a package, return it's name. duke@1: * @param packageDoc the package to check. duke@1: * @return the name of the given package. duke@1: */ duke@1: public static String getPackageName(PackageDoc packageDoc) { duke@1: return packageDoc == null || packageDoc.name().length() == 0 ? duke@1: DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name(); duke@1: } duke@1: duke@1: /** duke@1: * Given a package, return it's file name without the extension. duke@1: * @param packageDoc the package to check. duke@1: * @return the file name of the given package. duke@1: */ duke@1: public static String getPackageFileHeadName(PackageDoc packageDoc) { duke@1: return packageDoc == null || packageDoc.name().length() == 0 ? duke@1: DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name(); duke@1: } duke@1: duke@1: /** duke@1: * Given a string, replace all occurraces of 'newStr' with 'oldStr'. duke@1: * @param originalStr the string to modify. duke@1: * @param oldStr the string to replace. duke@1: * @param newStr the string to insert in place of the old string. duke@1: */ duke@1: public static String replaceText(String originalStr, String oldStr, duke@1: String newStr) { duke@1: if (oldStr == null || newStr == null || oldStr.equals(newStr)) { duke@1: return originalStr; duke@1: } duke@1: StringBuffer result = new StringBuffer(originalStr); duke@1: int startIndex = 0; duke@1: while ((startIndex = result.indexOf(oldStr, startIndex)) != -1) { duke@1: result = result.replace(startIndex, startIndex + oldStr.length(), duke@1: newStr); duke@1: startIndex += newStr.length(); duke@1: } duke@1: return result.toString(); duke@1: } duke@1: duke@1: /** duke@1: * Given a string, escape all special html characters and duke@1: * return the result. duke@1: * duke@1: * @param s The string to check. duke@1: * @return the original string with all of the HTML characters duke@1: * escaped. duke@1: * duke@1: * @see #HTML_ESCAPE_CHARS duke@1: */ duke@1: public static String escapeHtmlChars(String s) { duke@1: String result = s; duke@1: for (int i = 0; i < HTML_ESCAPE_CHARS.length; i++) { duke@1: result = Util.replaceText(result, duke@1: HTML_ESCAPE_CHARS[i][0], HTML_ESCAPE_CHARS[i][1]); duke@1: } duke@1: return result; duke@1: } duke@1: duke@1: /** duke@1: * Create the directory path for the file to be generated, construct duke@1: * FileOutputStream and OutputStreamWriter depending upon docencoding. duke@1: * duke@1: * @param path The directory path to be created for this file. duke@1: * @param filename File Name to which the PrintWriter will do the Output. duke@1: * @param docencoding Encoding to be used for this file. duke@1: * @exception IOException Exception raised by the FileWriter is passed on duke@1: * to next level. jjg@197: * @exception UnsupportedEncodingException Exception raised by the duke@1: * OutputStreamWriter is passed on to next level. duke@1: * @return Writer Writer for the file getting generated. duke@1: * @see java.io.FileOutputStream duke@1: * @see java.io.OutputStreamWriter duke@1: */ duke@1: public static Writer genWriter(Configuration configuration, duke@1: String path, String filename, duke@1: String docencoding) duke@1: throws IOException, UnsupportedEncodingException { duke@1: FileOutputStream fos; duke@1: if (path != null) { duke@1: DirectoryManager.createDirectory(configuration, path); duke@1: fos = new FileOutputStream(((path.length() > 0)? duke@1: path + File.separator: "") + filename); duke@1: } else { duke@1: fos = new FileOutputStream(filename); duke@1: } duke@1: if (docencoding == null) { jjg@197: return new OutputStreamWriter(fos); duke@1: } else { duke@1: return new OutputStreamWriter(fos, docencoding); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Given an annotation, return true if it should be documented and false duke@1: * otherwise. duke@1: * duke@1: * @param annotationDoc the annotation to check. duke@1: * duke@1: * @return true return true if it should be documented and false otherwise. duke@1: */ duke@1: public static boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) { duke@1: AnnotationDesc[] annotationDescList = annotationDoc.annotations(); duke@1: for (int i = 0; i < annotationDescList.length; i++) { duke@1: if (annotationDescList[i].annotationType().qualifiedName().equals( duke@1: java.lang.annotation.Documented.class.getName())){ duke@1: return true; duke@1: } duke@1: } duke@1: return false; duke@1: } duke@1: duke@1: /** duke@1: * Given a string, return an array of tokens. The separator can be escaped duke@1: * with the '\' character. The '\' character may also be escaped by the duke@1: * '\' character. duke@1: * duke@1: * @param s the string to tokenize. duke@1: * @param separator the separator char. duke@1: * @param maxTokens the maxmimum number of tokens returned. If the duke@1: * max is reached, the remaining part of s is appended duke@1: * to the end of the last token. duke@1: * duke@1: * @return an array of tokens. duke@1: */ duke@1: public static String[] tokenize(String s, char separator, int maxTokens) { jjg@74: List tokens = new ArrayList(); duke@1: StringBuilder token = new StringBuilder (); duke@1: boolean prevIsEscapeChar = false; duke@1: for (int i = 0; i < s.length(); i += Character.charCount(i)) { duke@1: int currentChar = s.codePointAt(i); duke@1: if (prevIsEscapeChar) { duke@1: // Case 1: escaped character duke@1: token.appendCodePoint(currentChar); duke@1: prevIsEscapeChar = false; duke@1: } else if (currentChar == separator && tokens.size() < maxTokens-1) { duke@1: // Case 2: separator duke@1: tokens.add(token.toString()); duke@1: token = new StringBuilder(); duke@1: } else if (currentChar == '\\') { duke@1: // Case 3: escape character duke@1: prevIsEscapeChar = true; duke@1: } else { duke@1: // Case 4: regular character duke@1: token.appendCodePoint(currentChar); duke@1: } duke@1: } duke@1: if (token.length() > 0) { duke@1: tokens.add(token.toString()); duke@1: } jjg@74: return tokens.toArray(new String[] {}); duke@1: } duke@1: duke@1: /** duke@1: * Return true if this class is linkable and false if we can't link to the duke@1: * desired class. duke@1: *
duke@1: * NOTE: You can only link to external classes if they are public or duke@1: * protected. duke@1: * duke@1: * @param classDoc the class to check. duke@1: * @param configuration the current configuration of the doclet. duke@1: * duke@1: * @return true if this class is linkable and false if we can't link to the duke@1: * desired class. duke@1: */ duke@1: public static boolean isLinkable(ClassDoc classDoc, duke@1: Configuration configuration) { duke@1: return duke@1: ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) || duke@1: (configuration.extern.isExternal(classDoc) && duke@1: (classDoc.isPublic() || classDoc.isProtected())); duke@1: } duke@1: duke@1: /** duke@1: * Given a class, return the closest visible super class. duke@1: * duke@1: * @param classDoc the class we are searching the parent for. duke@1: * @param configuration the current configuration of the doclet. duke@1: * @return the closest visible super class. Return null if it cannot duke@1: * be found (i.e. classDoc is java.lang.Object). duke@1: */ duke@1: public static Type getFirstVisibleSuperClass(ClassDoc classDoc, duke@1: Configuration configuration) { duke@1: if (classDoc == null) { duke@1: return null; duke@1: } duke@1: Type sup = classDoc.superclassType(); duke@1: ClassDoc supClassDoc = classDoc.superclass(); duke@1: while (sup != null && duke@1: (! (supClassDoc.isPublic() || duke@1: isLinkable(supClassDoc, configuration))) ) { duke@1: if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName())) duke@1: break; duke@1: sup = supClassDoc.superclassType(); duke@1: supClassDoc = supClassDoc.superclass(); duke@1: } duke@1: if (classDoc.equals(supClassDoc)) { duke@1: return null; duke@1: } duke@1: return sup; duke@1: } duke@1: duke@1: /** duke@1: * Given a class, return the closest visible super class. duke@1: * duke@1: * @param classDoc the class we are searching the parent for. duke@1: * @param configuration the current configuration of the doclet. duke@1: * @return the closest visible super class. Return null if it cannot duke@1: * be found (i.e. classDoc is java.lang.Object). duke@1: */ duke@1: public static ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc, duke@1: Configuration configuration) { duke@1: if (classDoc == null) { duke@1: return null; duke@1: } duke@1: ClassDoc supClassDoc = classDoc.superclass(); duke@1: while (supClassDoc != null && duke@1: (! (supClassDoc.isPublic() || duke@1: isLinkable(supClassDoc, configuration))) ) { duke@1: supClassDoc = supClassDoc.superclass(); duke@1: } duke@1: if (classDoc.equals(supClassDoc)) { duke@1: return null; duke@1: } duke@1: return supClassDoc; duke@1: } duke@1: duke@1: /** duke@1: * Given a ClassDoc, return the name of its type (Class, Interface, etc.). duke@1: * duke@1: * @param cd the ClassDoc to check. duke@1: * @param lowerCaseOnly true if you want the name returned in lower case. duke@1: * If false, the first letter of the name is capatilized. duke@1: * @return duke@1: */ duke@1: public static String getTypeName(Configuration config, duke@1: ClassDoc cd, boolean lowerCaseOnly) { duke@1: String typeName = ""; duke@1: if (cd.isOrdinaryClass()) { duke@1: typeName = "doclet.Class"; duke@1: } else if (cd.isInterface()) { duke@1: typeName = "doclet.Interface"; duke@1: } else if (cd.isException()) { duke@1: typeName = "doclet.Exception"; duke@1: } else if (cd.isError()) { duke@1: typeName = "doclet.Error"; duke@1: } else if (cd.isAnnotationType()) { duke@1: typeName = "doclet.AnnotationType"; duke@1: } else if (cd.isEnum()) { duke@1: typeName = "doclet.Enum"; duke@1: } duke@1: return config.getText( duke@1: lowerCaseOnly ? typeName.toLowerCase() : typeName); duke@1: } duke@1: duke@1: /** duke@1: * Given a string, replace all tabs with the appropriate duke@1: * number of spaces. duke@1: * @param tabLength the length of each tab. duke@1: * @param s the String to scan. duke@1: */ duke@1: public static void replaceTabs(int tabLength, StringBuffer s) { duke@1: int index, col; duke@1: StringBuffer whitespace; duke@1: while ((index = s.indexOf("\t")) != -1) { duke@1: whitespace = new StringBuffer(); duke@1: col = index; duke@1: do { duke@1: whitespace.append(" "); duke@1: col++; duke@1: } while ((col%tabLength) != 0); duke@1: s.replace(index, index+1, whitespace.toString()); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * The documentation for values() and valueOf() in Enums are set by the duke@1: * doclet. duke@1: */ duke@1: public static void setEnumDocumentation(Configuration configuration, duke@1: ClassDoc classDoc) { duke@1: MethodDoc[] methods = classDoc.methods(); duke@1: for (int j = 0; j < methods.length; j++) { duke@1: MethodDoc currentMethod = methods[j]; duke@1: if (currentMethod.name().equals("values") && duke@1: currentMethod.parameters().length == 0) { duke@1: currentMethod.setRawCommentText( duke@1: configuration.getText("doclet.enum_values_doc", classDoc.name())); duke@1: } else if (currentMethod.name().equals("valueOf") && duke@1: currentMethod.parameters().length == 1) { duke@1: Type paramType = currentMethod.parameters()[0].type(); duke@1: if (paramType != null && duke@1: paramType.qualifiedTypeName().equals(String.class.getName())) { duke@1: currentMethod.setRawCommentText( duke@1: configuration.getText("doclet.enum_valueof_doc")); duke@1: } duke@1: } duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Return true if the given Doc is deprecated. duke@1: * duke@1: * @param doc the Doc to check. duke@1: * @return true if the given Doc is deprecated. duke@1: */ duke@1: public static boolean isDeprecated(ProgramElementDoc doc) { duke@1: if (doc.tags("deprecated").length > 0) { duke@1: return true; duke@1: } duke@1: AnnotationDesc[] annotationDescList = doc.annotations(); duke@1: for (int i = 0; i < annotationDescList.length; i++) { duke@1: if (annotationDescList[i].annotationType().qualifiedName().equals( duke@1: java.lang.Deprecated.class.getName())){ duke@1: return true; duke@1: } duke@1: } duke@1: return false; duke@1: } duke@1: }