src/share/classes/com/sun/tools/doclets/internal/toolkit/util/Util.java

Thu, 15 Jan 2009 18:06:36 -0800

author
jjg
date
Thu, 15 Jan 2009 18:06:36 -0800
changeset 192
42f9d392159d
parent 184
905e151a185a
child 197
1bf037016426
permissions
-rw-r--r--

6794520: MessageRetriever should be upgraded to use varargs Object...
Reviewed-by: bpatel

     1 /*
     2  * Copyright 1999-2008 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.tools.doclets.internal.toolkit.util;
    28 import com.sun.javadoc.*;
    29 import com.sun.tools.doclets.internal.toolkit.*;
    30 import java.util.*;
    31 import java.io.*;
    33 /**
    34  * Utilities Class for Doclets.
    35  *
    36  * This code is not part of an API.
    37  * It is implementation that is subject to change.
    38  * Do not use it as an API
    39  *
    40  * @author Atul M Dambalkar
    41  * @author Jamie Ho
    42  */
    43 public class Util {
    45     /**
    46      * A mapping between characters and their
    47      * corresponding HTML escape character.
    48      */
    49     public static final String[][] HTML_ESCAPE_CHARS =
    50     {{"&", "&amp;"}, {"<", "&lt;"}, {">", "&gt;"}};
    52     /**
    53      * Return array of class members whose documentation is to be generated.
    54      * If the member is deprecated do not include such a member in the
    55      * returned array.
    56      *
    57      * @param  members             Array of members to choose from.
    58      * @return ProgramElementDoc[] Array of eligible members for whom
    59      *                             documentation is getting generated.
    60      */
    61     public static ProgramElementDoc[] excludeDeprecatedMembers(
    62         ProgramElementDoc[] members) {
    63         return
    64             toProgramElementDocArray(excludeDeprecatedMembersAsList(members));
    65     }
    67     /**
    68      * Return array of class members whose documentation is to be generated.
    69      * If the member is deprecated do not include such a member in the
    70      * returned array.
    71      *
    72      * @param  members    Array of members to choose from.
    73      * @return List       List of eligible members for whom
    74      *                    documentation is getting generated.
    75      */
    76     public static List<ProgramElementDoc> excludeDeprecatedMembersAsList(
    77         ProgramElementDoc[] members) {
    78         List<ProgramElementDoc> list = new ArrayList<ProgramElementDoc>();
    79         for (int i = 0; i < members.length; i++) {
    80             if (members[i].tags("deprecated").length == 0) {
    81                 list.add(members[i]);
    82             }
    83         }
    84         Collections.sort(list);
    85         return list;
    86     }
    88     /**
    89      * Return the list of ProgramElementDoc objects as Array.
    90      */
    91     public static ProgramElementDoc[] toProgramElementDocArray(List<ProgramElementDoc> list) {
    92         ProgramElementDoc[] pgmarr = new ProgramElementDoc[list.size()];
    93         for (int i = 0; i < list.size(); i++) {
    94             pgmarr[i] = list.get(i);
    95         }
    96         return pgmarr;
    97     }
    99     /**
   100      * Return true if a non-public member found in the given array.
   101      *
   102      * @param  members Array of members to look into.
   103      * @return boolean True if non-public member found, false otherwise.
   104      */
   105     public static boolean nonPublicMemberFound(ProgramElementDoc[] members) {
   106         for (int i = 0; i < members.length; i++) {
   107             if (!members[i].isPublic()) {
   108                 return true;
   109             }
   110         }
   111         return false;
   112     }
   114     /**
   115      * Search for the given method in the given class.
   116      *
   117      * @param  cd        Class to search into.
   118      * @param  method    Method to be searched.
   119      * @return MethodDoc Method found, null otherwise.
   120      */
   121     public static MethodDoc findMethod(ClassDoc cd, MethodDoc method) {
   122         MethodDoc[] methods = cd.methods();
   123         for (int i = 0; i < methods.length; i++) {
   124             if (executableMembersEqual(method, methods[i])) {
   125                 return methods[i];
   127             }
   128         }
   129         return null;
   130     }
   132     /**
   133      * @param member1 the first method to compare.
   134      * @param member2 the second method to compare.
   135      * @return true if member1 overrides/hides or is overriden/hidden by member2.
   136      */
   137     public static boolean executableMembersEqual(ExecutableMemberDoc member1,
   138             ExecutableMemberDoc member2) {
   139         if (! (member1 instanceof MethodDoc && member2 instanceof MethodDoc))
   140             return false;
   142         MethodDoc method1 = (MethodDoc) member1;
   143         MethodDoc method2 = (MethodDoc) member2;
   144         if (method1.isStatic() && method2.isStatic()) {
   145             Parameter[] targetParams = method1.parameters();
   146             Parameter[] currentParams;
   147             if (method1.name().equals(method2.name()) &&
   148                    (currentParams = method2.parameters()).length ==
   149                 targetParams.length) {
   150                 int j;
   151                 for (j = 0; j < targetParams.length; j++) {
   152                     if (! (targetParams[j].typeName().equals(
   153                               currentParams[j].typeName()) ||
   154                                    currentParams[j].type() instanceof TypeVariable ||
   155                                    targetParams[j].type() instanceof TypeVariable)) {
   156                         break;
   157                     }
   158                 }
   159                 if (j == targetParams.length) {
   160                     return true;
   161                 }
   162             }
   163             return false;
   164         } else {
   165                 return method1.overrides(method2) ||
   166                 method2.overrides(method1) ||
   167                                 member1 == member2;
   168         }
   169     }
   171     /**
   172      * According to the Java Language Specifications, all the outer classes
   173      * and static inner classes are core classes.
   174      */
   175     public static boolean isCoreClass(ClassDoc cd) {
   176         return cd.containingClass() == null || cd.isStatic();
   177     }
   179     public static boolean matches(ProgramElementDoc doc1,
   180             ProgramElementDoc doc2) {
   181         if (doc1 instanceof ExecutableMemberDoc &&
   182             doc2 instanceof ExecutableMemberDoc) {
   183             ExecutableMemberDoc ed1 = (ExecutableMemberDoc)doc1;
   184             ExecutableMemberDoc ed2 = (ExecutableMemberDoc)doc2;
   185             return executableMembersEqual(ed1, ed2);
   186         } else {
   187             return doc1.name().equals(doc2.name());
   188         }
   189     }
   191     /**
   192      * Copy source file to destination file.
   193      *
   194      * @throws SecurityException
   195      * @throws IOException
   196      */
   197     public static void copyFile(File destfile, File srcfile)
   198         throws IOException {
   199         byte[] bytearr = new byte[512];
   200         int len = 0;
   201         FileInputStream input = new FileInputStream(srcfile);
   202         File destDir = destfile.getParentFile();
   203         destDir.mkdirs();
   204         FileOutputStream output = new FileOutputStream(destfile);
   205         try {
   206             while ((len = input.read(bytearr)) != -1) {
   207                 output.write(bytearr, 0, len);
   208             }
   209         } catch (FileNotFoundException exc) {
   210         } catch (SecurityException exc) {
   211         } finally {
   212             input.close();
   213             output.close();
   214         }
   215     }
   217     /**
   218      * Copy the given directory contents from the source package directory
   219      * to the generated documentation directory. For example for a package
   220      * java.lang this method find out the source location of the package using
   221      * {@link SourcePath} and if given directory is found in the source
   222      * directory structure, copy the entire directory, to the generated
   223      * documentation hierarchy.
   224      *
   225      * @param configuration The configuration of the current doclet.
   226      * @param path The relative path to the directory to be copied.
   227      * @param dir The original directory name to copy from.
   228      * @param overwrite Overwrite files if true.
   229      */
   230     public static void copyDocFiles(Configuration configuration,
   231             String path, String dir, boolean overwrite) {
   232         if (checkCopyDocFilesErrors(configuration, path, dir)) {
   233             return;
   234         }
   235         String destname = configuration.docFileDestDirName;
   236         File srcdir = new File(path + dir);
   237         if (destname.length() > 0 && !destname.endsWith(
   238                DirectoryManager.URL_FILE_SEPERATOR)) {
   239             destname += DirectoryManager.URL_FILE_SEPERATOR;
   240         }
   241         String dest = destname + dir;
   242         try {
   243             File destdir = new File(dest);
   244             DirectoryManager.createDirectory(configuration, dest);
   245             String[] files = srcdir.list();
   246             for (int i = 0; i < files.length; i++) {
   247                 File srcfile = new File(srcdir, files[i]);
   248                 File destfile = new File(destdir, files[i]);
   249                 if (srcfile.isFile()) {
   250                     if(destfile.exists() && ! overwrite) {
   251                         configuration.message.warning((SourcePosition) null,
   252                                 "doclet.Copy_Overwrite_warning",
   253                                 srcfile.toString(), destdir.toString());
   254                     } else {
   255                         configuration.message.notice(
   256                             "doclet.Copying_File_0_To_Dir_1",
   257                             srcfile.toString(), destdir.toString());
   258                         Util.copyFile(destfile, srcfile);
   259                     }
   260                 } else if(srcfile.isDirectory()) {
   261                     if(configuration.copydocfilesubdirs
   262                         && ! configuration.shouldExcludeDocFileDir(
   263                           srcfile.getName())){
   264                         copyDocFiles(configuration, path, dir +
   265                                     DirectoryManager.URL_FILE_SEPERATOR + srcfile.getName(),
   266                                 overwrite);
   267                     }
   268                 }
   269             }
   270         } catch (SecurityException exc) {
   271             throw new DocletAbortException();
   272         } catch (IOException exc) {
   273             throw new DocletAbortException();
   274         }
   275     }
   277     /**
   278      * Given the parameters for copying doc-files, check for errors.
   279      *
   280      * @param configuration The configuration of the current doclet.
   281      * @param path The relative path to the directory to be copied.
   282      * @param dirName The original directory name to copy from.
   283      */
   284     private static boolean checkCopyDocFilesErrors (Configuration configuration,
   285             String path, String dirName) {
   286         if ((configuration.sourcepath == null || configuration.sourcepath.length() == 0) &&
   287                (configuration.destDirName == null || configuration.destDirName.length() == 0)) {
   288             //The destination path and source path are definitely equal.
   289             return true;
   290         }
   291         File sourcePath, destPath = new File(configuration.destDirName);
   292         StringTokenizer pathTokens = new StringTokenizer(
   293             configuration.sourcepath == null ? "" : configuration.sourcepath,
   294             File.pathSeparator);
   295         //Check if the destination path is equal to the source path.  If yes,
   296         //do not copy doc-file directories.
   297         while(pathTokens.hasMoreTokens()){
   298             sourcePath = new File(pathTokens.nextToken());
   299             if(destPath.equals(sourcePath)){
   300                 return true;
   301             }
   302         }
   303         //Make sure the doc-file being copied exists.
   304         File srcdir = new File(path + dirName);
   305         if (! srcdir.exists()) {
   306             return true;
   307         }
   308         return false;
   309     }
   311     /**
   312      * Copy a file in the resources directory to the destination
   313      * directory (if it is not there already).  If
   314      * <code>overwrite</code> is true and the destination file
   315      * already exists, overwrite it.
   316      *
   317      * @param configuration  Holds the destination directory and error message
   318      * @param resourcefile   The name of the resource file to copy
   319      * @param overwrite      A flag to indicate whether the file in the
   320      *                       destination directory will be overwritten if
   321      *                       it already exists.
   322      */
   323     public static void copyResourceFile(Configuration configuration,
   324             String resourcefile,
   325             boolean overwrite) {
   326         String destdir = configuration.destDirName;
   327         String destresourcesdir = destdir + "resources";
   328         DirectoryManager.createDirectory(configuration, destresourcesdir);
   329         File destfile = new File(destresourcesdir, resourcefile);
   330         if(destfile.exists() && (! overwrite)) return;
   331         try {
   333             InputStream in = Configuration.class.getResourceAsStream(
   334                 "resources/" + resourcefile);
   336             if(in==null) return;
   338             OutputStream out = new FileOutputStream(destfile);
   339             byte[] buf = new byte[2048];
   340             int n;
   341             while((n = in.read(buf))>0) out.write(buf,0,n);
   343             in.close();
   344             out.close();
   345         } catch(Throwable t) {}
   346     }
   348     /**
   349      * Given a PackageDoc, return the source path for that package.
   350      * @param configuration The Configuration for the current Doclet.
   351      * @param pkgDoc The package to seach the path for.
   352      * @return A string representing the path to the given package.
   353      */
   354     public static String getPackageSourcePath(Configuration configuration,
   355             PackageDoc pkgDoc){
   356         try{
   357             String pkgPath = DirectoryManager.getDirectoryPath(pkgDoc);
   358             String completePath = new SourcePath(configuration.sourcepath).
   359                 getDirectory(pkgPath) + DirectoryManager.URL_FILE_SEPERATOR;
   360             //Make sure that both paths are using the same seperators.
   361             completePath = Util.replaceText(completePath, File.separator,
   362                     DirectoryManager.URL_FILE_SEPERATOR);
   363             pkgPath = Util.replaceText(pkgPath, File.separator,
   364                     DirectoryManager.URL_FILE_SEPERATOR);
   365             return completePath.substring(0, completePath.indexOf(pkgPath));
   366         } catch (Exception e){
   367             return "";
   368         }
   369     }
   371     /**
   372      * We want the list of types in alphabetical order.  However, types are not
   373      * comparable.  We need a comparator for now.
   374      */
   375     private static class TypeComparator implements Comparator<Type> {
   376         public int compare(Type type1, Type type2) {
   377             return type1.qualifiedTypeName().toLowerCase().compareTo(
   378                 type2.qualifiedTypeName().toLowerCase());
   379         }
   380     }
   382     /**
   383      * For the class return all implemented interfaces including the
   384      * superinterfaces of the implementing interfaces, also iterate over for
   385      * all the superclasses. For interface return all the extended interfaces
   386      * as well as superinterfaces for those extended interfaces.
   387      *
   388      * @param  type       type whose implemented or
   389      *                    super interfaces are sought.
   390      * @param  configuration the current configuration of the doclet.
   391      * @param  sort if true, return list of interfaces sorted alphabetically.
   392      * @return List of all the required interfaces.
   393      */
   394     public static List<Type> getAllInterfaces(Type type,
   395             Configuration configuration, boolean sort) {
   396         Map<ClassDoc,Type> results = sort ? new TreeMap<ClassDoc,Type>() : new LinkedHashMap<ClassDoc,Type>();
   397         Type[] interfaceTypes = null;
   398         Type superType = null;
   399         if (type instanceof ParameterizedType) {
   400             interfaceTypes = ((ParameterizedType) type).interfaceTypes();
   401             superType = ((ParameterizedType) type).superclassType();
   402         } else if (type instanceof ClassDoc) {
   403             interfaceTypes = ((ClassDoc) type).interfaceTypes();
   404             superType = ((ClassDoc) type).superclassType();
   405         } else {
   406             interfaceTypes = type.asClassDoc().interfaceTypes();
   407             superType = type.asClassDoc().superclassType();
   408         }
   410         for (int i = 0; i < interfaceTypes.length; i++) {
   411             Type interfaceType = interfaceTypes[i];
   412             ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
   413             if (! (interfaceClassDoc.isPublic() ||
   414                 (configuration == null ||
   415                 isLinkable(interfaceClassDoc, configuration)))) {
   416                 continue;
   417             }
   418             results.put(interfaceClassDoc, interfaceType);
   419             List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration, sort);
   420             for (Iterator<Type> iter = superInterfaces.iterator(); iter.hasNext(); ) {
   421                 Type t = iter.next();
   422                 results.put(t.asClassDoc(), t);
   423             }
   424         }
   425         if (superType == null)
   426             return new ArrayList<Type>(results.values());
   427         //Try walking the tree.
   428         addAllInterfaceTypes(results,
   429             superType,
   430             superType instanceof ClassDoc ?
   431                 ((ClassDoc) superType).interfaceTypes() :
   432                 ((ParameterizedType) superType).interfaceTypes(),
   433             false, configuration);
   434         List<Type> resultsList = new ArrayList<Type>(results.values());
   435         if (sort) {
   436                 Collections.sort(resultsList, new TypeComparator());
   437         }
   438         return resultsList;
   439     }
   441     public static List<Type> getAllInterfaces(Type type, Configuration configuration) {
   442         return getAllInterfaces(type, configuration, true);
   443     }
   445     private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ClassDoc c, boolean raw,
   446             Configuration configuration) {
   447         Type superType = c.superclassType();
   448         if (superType == null)
   449             return;
   450         addAllInterfaceTypes(results, superType,
   451                 superType instanceof ClassDoc ?
   452                 ((ClassDoc) superType).interfaceTypes() :
   453                 ((ParameterizedType) superType).interfaceTypes(),
   454                 raw, configuration);
   455     }
   457     private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ParameterizedType p,
   458             Configuration configuration) {
   459         Type superType = p.superclassType();
   460         if (superType == null)
   461             return;
   462         addAllInterfaceTypes(results, superType,
   463                 superType instanceof ClassDoc ?
   464                 ((ClassDoc) superType).interfaceTypes() :
   465                 ((ParameterizedType) superType).interfaceTypes(),
   466                 false, configuration);
   467     }
   469     private static void addAllInterfaceTypes(Map<ClassDoc,Type> results, Type type,
   470             Type[] interfaceTypes, boolean raw,
   471             Configuration configuration) {
   472         for (int i = 0; i < interfaceTypes.length; i++) {
   473             Type interfaceType = interfaceTypes[i];
   474             ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
   475             if (! (interfaceClassDoc.isPublic() ||
   476                 (configuration != null &&
   477                 isLinkable(interfaceClassDoc, configuration)))) {
   478                 continue;
   479             }
   480             if (raw)
   481                 interfaceType = interfaceType.asClassDoc();
   482             results.put(interfaceClassDoc, interfaceType);
   483             List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration);
   484             for (Iterator<Type> iter = superInterfaces.iterator(); iter.hasNext(); ) {
   485                 Type superInterface = iter.next();
   486                 results.put(superInterface.asClassDoc(), superInterface);
   487             }
   488         }
   489         if (type instanceof ParameterizedType)
   490             findAllInterfaceTypes(results, (ParameterizedType) type, configuration);
   491         else if (((ClassDoc) type).typeParameters().length == 0)
   492             findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration);
   493         else
   494             findAllInterfaceTypes(results, (ClassDoc) type, true, configuration);
   495     }
   498     public static <T extends ProgramElementDoc> List<T> asList(T[] members) {
   499         List<T> list = new ArrayList<T>();
   500         for (int i = 0; i < members.length; i++) {
   501             list.add(members[i]);
   502         }
   503         return list;
   504     }
   506     /**
   507      * Enclose in quotes, used for paths and filenames that contains spaces
   508      */
   509     public static String quote(String filepath) {
   510         return ("\"" + filepath + "\"");
   511     }
   513     /**
   514      * Given a package, return it's name.
   515      * @param packageDoc the package to check.
   516      * @return the name of the given package.
   517      */
   518     public static String getPackageName(PackageDoc packageDoc) {
   519         return packageDoc == null || packageDoc.name().length() == 0 ?
   520             DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name();
   521     }
   523     /**
   524      * Given a package, return it's file name without the extension.
   525      * @param packageDoc the package to check.
   526      * @return the file name of the given package.
   527      */
   528     public static String getPackageFileHeadName(PackageDoc packageDoc) {
   529         return packageDoc == null || packageDoc.name().length() == 0 ?
   530             DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name();
   531     }
   533     /**
   534      * Given a string, replace all occurraces of 'newStr' with 'oldStr'.
   535      * @param originalStr the string to modify.
   536      * @param oldStr the string to replace.
   537      * @param newStr the string to insert in place of the old string.
   538      */
   539     public static String replaceText(String originalStr, String oldStr,
   540             String newStr) {
   541         if (oldStr == null || newStr == null || oldStr.equals(newStr)) {
   542             return originalStr;
   543         }
   544         StringBuffer result = new StringBuffer(originalStr);
   545         int startIndex = 0;
   546         while ((startIndex = result.indexOf(oldStr, startIndex)) != -1) {
   547             result = result.replace(startIndex, startIndex + oldStr.length(),
   548                     newStr);
   549             startIndex += newStr.length();
   550         }
   551         return result.toString();
   552     }
   554     /**
   555      * Given a string, escape all special html characters and
   556      * return the result.
   557      *
   558      * @param s The string to check.
   559      * @return the original string with all of the HTML characters
   560      * escaped.
   561      *
   562      * @see #HTML_ESCAPE_CHARS
   563      */
   564     public static String escapeHtmlChars(String s) {
   565         String result = s;
   566         for (int i = 0; i < HTML_ESCAPE_CHARS.length; i++) {
   567             result = Util.replaceText(result,
   568                     HTML_ESCAPE_CHARS[i][0], HTML_ESCAPE_CHARS[i][1]);
   569         }
   570         return result;
   571     }
   573     /**
   574      * Create the directory path for the file to be generated, construct
   575      * FileOutputStream and OutputStreamWriter depending upon docencoding.
   576      *
   577      * @param path The directory path to be created for this file.
   578      * @param filename File Name to which the PrintWriter will do the Output.
   579      * @param docencoding Encoding to be used for this file.
   580      * @exception IOException Exception raised by the FileWriter is passed on
   581      * to next level.
   582      * @exception UnSupportedEncodingException Exception raised by the
   583      * OutputStreamWriter is passed on to next level.
   584      * @return Writer Writer for the file getting generated.
   585      * @see java.io.FileOutputStream
   586      * @see java.io.OutputStreamWriter
   587      */
   588     public static Writer genWriter(Configuration configuration,
   589             String path, String filename,
   590             String docencoding)
   591         throws IOException, UnsupportedEncodingException {
   592         FileOutputStream fos;
   593         if (path != null) {
   594             DirectoryManager.createDirectory(configuration, path);
   595             fos = new FileOutputStream(((path.length() > 0)?
   596                                                   path + File.separator: "") + filename);
   597         } else {
   598             fos = new FileOutputStream(filename);
   599         }
   600         if (docencoding == null) {
   601             OutputStreamWriter oswriter = new OutputStreamWriter(fos);
   602             return oswriter;
   603         } else {
   604             return new OutputStreamWriter(fos, docencoding);
   605         }
   606     }
   608     /**
   609      * Given an annotation, return true if it should be documented and false
   610      * otherwise.
   611      *
   612      * @param annotationDoc the annotation to check.
   613      *
   614      * @return true return true if it should be documented and false otherwise.
   615      */
   616     public static boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) {
   617         AnnotationDesc[] annotationDescList = annotationDoc.annotations();
   618         for (int i = 0; i < annotationDescList.length; i++) {
   619             if (annotationDescList[i].annotationType().qualifiedName().equals(
   620                    java.lang.annotation.Documented.class.getName())){
   621                 return true;
   622             }
   623         }
   624         return false;
   625     }
   627     /**
   628      * Given a string, return an array of tokens.  The separator can be escaped
   629      * with the '\' character.  The '\' character may also be escaped by the
   630      * '\' character.
   631      *
   632      * @param s         the string to tokenize.
   633      * @param separator the separator char.
   634      * @param maxTokens the maxmimum number of tokens returned.  If the
   635      *                  max is reached, the remaining part of s is appended
   636      *                  to the end of the last token.
   637      *
   638      * @return an array of tokens.
   639      */
   640     public static String[] tokenize(String s, char separator, int maxTokens) {
   641         List<String> tokens = new ArrayList<String>();
   642         StringBuilder  token = new StringBuilder ();
   643         boolean prevIsEscapeChar = false;
   644         for (int i = 0; i < s.length(); i += Character.charCount(i)) {
   645             int currentChar = s.codePointAt(i);
   646             if (prevIsEscapeChar) {
   647                 // Case 1:  escaped character
   648                 token.appendCodePoint(currentChar);
   649                 prevIsEscapeChar = false;
   650             } else if (currentChar == separator && tokens.size() < maxTokens-1) {
   651                 // Case 2:  separator
   652                 tokens.add(token.toString());
   653                 token = new StringBuilder();
   654             } else if (currentChar == '\\') {
   655                 // Case 3:  escape character
   656                 prevIsEscapeChar = true;
   657             } else {
   658                 // Case 4:  regular character
   659                 token.appendCodePoint(currentChar);
   660             }
   661         }
   662         if (token.length() > 0) {
   663             tokens.add(token.toString());
   664         }
   665         return tokens.toArray(new String[] {});
   666     }
   668     /**
   669      * Return true if this class is linkable and false if we can't link to the
   670      * desired class.
   671      * <br>
   672      * <b>NOTE:</b>  You can only link to external classes if they are public or
   673      * protected.
   674      *
   675      * @param classDoc the class to check.
   676      * @param configuration the current configuration of the doclet.
   677      *
   678      * @return true if this class is linkable and false if we can't link to the
   679      * desired class.
   680      */
   681     public static boolean isLinkable(ClassDoc classDoc,
   682             Configuration configuration) {
   683         return
   684             ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) ||
   685             (configuration.extern.isExternal(classDoc) &&
   686                 (classDoc.isPublic() || classDoc.isProtected()));
   687     }
   689     /**
   690      * Given a class, return the closest visible super class.
   691      *
   692      * @param classDoc the class we are searching the parent for.
   693      * @param configuration the current configuration of the doclet.
   694      * @return the closest visible super class.  Return null if it cannot
   695      *         be found (i.e. classDoc is java.lang.Object).
   696      */
   697     public static Type getFirstVisibleSuperClass(ClassDoc classDoc,
   698             Configuration configuration) {
   699         if (classDoc == null) {
   700             return null;
   701         }
   702         Type sup = classDoc.superclassType();
   703         ClassDoc supClassDoc = classDoc.superclass();
   704         while (sup != null &&
   705                   (! (supClassDoc.isPublic() ||
   706                               isLinkable(supClassDoc, configuration))) ) {
   707             if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName()))
   708                 break;
   709             sup = supClassDoc.superclassType();
   710             supClassDoc = supClassDoc.superclass();
   711         }
   712         if (classDoc.equals(supClassDoc)) {
   713             return null;
   714         }
   715         return sup;
   716     }
   718     /**
   719      * Given a class, return the closest visible super class.
   720      *
   721      * @param classDoc the class we are searching the parent for.
   722      * @param configuration the current configuration of the doclet.
   723      * @return the closest visible super class.  Return null if it cannot
   724      *         be found (i.e. classDoc is java.lang.Object).
   725      */
   726     public static ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc,
   727             Configuration configuration) {
   728         if (classDoc == null) {
   729             return null;
   730         }
   731         ClassDoc supClassDoc = classDoc.superclass();
   732         while (supClassDoc != null &&
   733                   (! (supClassDoc.isPublic() ||
   734                               isLinkable(supClassDoc, configuration))) ) {
   735             supClassDoc = supClassDoc.superclass();
   736         }
   737         if (classDoc.equals(supClassDoc)) {
   738             return null;
   739         }
   740         return supClassDoc;
   741     }
   743     /**
   744      * Given a ClassDoc, return the name of its type (Class, Interface, etc.).
   745      *
   746      * @param cd the ClassDoc to check.
   747      * @param lowerCaseOnly true if you want the name returned in lower case.
   748      *                      If false, the first letter of the name is capatilized.
   749      * @return
   750      */
   751     public static String getTypeName(Configuration config,
   752         ClassDoc cd, boolean lowerCaseOnly) {
   753         String typeName = "";
   754         if (cd.isOrdinaryClass()) {
   755             typeName = "doclet.Class";
   756         } else if (cd.isInterface()) {
   757             typeName = "doclet.Interface";
   758         } else if (cd.isException()) {
   759             typeName = "doclet.Exception";
   760         } else if (cd.isError()) {
   761             typeName = "doclet.Error";
   762         } else if (cd.isAnnotationType()) {
   763             typeName = "doclet.AnnotationType";
   764         } else if (cd.isEnum()) {
   765             typeName = "doclet.Enum";
   766         }
   767         return config.getText(
   768             lowerCaseOnly ? typeName.toLowerCase() : typeName);
   769     }
   771     /**
   772      * Given a string, replace all tabs with the appropriate
   773      * number of spaces.
   774      * @param tabLength the length of each tab.
   775      * @param s the String to scan.
   776      */
   777     public static void replaceTabs(int tabLength, StringBuffer s) {
   778         int index, col;
   779         StringBuffer whitespace;
   780         while ((index = s.indexOf("\t")) != -1) {
   781             whitespace = new StringBuffer();
   782             col = index;
   783             do {
   784                 whitespace.append(" ");
   785                 col++;
   786             } while ((col%tabLength) != 0);
   787             s.replace(index, index+1, whitespace.toString());
   788         }
   789     }
   791     /**
   792      * The documentation for values() and valueOf() in Enums are set by the
   793      * doclet.
   794      */
   795     public static void setEnumDocumentation(Configuration configuration,
   796             ClassDoc classDoc) {
   797         MethodDoc[] methods = classDoc.methods();
   798         for (int j = 0; j < methods.length; j++) {
   799             MethodDoc currentMethod = methods[j];
   800             if (currentMethod.name().equals("values") &&
   801                 currentMethod.parameters().length == 0) {
   802                 currentMethod.setRawCommentText(
   803                     configuration.getText("doclet.enum_values_doc", classDoc.name()));
   804             } else if (currentMethod.name().equals("valueOf") &&
   805                 currentMethod.parameters().length == 1) {
   806                 Type paramType = currentMethod.parameters()[0].type();
   807                 if (paramType != null &&
   808                     paramType.qualifiedTypeName().equals(String.class.getName())) {
   809                     currentMethod.setRawCommentText(
   810                         configuration.getText("doclet.enum_valueof_doc"));
   811                 }
   812             }
   813         }
   814     }
   816     /**
   817      *  Return true if the given Doc is deprecated.
   818      *
   819      * @param doc the Doc to check.
   820      * @return true if the given Doc is deprecated.
   821      */
   822     public static boolean isDeprecated(ProgramElementDoc doc) {
   823         if (doc.tags("deprecated").length > 0) {
   824             return true;
   825         }
   826         AnnotationDesc[] annotationDescList = doc.annotations();
   827         for (int i = 0; i < annotationDescList.length; i++) {
   828             if (annotationDescList[i].annotationType().qualifiedName().equals(
   829                    java.lang.Deprecated.class.getName())){
   830                 return true;
   831             }
   832         }
   833         return false;
   834     }
   835 }

mercurial