Thu, 15 Nov 2012 19:54:20 -0800
8002079: update DocFile to use a JavaFileManager
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.*;
33 import javax.tools.StandardLocation;
35 /**
36 * Utilities Class for Doclets.
37 *
38 * <p><b>This is NOT part of any supported API.
39 * If you write code that depends on this, you do so at your own risk.
40 * This code and its internal interfaces are subject to change or
41 * deletion without notice.</b>
42 *
43 * @author Atul M Dambalkar
44 * @author Jamie Ho
45 */
46 public class Util {
48 /**
49 * Return array of class members whose documentation is to be generated.
50 * If the member is deprecated do not include such a member in the
51 * returned array.
52 *
53 * @param members Array of members to choose from.
54 * @return ProgramElementDoc[] Array of eligible members for whom
55 * documentation is getting generated.
56 */
57 public static ProgramElementDoc[] excludeDeprecatedMembers(
58 ProgramElementDoc[] members) {
59 return
60 toProgramElementDocArray(excludeDeprecatedMembersAsList(members));
61 }
63 /**
64 * Return array of class members whose documentation is to be generated.
65 * If the member is deprecated do not include such a member in the
66 * returned array.
67 *
68 * @param members Array of members to choose from.
69 * @return List List of eligible members for whom
70 * documentation is getting generated.
71 */
72 public static List<ProgramElementDoc> excludeDeprecatedMembersAsList(
73 ProgramElementDoc[] members) {
74 List<ProgramElementDoc> list = new ArrayList<ProgramElementDoc>();
75 for (int i = 0; i < members.length; i++) {
76 if (members[i].tags("deprecated").length == 0) {
77 list.add(members[i]);
78 }
79 }
80 Collections.sort(list);
81 return list;
82 }
84 /**
85 * Return the list of ProgramElementDoc objects as Array.
86 */
87 public static ProgramElementDoc[] toProgramElementDocArray(List<ProgramElementDoc> list) {
88 ProgramElementDoc[] pgmarr = new ProgramElementDoc[list.size()];
89 for (int i = 0; i < list.size(); i++) {
90 pgmarr[i] = list.get(i);
91 }
92 return pgmarr;
93 }
95 /**
96 * Return true if a non-public member found in the given array.
97 *
98 * @param members Array of members to look into.
99 * @return boolean True if non-public member found, false otherwise.
100 */
101 public static boolean nonPublicMemberFound(ProgramElementDoc[] members) {
102 for (int i = 0; i < members.length; i++) {
103 if (!members[i].isPublic()) {
104 return true;
105 }
106 }
107 return false;
108 }
110 /**
111 * Search for the given method in the given class.
112 *
113 * @param cd Class to search into.
114 * @param method Method to be searched.
115 * @return MethodDoc Method found, null otherwise.
116 */
117 public static MethodDoc findMethod(ClassDoc cd, MethodDoc method) {
118 MethodDoc[] methods = cd.methods();
119 for (int i = 0; i < methods.length; i++) {
120 if (executableMembersEqual(method, methods[i])) {
121 return methods[i];
123 }
124 }
125 return null;
126 }
128 /**
129 * @param member1 the first method to compare.
130 * @param member2 the second method to compare.
131 * @return true if member1 overrides/hides or is overriden/hidden by member2.
132 */
133 public static boolean executableMembersEqual(ExecutableMemberDoc member1,
134 ExecutableMemberDoc member2) {
135 if (! (member1 instanceof MethodDoc && member2 instanceof MethodDoc))
136 return false;
138 MethodDoc method1 = (MethodDoc) member1;
139 MethodDoc method2 = (MethodDoc) member2;
140 if (method1.isStatic() && method2.isStatic()) {
141 Parameter[] targetParams = method1.parameters();
142 Parameter[] currentParams;
143 if (method1.name().equals(method2.name()) &&
144 (currentParams = method2.parameters()).length ==
145 targetParams.length) {
146 int j;
147 for (j = 0; j < targetParams.length; j++) {
148 if (! (targetParams[j].typeName().equals(
149 currentParams[j].typeName()) ||
150 currentParams[j].type() instanceof TypeVariable ||
151 targetParams[j].type() instanceof TypeVariable)) {
152 break;
153 }
154 }
155 if (j == targetParams.length) {
156 return true;
157 }
158 }
159 return false;
160 } else {
161 return method1.overrides(method2) ||
162 method2.overrides(method1) ||
163 member1 == member2;
164 }
165 }
167 /**
168 * According to
169 * <cite>The Java™ Language Specification</cite>,
170 * all the outer classes and static inner classes are core classes.
171 */
172 public static boolean isCoreClass(ClassDoc cd) {
173 return cd.containingClass() == null || cd.isStatic();
174 }
176 public static boolean matches(ProgramElementDoc doc1,
177 ProgramElementDoc doc2) {
178 if (doc1 instanceof ExecutableMemberDoc &&
179 doc2 instanceof ExecutableMemberDoc) {
180 ExecutableMemberDoc ed1 = (ExecutableMemberDoc)doc1;
181 ExecutableMemberDoc ed2 = (ExecutableMemberDoc)doc2;
182 return executableMembersEqual(ed1, ed2);
183 } else {
184 return doc1.name().equals(doc2.name());
185 }
186 }
188 /**
189 * Copy the given directory contents from the source package directory
190 * to the generated documentation directory. For example for a package
191 * java.lang this method find out the source location of the package using
192 * {@link SourcePath} and if given directory is found in the source
193 * directory structure, copy the entire directory, to the generated
194 * documentation hierarchy.
195 *
196 * @param configuration The configuration of the current doclet.
197 * @param path The relative path to the directory to be copied.
198 * @param dir The original directory name to copy from.
199 * @param overwrite Overwrite files if true.
200 */
201 public static void copyDocFiles(Configuration configuration, PackageDoc pd) {
202 copyDocFiles(configuration, DocPath.forPackage(pd).resolve(DocPaths.DOC_FILES));
203 }
205 public static void copyDocFiles(Configuration configuration, DocPath dir) {
206 try {
207 boolean first = true;
208 for (DocFile f : DocFile.list(configuration, StandardLocation.SOURCE_PATH, dir)) {
209 if (!f.isDirectory()) {
210 continue;
211 }
212 DocFile srcdir = f;
213 DocFile destdir = DocFile.createFileForOutput(configuration, dir);
214 if (srcdir.isSameFile(destdir)) {
215 continue;
216 }
218 for (DocFile srcfile: srcdir.list()) {
219 DocFile destfile = destdir.resolve(srcfile.getName());
220 if (srcfile.isFile()) {
221 if (destfile.exists() && !first) {
222 configuration.message.warning((SourcePosition) null,
223 "doclet.Copy_Overwrite_warning",
224 srcfile.getPath(), destdir.getPath());
225 } else {
226 configuration.message.notice(
227 "doclet.Copying_File_0_To_Dir_1",
228 srcfile.getPath(), destdir.getPath());
229 destfile.copyFile(srcfile);
230 }
231 } else if (srcfile.isDirectory()) {
232 if (configuration.copydocfilesubdirs
233 && !configuration.shouldExcludeDocFileDir(srcfile.getName())) {
234 copyDocFiles(configuration, dir.resolve(srcfile.getName()));
235 }
236 }
237 }
239 first = false;
240 }
241 } catch (SecurityException exc) {
242 throw new DocletAbortException();
243 } catch (IOException exc) {
244 throw new DocletAbortException();
245 }
246 }
248 /**
249 * We want the list of types in alphabetical order. However, types are not
250 * comparable. We need a comparator for now.
251 */
252 private static class TypeComparator implements Comparator<Type> {
253 public int compare(Type type1, Type type2) {
254 return type1.qualifiedTypeName().toLowerCase().compareTo(
255 type2.qualifiedTypeName().toLowerCase());
256 }
257 }
259 /**
260 * For the class return all implemented interfaces including the
261 * superinterfaces of the implementing interfaces, also iterate over for
262 * all the superclasses. For interface return all the extended interfaces
263 * as well as superinterfaces for those extended interfaces.
264 *
265 * @param type type whose implemented or
266 * super interfaces are sought.
267 * @param configuration the current configuration of the doclet.
268 * @param sort if true, return list of interfaces sorted alphabetically.
269 * @return List of all the required interfaces.
270 */
271 public static List<Type> getAllInterfaces(Type type,
272 Configuration configuration, boolean sort) {
273 Map<ClassDoc,Type> results = sort ? new TreeMap<ClassDoc,Type>() : new LinkedHashMap<ClassDoc,Type>();
274 Type[] interfaceTypes = null;
275 Type superType = null;
276 if (type instanceof ParameterizedType) {
277 interfaceTypes = ((ParameterizedType) type).interfaceTypes();
278 superType = ((ParameterizedType) type).superclassType();
279 } else if (type instanceof ClassDoc) {
280 interfaceTypes = ((ClassDoc) type).interfaceTypes();
281 superType = ((ClassDoc) type).superclassType();
282 } else {
283 interfaceTypes = type.asClassDoc().interfaceTypes();
284 superType = type.asClassDoc().superclassType();
285 }
287 for (int i = 0; i < interfaceTypes.length; i++) {
288 Type interfaceType = interfaceTypes[i];
289 ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
290 if (! (interfaceClassDoc.isPublic() ||
291 (configuration == null ||
292 isLinkable(interfaceClassDoc, configuration)))) {
293 continue;
294 }
295 results.put(interfaceClassDoc, interfaceType);
296 List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration, sort);
297 for (Iterator<Type> iter = superInterfaces.iterator(); iter.hasNext(); ) {
298 Type t = iter.next();
299 results.put(t.asClassDoc(), t);
300 }
301 }
302 if (superType == null)
303 return new ArrayList<Type>(results.values());
304 //Try walking the tree.
305 addAllInterfaceTypes(results,
306 superType,
307 superType instanceof ClassDoc ?
308 ((ClassDoc) superType).interfaceTypes() :
309 ((ParameterizedType) superType).interfaceTypes(),
310 false, configuration);
311 List<Type> resultsList = new ArrayList<Type>(results.values());
312 if (sort) {
313 Collections.sort(resultsList, new TypeComparator());
314 }
315 return resultsList;
316 }
318 public static List<Type> getAllInterfaces(Type type, Configuration configuration) {
319 return getAllInterfaces(type, configuration, true);
320 }
322 private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ClassDoc c, boolean raw,
323 Configuration configuration) {
324 Type superType = c.superclassType();
325 if (superType == null)
326 return;
327 addAllInterfaceTypes(results, superType,
328 superType instanceof ClassDoc ?
329 ((ClassDoc) superType).interfaceTypes() :
330 ((ParameterizedType) superType).interfaceTypes(),
331 raw, configuration);
332 }
334 private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ParameterizedType p,
335 Configuration configuration) {
336 Type superType = p.superclassType();
337 if (superType == null)
338 return;
339 addAllInterfaceTypes(results, superType,
340 superType instanceof ClassDoc ?
341 ((ClassDoc) superType).interfaceTypes() :
342 ((ParameterizedType) superType).interfaceTypes(),
343 false, configuration);
344 }
346 private static void addAllInterfaceTypes(Map<ClassDoc,Type> results, Type type,
347 Type[] interfaceTypes, boolean raw,
348 Configuration configuration) {
349 for (int i = 0; i < interfaceTypes.length; i++) {
350 Type interfaceType = interfaceTypes[i];
351 ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
352 if (! (interfaceClassDoc.isPublic() ||
353 (configuration != null &&
354 isLinkable(interfaceClassDoc, configuration)))) {
355 continue;
356 }
357 if (raw)
358 interfaceType = interfaceType.asClassDoc();
359 results.put(interfaceClassDoc, interfaceType);
360 List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration);
361 for (Iterator<Type> iter = superInterfaces.iterator(); iter.hasNext(); ) {
362 Type superInterface = iter.next();
363 results.put(superInterface.asClassDoc(), superInterface);
364 }
365 }
366 if (type instanceof ParameterizedType)
367 findAllInterfaceTypes(results, (ParameterizedType) type, configuration);
368 else if (((ClassDoc) type).typeParameters().length == 0)
369 findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration);
370 else
371 findAllInterfaceTypes(results, (ClassDoc) type, true, configuration);
372 }
374 /**
375 * Enclose in quotes, used for paths and filenames that contains spaces
376 */
377 public static String quote(String filepath) {
378 return ("\"" + filepath + "\"");
379 }
381 /**
382 * Given a package, return its name.
383 * @param packageDoc the package to check.
384 * @return the name of the given package.
385 */
386 public static String getPackageName(PackageDoc packageDoc) {
387 return packageDoc == null || packageDoc.name().length() == 0 ?
388 DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name();
389 }
391 /**
392 * Given a package, return its file name without the extension.
393 * @param packageDoc the package to check.
394 * @return the file name of the given package.
395 */
396 public static String getPackageFileHeadName(PackageDoc packageDoc) {
397 return packageDoc == null || packageDoc.name().length() == 0 ?
398 DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name();
399 }
401 /**
402 * Given a string, replace all occurrences of 'newStr' with 'oldStr'.
403 * @param originalStr the string to modify.
404 * @param oldStr the string to replace.
405 * @param newStr the string to insert in place of the old string.
406 */
407 public static String replaceText(String originalStr, String oldStr,
408 String newStr) {
409 if (oldStr == null || newStr == null || oldStr.equals(newStr)) {
410 return originalStr;
411 }
412 return originalStr.replace(oldStr, newStr);
413 }
415 /**
416 * Given a string, escape all special html characters and
417 * return the result.
418 *
419 * @param s The string to check.
420 * @return the original string with all of the HTML characters escaped.
421 */
422 public static String escapeHtmlChars(String s) {
423 for (int i = 0; i < s.length(); i++) {
424 char ch = s.charAt(i);
425 switch (ch) {
426 // only start building a new string if we need to
427 case '<': case '>': case '&':
428 StringBuilder sb = new StringBuilder(s.substring(0, i));
429 for ( ; i < s.length(); i++) {
430 ch = s.charAt(i);
431 switch (ch) {
432 case '<': sb.append("<"); break;
433 case '>': sb.append(">"); break;
434 case '&': sb.append("&"); break;
435 default: sb.append(ch); break;
436 }
437 }
438 return sb.toString();
439 }
440 }
441 return s;
442 }
444 /**
445 * Escape all special html characters in a string buffer.
446 *
447 * @param sb The string buffer to update
448 */
449 public static void escapeHtmlChars(StringBuilder sb) {
450 // scan backwards, replacing characters as needed.
451 for (int i = sb.length() - 1; i >= 0; i--) {
452 switch (sb.charAt(i)) {
453 case '<': sb.replace(i, i+1, "<"); break;
454 case '>': sb.replace(i, i+1, ">"); break;
455 case '&': sb.replace(i, i+1, "&"); break;
456 }
457 }
458 }
460 /**
461 * Given a string, strips all html characters and
462 * return the result.
463 *
464 * @param rawString The string to check.
465 * @return the original string with all of the HTML characters
466 * stripped.
467 *
468 */
469 public static String stripHtml(String rawString) {
470 // remove HTML tags
471 rawString = rawString.replaceAll("\\<.*?>", " ");
472 // consolidate multiple spaces between a word to a single space
473 rawString = rawString.replaceAll("\\b\\s{2,}\\b", " ");
474 // remove extra whitespaces
475 return rawString.trim();
476 }
478 /**
479 * Given an annotation, return true if it should be documented and false
480 * otherwise.
481 *
482 * @param annotationDoc the annotation to check.
483 *
484 * @return true return true if it should be documented and false otherwise.
485 */
486 public static boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) {
487 AnnotationDesc[] annotationDescList = annotationDoc.annotations();
488 for (int i = 0; i < annotationDescList.length; i++) {
489 if (annotationDescList[i].annotationType().qualifiedName().equals(
490 java.lang.annotation.Documented.class.getName())){
491 return true;
492 }
493 }
494 return false;
495 }
497 /**
498 * Return true if this class is linkable and false if we can't link to the
499 * desired class.
500 * <br>
501 * <b>NOTE:</b> You can only link to external classes if they are public or
502 * protected.
503 *
504 * @param classDoc the class to check.
505 * @param configuration the current configuration of the doclet.
506 *
507 * @return true if this class is linkable and false if we can't link to the
508 * desired class.
509 */
510 public static boolean isLinkable(ClassDoc classDoc,
511 Configuration configuration) {
512 return
513 ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) ||
514 (configuration.extern.isExternal(classDoc) &&
515 (classDoc.isPublic() || classDoc.isProtected()));
516 }
518 /**
519 * Given a class, return the closest visible super class.
520 *
521 * @param classDoc the class we are searching the parent for.
522 * @param configuration the current configuration of the doclet.
523 * @return the closest visible super class. Return null if it cannot
524 * be found (i.e. classDoc is java.lang.Object).
525 */
526 public static Type getFirstVisibleSuperClass(ClassDoc classDoc,
527 Configuration configuration) {
528 if (classDoc == null) {
529 return null;
530 }
531 Type sup = classDoc.superclassType();
532 ClassDoc supClassDoc = classDoc.superclass();
533 while (sup != null &&
534 (! (supClassDoc.isPublic() ||
535 isLinkable(supClassDoc, configuration))) ) {
536 if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName()))
537 break;
538 sup = supClassDoc.superclassType();
539 supClassDoc = supClassDoc.superclass();
540 }
541 if (classDoc.equals(supClassDoc)) {
542 return null;
543 }
544 return sup;
545 }
547 /**
548 * Given a class, return the closest visible super class.
549 *
550 * @param classDoc the class we are searching the parent for.
551 * @param configuration the current configuration of the doclet.
552 * @return the closest visible super class. Return null if it cannot
553 * be found (i.e. classDoc is java.lang.Object).
554 */
555 public static ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc,
556 Configuration configuration) {
557 if (classDoc == null) {
558 return null;
559 }
560 ClassDoc supClassDoc = classDoc.superclass();
561 while (supClassDoc != null &&
562 (! (supClassDoc.isPublic() ||
563 isLinkable(supClassDoc, configuration))) ) {
564 supClassDoc = supClassDoc.superclass();
565 }
566 if (classDoc.equals(supClassDoc)) {
567 return null;
568 }
569 return supClassDoc;
570 }
572 /**
573 * Given a ClassDoc, return the name of its type (Class, Interface, etc.).
574 *
575 * @param cd the ClassDoc to check.
576 * @param lowerCaseOnly true if you want the name returned in lower case.
577 * If false, the first letter of the name is capitalized.
578 * @return
579 */
580 public static String getTypeName(Configuration config,
581 ClassDoc cd, boolean lowerCaseOnly) {
582 String typeName = "";
583 if (cd.isOrdinaryClass()) {
584 typeName = "doclet.Class";
585 } else if (cd.isInterface()) {
586 typeName = "doclet.Interface";
587 } else if (cd.isException()) {
588 typeName = "doclet.Exception";
589 } else if (cd.isError()) {
590 typeName = "doclet.Error";
591 } else if (cd.isAnnotationType()) {
592 typeName = "doclet.AnnotationType";
593 } else if (cd.isEnum()) {
594 typeName = "doclet.Enum";
595 }
596 return config.getText(
597 lowerCaseOnly ? typeName.toLowerCase() : typeName);
598 }
600 /**
601 * Replace all tabs with the appropriate number of spaces.
602 * @param configuration the doclet configuration defining the setting for the
603 * tab length.
604 * @param sb the StringBuilder in which to replace the tabs
605 */
606 public static void replaceTabs(Configuration configuration, StringBuilder sb) {
607 int tabLength = configuration.sourcetab;
608 String whitespace = configuration.tabSpaces;
609 int index = 0;
610 while ((index = sb.indexOf("\t", index)) != -1) {
611 int spaceCount = tabLength - index % tabLength;
612 sb.replace(index, index+1, whitespace.substring(0, spaceCount));
613 index += spaceCount;
614 }
615 }
617 /**
618 * The documentation for values() and valueOf() in Enums are set by the
619 * doclet.
620 */
621 public static void setEnumDocumentation(Configuration configuration,
622 ClassDoc classDoc) {
623 MethodDoc[] methods = classDoc.methods();
624 for (int j = 0; j < methods.length; j++) {
625 MethodDoc currentMethod = methods[j];
626 if (currentMethod.name().equals("values") &&
627 currentMethod.parameters().length == 0) {
628 currentMethod.setRawCommentText(
629 configuration.getText("doclet.enum_values_doc", classDoc.name()));
630 } else if (currentMethod.name().equals("valueOf") &&
631 currentMethod.parameters().length == 1) {
632 Type paramType = currentMethod.parameters()[0].type();
633 if (paramType != null &&
634 paramType.qualifiedTypeName().equals(String.class.getName())) {
635 currentMethod.setRawCommentText(
636 configuration.getText("doclet.enum_valueof_doc"));
637 }
638 }
639 }
640 }
642 /**
643 * Return true if the given Doc is deprecated.
644 *
645 * @param doc the Doc to check.
646 * @return true if the given Doc is deprecated.
647 */
648 public static boolean isDeprecated(Doc doc) {
649 if (doc.tags("deprecated").length > 0) {
650 return true;
651 }
652 AnnotationDesc[] annotationDescList;
653 if (doc instanceof PackageDoc)
654 annotationDescList = ((PackageDoc)doc).annotations();
655 else
656 annotationDescList = ((ProgramElementDoc)doc).annotations();
657 for (int i = 0; i < annotationDescList.length; i++) {
658 if (annotationDescList[i].annotationType().qualifiedName().equals(
659 java.lang.Deprecated.class.getName())){
660 return true;
661 }
662 }
663 return false;
664 }
665 }