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