Fri, 04 Mar 2011 19:53:03 -0800
6866185: Util.getPackageSourcePath should use lastIndexOf not indexOf and related cleanup
Reviewed-by: bpatel
1 /*
2 * Copyright (c) 1999, 2011, 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, false);
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 * @param replaceNewLine true if the newline needs to be replaced with platform-
349 * specific newline.
350 */
351 public static void copyFile(Configuration configuration, String file, String source,
352 String destination, boolean overwrite, boolean replaceNewLine) {
353 DirectoryManager.createDirectory(configuration, destination);
354 File destfile = new File(destination, file);
355 if(destfile.exists() && (! overwrite)) return;
356 try {
357 InputStream in = Configuration.class.getResourceAsStream(
358 source + DirectoryManager.URL_FILE_SEPARATOR + file);
359 if(in==null) return;
360 OutputStream out = new FileOutputStream(destfile);
361 try {
362 if (!replaceNewLine) {
363 byte[] buf = new byte[2048];
364 int n;
365 while((n = in.read(buf))>0) out.write(buf,0,n);
366 } else {
367 BufferedReader reader = new BufferedReader(new InputStreamReader(in));
368 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
369 try {
370 String line;
371 while ((line = reader.readLine()) != null) {
372 writer.write(line);
373 writer.write(DocletConstants.NL);
374 }
375 } finally {
376 reader.close();
377 writer.close();
378 }
379 }
380 } finally {
381 in.close();
382 out.close();
383 }
384 } catch (IOException ie) {
385 ie.printStackTrace(System.err);
386 throw new DocletAbortException();
387 }
388 }
390 /**
391 * Given a PackageDoc, return the source path for that package.
392 * @param configuration The Configuration for the current Doclet.
393 * @param pkgDoc The package to seach the path for.
394 * @return A string representing the path to the given package.
395 */
396 public static String getPackageSourcePath(Configuration configuration,
397 PackageDoc pkgDoc){
398 try{
399 String pkgPath = DirectoryManager.getDirectoryPath(pkgDoc);
400 String completePath = new SourcePath(configuration.sourcepath).
401 getDirectory(pkgPath) + DirectoryManager.URL_FILE_SEPARATOR;
402 //Make sure that both paths are using the same separators.
403 completePath = Util.replaceText(completePath, File.separator,
404 DirectoryManager.URL_FILE_SEPARATOR);
405 pkgPath = Util.replaceText(pkgPath, File.separator,
406 DirectoryManager.URL_FILE_SEPARATOR);
407 return completePath.substring(0, completePath.lastIndexOf(pkgPath));
408 } catch (Exception e){
409 return "";
410 }
411 }
413 /**
414 * We want the list of types in alphabetical order. However, types are not
415 * comparable. We need a comparator for now.
416 */
417 private static class TypeComparator implements Comparator<Type> {
418 public int compare(Type type1, Type type2) {
419 return type1.qualifiedTypeName().toLowerCase().compareTo(
420 type2.qualifiedTypeName().toLowerCase());
421 }
422 }
424 /**
425 * For the class return all implemented interfaces including the
426 * superinterfaces of the implementing interfaces, also iterate over for
427 * all the superclasses. For interface return all the extended interfaces
428 * as well as superinterfaces for those extended interfaces.
429 *
430 * @param type type whose implemented or
431 * super interfaces are sought.
432 * @param configuration the current configuration of the doclet.
433 * @param sort if true, return list of interfaces sorted alphabetically.
434 * @return List of all the required interfaces.
435 */
436 public static List<Type> getAllInterfaces(Type type,
437 Configuration configuration, boolean sort) {
438 Map<ClassDoc,Type> results = sort ? new TreeMap<ClassDoc,Type>() : new LinkedHashMap<ClassDoc,Type>();
439 Type[] interfaceTypes = null;
440 Type superType = null;
441 if (type instanceof ParameterizedType) {
442 interfaceTypes = ((ParameterizedType) type).interfaceTypes();
443 superType = ((ParameterizedType) type).superclassType();
444 } else if (type instanceof ClassDoc) {
445 interfaceTypes = ((ClassDoc) type).interfaceTypes();
446 superType = ((ClassDoc) type).superclassType();
447 } else {
448 interfaceTypes = type.asClassDoc().interfaceTypes();
449 superType = type.asClassDoc().superclassType();
450 }
452 for (int i = 0; i < interfaceTypes.length; i++) {
453 Type interfaceType = interfaceTypes[i];
454 ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
455 if (! (interfaceClassDoc.isPublic() ||
456 (configuration == null ||
457 isLinkable(interfaceClassDoc, configuration)))) {
458 continue;
459 }
460 results.put(interfaceClassDoc, interfaceType);
461 List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration, sort);
462 for (Iterator<Type> iter = superInterfaces.iterator(); iter.hasNext(); ) {
463 Type t = iter.next();
464 results.put(t.asClassDoc(), t);
465 }
466 }
467 if (superType == null)
468 return new ArrayList<Type>(results.values());
469 //Try walking the tree.
470 addAllInterfaceTypes(results,
471 superType,
472 superType instanceof ClassDoc ?
473 ((ClassDoc) superType).interfaceTypes() :
474 ((ParameterizedType) superType).interfaceTypes(),
475 false, configuration);
476 List<Type> resultsList = new ArrayList<Type>(results.values());
477 if (sort) {
478 Collections.sort(resultsList, new TypeComparator());
479 }
480 return resultsList;
481 }
483 public static List<Type> getAllInterfaces(Type type, Configuration configuration) {
484 return getAllInterfaces(type, configuration, true);
485 }
487 private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ClassDoc c, boolean raw,
488 Configuration configuration) {
489 Type superType = c.superclassType();
490 if (superType == null)
491 return;
492 addAllInterfaceTypes(results, superType,
493 superType instanceof ClassDoc ?
494 ((ClassDoc) superType).interfaceTypes() :
495 ((ParameterizedType) superType).interfaceTypes(),
496 raw, configuration);
497 }
499 private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ParameterizedType p,
500 Configuration configuration) {
501 Type superType = p.superclassType();
502 if (superType == null)
503 return;
504 addAllInterfaceTypes(results, superType,
505 superType instanceof ClassDoc ?
506 ((ClassDoc) superType).interfaceTypes() :
507 ((ParameterizedType) superType).interfaceTypes(),
508 false, configuration);
509 }
511 private static void addAllInterfaceTypes(Map<ClassDoc,Type> results, Type type,
512 Type[] interfaceTypes, boolean raw,
513 Configuration configuration) {
514 for (int i = 0; i < interfaceTypes.length; i++) {
515 Type interfaceType = interfaceTypes[i];
516 ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
517 if (! (interfaceClassDoc.isPublic() ||
518 (configuration != null &&
519 isLinkable(interfaceClassDoc, configuration)))) {
520 continue;
521 }
522 if (raw)
523 interfaceType = interfaceType.asClassDoc();
524 results.put(interfaceClassDoc, interfaceType);
525 List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration);
526 for (Iterator<Type> iter = superInterfaces.iterator(); iter.hasNext(); ) {
527 Type superInterface = iter.next();
528 results.put(superInterface.asClassDoc(), superInterface);
529 }
530 }
531 if (type instanceof ParameterizedType)
532 findAllInterfaceTypes(results, (ParameterizedType) type, configuration);
533 else if (((ClassDoc) type).typeParameters().length == 0)
534 findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration);
535 else
536 findAllInterfaceTypes(results, (ClassDoc) type, true, configuration);
537 }
539 /**
540 * Enclose in quotes, used for paths and filenames that contains spaces
541 */
542 public static String quote(String filepath) {
543 return ("\"" + filepath + "\"");
544 }
546 /**
547 * Given a package, return it's name.
548 * @param packageDoc the package to check.
549 * @return the name of the given package.
550 */
551 public static String getPackageName(PackageDoc packageDoc) {
552 return packageDoc == null || packageDoc.name().length() == 0 ?
553 DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name();
554 }
556 /**
557 * Given a package, return it's file name without the extension.
558 * @param packageDoc the package to check.
559 * @return the file name of the given package.
560 */
561 public static String getPackageFileHeadName(PackageDoc packageDoc) {
562 return packageDoc == null || packageDoc.name().length() == 0 ?
563 DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name();
564 }
566 /**
567 * Given a string, replace all occurraces of 'newStr' with 'oldStr'.
568 * @param originalStr the string to modify.
569 * @param oldStr the string to replace.
570 * @param newStr the string to insert in place of the old string.
571 */
572 public static String replaceText(String originalStr, String oldStr,
573 String newStr) {
574 if (oldStr == null || newStr == null || oldStr.equals(newStr)) {
575 return originalStr;
576 }
577 return originalStr.replace(oldStr, newStr);
578 }
580 /**
581 * Given a string, escape all special html characters and
582 * return the result.
583 *
584 * @param s The string to check.
585 * @return the original string with all of the HTML characters
586 * escaped.
587 *
588 * @see #HTML_ESCAPE_CHARS
589 */
590 public static String escapeHtmlChars(String s) {
591 String result = s;
592 for (int i = 0; i < HTML_ESCAPE_CHARS.length; i++) {
593 result = Util.replaceText(result,
594 HTML_ESCAPE_CHARS[i][0], HTML_ESCAPE_CHARS[i][1]);
595 }
596 return result;
597 }
599 /**
600 * Given a string, strips all html characters and
601 * return the result.
602 *
603 * @param rawString The string to check.
604 * @return the original string with all of the HTML characters
605 * stripped.
606 *
607 */
608 public static String stripHtml(String rawString) {
609 // remove HTML tags
610 rawString = rawString.replaceAll("\\<.*?>", " ");
611 // consolidate multiple spaces between a word to a single space
612 rawString = rawString.replaceAll("\\b\\s{2,}\\b", " ");
613 // remove extra whitespaces
614 return rawString.trim();
615 }
617 /**
618 * Create the directory path for the file to be generated, construct
619 * FileOutputStream and OutputStreamWriter depending upon docencoding.
620 *
621 * @param path The directory path to be created for this file.
622 * @param filename File Name to which the PrintWriter will do the Output.
623 * @param docencoding Encoding to be used for this file.
624 * @exception IOException Exception raised by the FileWriter is passed on
625 * to next level.
626 * @exception UnsupportedEncodingException Exception raised by the
627 * OutputStreamWriter is passed on to next level.
628 * @return Writer Writer for the file getting generated.
629 * @see java.io.FileOutputStream
630 * @see java.io.OutputStreamWriter
631 */
632 public static Writer genWriter(Configuration configuration,
633 String path, String filename,
634 String docencoding)
635 throws IOException, UnsupportedEncodingException {
636 FileOutputStream fos;
637 if (path != null) {
638 DirectoryManager.createDirectory(configuration, path);
639 fos = new FileOutputStream(((path.length() > 0)?
640 path + File.separator: "") + filename);
641 } else {
642 fos = new FileOutputStream(filename);
643 }
644 if (docencoding == null) {
645 return new OutputStreamWriter(fos);
646 } else {
647 return new OutputStreamWriter(fos, docencoding);
648 }
649 }
651 /**
652 * Given an annotation, return true if it should be documented and false
653 * otherwise.
654 *
655 * @param annotationDoc the annotation to check.
656 *
657 * @return true return true if it should be documented and false otherwise.
658 */
659 public static boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) {
660 AnnotationDesc[] annotationDescList = annotationDoc.annotations();
661 for (int i = 0; i < annotationDescList.length; i++) {
662 if (annotationDescList[i].annotationType().qualifiedName().equals(
663 java.lang.annotation.Documented.class.getName())){
664 return true;
665 }
666 }
667 return false;
668 }
670 /**
671 * Given a string, return an array of tokens. The separator can be escaped
672 * with the '\' character. The '\' character may also be escaped by the
673 * '\' character.
674 *
675 * @param s the string to tokenize.
676 * @param separator the separator char.
677 * @param maxTokens the maxmimum number of tokens returned. If the
678 * max is reached, the remaining part of s is appended
679 * to the end of the last token.
680 *
681 * @return an array of tokens.
682 */
683 public static String[] tokenize(String s, char separator, int maxTokens) {
684 List<String> tokens = new ArrayList<String>();
685 StringBuilder token = new StringBuilder ();
686 boolean prevIsEscapeChar = false;
687 for (int i = 0; i < s.length(); i += Character.charCount(i)) {
688 int currentChar = s.codePointAt(i);
689 if (prevIsEscapeChar) {
690 // Case 1: escaped character
691 token.appendCodePoint(currentChar);
692 prevIsEscapeChar = false;
693 } else if (currentChar == separator && tokens.size() < maxTokens-1) {
694 // Case 2: separator
695 tokens.add(token.toString());
696 token = new StringBuilder();
697 } else if (currentChar == '\\') {
698 // Case 3: escape character
699 prevIsEscapeChar = true;
700 } else {
701 // Case 4: regular character
702 token.appendCodePoint(currentChar);
703 }
704 }
705 if (token.length() > 0) {
706 tokens.add(token.toString());
707 }
708 return tokens.toArray(new String[] {});
709 }
711 /**
712 * Return true if this class is linkable and false if we can't link to the
713 * desired class.
714 * <br>
715 * <b>NOTE:</b> You can only link to external classes if they are public or
716 * protected.
717 *
718 * @param classDoc the class to check.
719 * @param configuration the current configuration of the doclet.
720 *
721 * @return true if this class is linkable and false if we can't link to the
722 * desired class.
723 */
724 public static boolean isLinkable(ClassDoc classDoc,
725 Configuration configuration) {
726 return
727 ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) ||
728 (configuration.extern.isExternal(classDoc) &&
729 (classDoc.isPublic() || classDoc.isProtected()));
730 }
732 /**
733 * Given a class, return the closest visible super class.
734 *
735 * @param classDoc the class we are searching the parent for.
736 * @param configuration the current configuration of the doclet.
737 * @return the closest visible super class. Return null if it cannot
738 * be found (i.e. classDoc is java.lang.Object).
739 */
740 public static Type getFirstVisibleSuperClass(ClassDoc classDoc,
741 Configuration configuration) {
742 if (classDoc == null) {
743 return null;
744 }
745 Type sup = classDoc.superclassType();
746 ClassDoc supClassDoc = classDoc.superclass();
747 while (sup != null &&
748 (! (supClassDoc.isPublic() ||
749 isLinkable(supClassDoc, configuration))) ) {
750 if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName()))
751 break;
752 sup = supClassDoc.superclassType();
753 supClassDoc = supClassDoc.superclass();
754 }
755 if (classDoc.equals(supClassDoc)) {
756 return null;
757 }
758 return sup;
759 }
761 /**
762 * Given a class, return the closest visible super class.
763 *
764 * @param classDoc the class we are searching the parent for.
765 * @param configuration the current configuration of the doclet.
766 * @return the closest visible super class. Return null if it cannot
767 * be found (i.e. classDoc is java.lang.Object).
768 */
769 public static ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc,
770 Configuration configuration) {
771 if (classDoc == null) {
772 return null;
773 }
774 ClassDoc supClassDoc = classDoc.superclass();
775 while (supClassDoc != null &&
776 (! (supClassDoc.isPublic() ||
777 isLinkable(supClassDoc, configuration))) ) {
778 supClassDoc = supClassDoc.superclass();
779 }
780 if (classDoc.equals(supClassDoc)) {
781 return null;
782 }
783 return supClassDoc;
784 }
786 /**
787 * Given a ClassDoc, return the name of its type (Class, Interface, etc.).
788 *
789 * @param cd the ClassDoc to check.
790 * @param lowerCaseOnly true if you want the name returned in lower case.
791 * If false, the first letter of the name is capatilized.
792 * @return
793 */
794 public static String getTypeName(Configuration config,
795 ClassDoc cd, boolean lowerCaseOnly) {
796 String typeName = "";
797 if (cd.isOrdinaryClass()) {
798 typeName = "doclet.Class";
799 } else if (cd.isInterface()) {
800 typeName = "doclet.Interface";
801 } else if (cd.isException()) {
802 typeName = "doclet.Exception";
803 } else if (cd.isError()) {
804 typeName = "doclet.Error";
805 } else if (cd.isAnnotationType()) {
806 typeName = "doclet.AnnotationType";
807 } else if (cd.isEnum()) {
808 typeName = "doclet.Enum";
809 }
810 return config.getText(
811 lowerCaseOnly ? typeName.toLowerCase() : typeName);
812 }
814 /**
815 * Given a string, replace all tabs with the appropriate
816 * number of spaces.
817 * @param tabLength the length of each tab.
818 * @param s the String to scan.
819 */
820 public static void replaceTabs(int tabLength, StringBuilder s) {
821 if (whitespace == null || whitespace.length() < tabLength)
822 whitespace = String.format("%" + tabLength + "s", " ");
823 int index = 0;
824 while ((index = s.indexOf("\t", index)) != -1) {
825 int spaceCount = tabLength - index % tabLength;
826 s.replace(index, index+1, whitespace.substring(0, spaceCount));
827 index += spaceCount;
828 }
829 }
830 private static String whitespace;
832 /**
833 * The documentation for values() and valueOf() in Enums are set by the
834 * doclet.
835 */
836 public static void setEnumDocumentation(Configuration configuration,
837 ClassDoc classDoc) {
838 MethodDoc[] methods = classDoc.methods();
839 for (int j = 0; j < methods.length; j++) {
840 MethodDoc currentMethod = methods[j];
841 if (currentMethod.name().equals("values") &&
842 currentMethod.parameters().length == 0) {
843 currentMethod.setRawCommentText(
844 configuration.getText("doclet.enum_values_doc", classDoc.name()));
845 } else if (currentMethod.name().equals("valueOf") &&
846 currentMethod.parameters().length == 1) {
847 Type paramType = currentMethod.parameters()[0].type();
848 if (paramType != null &&
849 paramType.qualifiedTypeName().equals(String.class.getName())) {
850 currentMethod.setRawCommentText(
851 configuration.getText("doclet.enum_valueof_doc"));
852 }
853 }
854 }
855 }
857 /**
858 * Return true if the given Doc is deprecated.
859 *
860 * @param doc the Doc to check.
861 * @return true if the given Doc is deprecated.
862 */
863 public static boolean isDeprecated(ProgramElementDoc doc) {
864 if (doc.tags("deprecated").length > 0) {
865 return true;
866 }
867 AnnotationDesc[] annotationDescList = doc.annotations();
868 for (int i = 0; i < annotationDescList.length; i++) {
869 if (annotationDescList[i].annotationType().qualifiedName().equals(
870 java.lang.Deprecated.class.getName())){
871 return true;
872 }
873 }
874 return false;
875 }
876 }