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