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