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