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