|
1 /* |
|
2 * Copyright (c) 1999, 2013, 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 */ |
|
25 |
|
26 package com.sun.tools.doclets.internal.toolkit.util; |
|
27 |
|
28 import java.io.*; |
|
29 import java.lang.annotation.ElementType; |
|
30 import java.util.*; |
|
31 import javax.tools.StandardLocation; |
|
32 |
|
33 import com.sun.javadoc.*; |
|
34 import com.sun.javadoc.AnnotationDesc.ElementValuePair; |
|
35 import com.sun.tools.doclets.internal.toolkit.*; |
|
36 import com.sun.tools.javac.util.StringUtils; |
|
37 |
|
38 /** |
|
39 * Utilities Class for Doclets. |
|
40 * |
|
41 * <p><b>This is NOT part of any supported API. |
|
42 * If you write code that depends on this, you do so at your own risk. |
|
43 * This code and its internal interfaces are subject to change or |
|
44 * deletion without notice.</b> |
|
45 * |
|
46 * @author Atul M Dambalkar |
|
47 * @author Jamie Ho |
|
48 */ |
|
49 public class Util { |
|
50 |
|
51 /** |
|
52 * Return array of class members whose documentation is to be generated. |
|
53 * If the member is deprecated do not include such a member in the |
|
54 * returned array. |
|
55 * |
|
56 * @param members Array of members to choose from. |
|
57 * @return ProgramElementDoc[] Array of eligible members for whom |
|
58 * documentation is getting generated. |
|
59 */ |
|
60 public static ProgramElementDoc[] excludeDeprecatedMembers( |
|
61 ProgramElementDoc[] members) { |
|
62 return |
|
63 toProgramElementDocArray(excludeDeprecatedMembersAsList(members)); |
|
64 } |
|
65 |
|
66 /** |
|
67 * Return array of class members whose documentation is to be generated. |
|
68 * If the member is deprecated do not include such a member in the |
|
69 * returned array. |
|
70 * |
|
71 * @param members Array of members to choose from. |
|
72 * @return List List of eligible members for whom |
|
73 * documentation is getting generated. |
|
74 */ |
|
75 public static List<ProgramElementDoc> excludeDeprecatedMembersAsList( |
|
76 ProgramElementDoc[] members) { |
|
77 List<ProgramElementDoc> list = new ArrayList<ProgramElementDoc>(); |
|
78 for (int i = 0; i < members.length; i++) { |
|
79 if (members[i].tags("deprecated").length == 0) { |
|
80 list.add(members[i]); |
|
81 } |
|
82 } |
|
83 Collections.sort(list); |
|
84 return list; |
|
85 } |
|
86 |
|
87 /** |
|
88 * Return the list of ProgramElementDoc objects as Array. |
|
89 */ |
|
90 public static ProgramElementDoc[] toProgramElementDocArray(List<ProgramElementDoc> list) { |
|
91 ProgramElementDoc[] pgmarr = new ProgramElementDoc[list.size()]; |
|
92 for (int i = 0; i < list.size(); i++) { |
|
93 pgmarr[i] = list.get(i); |
|
94 } |
|
95 return pgmarr; |
|
96 } |
|
97 |
|
98 /** |
|
99 * 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(ProgramElementDoc[] members) { |
|
105 for (int i = 0; i < members.length; i++) { |
|
106 if (!members[i].isPublic()) { |
|
107 return true; |
|
108 } |
|
109 } |
|
110 return false; |
|
111 } |
|
112 |
|
113 /** |
|
114 * Search for the given method in the given class. |
|
115 * |
|
116 * @param cd Class to search into. |
|
117 * @param method Method to be searched. |
|
118 * @return MethodDoc Method found, null otherwise. |
|
119 */ |
|
120 public static MethodDoc findMethod(ClassDoc cd, MethodDoc method) { |
|
121 MethodDoc[] methods = cd.methods(); |
|
122 for (int i = 0; i < methods.length; i++) { |
|
123 if (executableMembersEqual(method, methods[i])) { |
|
124 return methods[i]; |
|
125 |
|
126 } |
|
127 } |
|
128 return null; |
|
129 } |
|
130 |
|
131 /** |
|
132 * @param member1 the first method to compare. |
|
133 * @param member2 the second method to compare. |
|
134 * @return true if member1 overrides/hides or is overriden/hidden by member2. |
|
135 */ |
|
136 public static boolean executableMembersEqual(ExecutableMemberDoc member1, |
|
137 ExecutableMemberDoc member2) { |
|
138 if (! (member1 instanceof MethodDoc && member2 instanceof MethodDoc)) |
|
139 return false; |
|
140 |
|
141 MethodDoc method1 = (MethodDoc) member1; |
|
142 MethodDoc method2 = (MethodDoc) member2; |
|
143 if (method1.isStatic() && method2.isStatic()) { |
|
144 Parameter[] targetParams = method1.parameters(); |
|
145 Parameter[] currentParams; |
|
146 if (method1.name().equals(method2.name()) && |
|
147 (currentParams = method2.parameters()).length == |
|
148 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 || |
|
154 targetParams[j].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) || |
|
166 member1 == member2; |
|
167 } |
|
168 } |
|
169 |
|
170 /** |
|
171 * According to |
|
172 * <cite>The Java™ Language Specification</cite>, |
|
173 * all the outer classes and static inner classes are core classes. |
|
174 */ |
|
175 public static boolean isCoreClass(ClassDoc cd) { |
|
176 return cd.containingClass() == null || cd.isStatic(); |
|
177 } |
|
178 |
|
179 public static boolean matches(ProgramElementDoc doc1, |
|
180 ProgramElementDoc doc2) { |
|
181 if (doc1 instanceof ExecutableMemberDoc && |
|
182 doc2 instanceof ExecutableMemberDoc) { |
|
183 ExecutableMemberDoc ed1 = (ExecutableMemberDoc)doc1; |
|
184 ExecutableMemberDoc ed2 = (ExecutableMemberDoc)doc2; |
|
185 return executableMembersEqual(ed1, ed2); |
|
186 } else { |
|
187 return doc1.name().equals(doc2.name()); |
|
188 } |
|
189 } |
|
190 |
|
191 /** |
|
192 * Copy the given directory contents from the source package directory |
|
193 * to the generated documentation directory. For example for a package |
|
194 * java.lang this method find out the source location of the package using |
|
195 * {@link SourcePath} and if given directory is found in the source |
|
196 * directory structure, copy the entire directory, to the generated |
|
197 * documentation hierarchy. |
|
198 * |
|
199 * @param configuration The configuration of the current doclet. |
|
200 * @param path The relative path to the directory to be copied. |
|
201 * @param dir The original directory name to copy from. |
|
202 * @param overwrite Overwrite files if true. |
|
203 */ |
|
204 public static void copyDocFiles(Configuration configuration, PackageDoc pd) { |
|
205 copyDocFiles(configuration, DocPath.forPackage(pd).resolve(DocPaths.DOC_FILES)); |
|
206 } |
|
207 |
|
208 public static void copyDocFiles(Configuration configuration, DocPath dir) { |
|
209 try { |
|
210 boolean first = true; |
|
211 for (DocFile f : DocFile.list(configuration, StandardLocation.SOURCE_PATH, dir)) { |
|
212 if (!f.isDirectory()) { |
|
213 continue; |
|
214 } |
|
215 DocFile srcdir = f; |
|
216 DocFile destdir = DocFile.createFileForOutput(configuration, dir); |
|
217 if (srcdir.isSameFile(destdir)) { |
|
218 continue; |
|
219 } |
|
220 |
|
221 for (DocFile srcfile: srcdir.list()) { |
|
222 DocFile destfile = destdir.resolve(srcfile.getName()); |
|
223 if (srcfile.isFile()) { |
|
224 if (destfile.exists() && !first) { |
|
225 configuration.message.warning((SourcePosition) null, |
|
226 "doclet.Copy_Overwrite_warning", |
|
227 srcfile.getPath(), destdir.getPath()); |
|
228 } else { |
|
229 configuration.message.notice( |
|
230 "doclet.Copying_File_0_To_Dir_1", |
|
231 srcfile.getPath(), destdir.getPath()); |
|
232 destfile.copyFile(srcfile); |
|
233 } |
|
234 } else if (srcfile.isDirectory()) { |
|
235 if (configuration.copydocfilesubdirs |
|
236 && !configuration.shouldExcludeDocFileDir(srcfile.getName())) { |
|
237 copyDocFiles(configuration, dir.resolve(srcfile.getName())); |
|
238 } |
|
239 } |
|
240 } |
|
241 |
|
242 first = false; |
|
243 } |
|
244 } catch (SecurityException exc) { |
|
245 throw new DocletAbortException(exc); |
|
246 } catch (IOException exc) { |
|
247 throw new DocletAbortException(exc); |
|
248 } |
|
249 } |
|
250 |
|
251 /** |
|
252 * We want the list of types in alphabetical order. However, types are not |
|
253 * comparable. We need a comparator for now. |
|
254 */ |
|
255 private static class TypeComparator implements Comparator<Type> { |
|
256 public int compare(Type type1, Type type2) { |
|
257 return type1.qualifiedTypeName().compareToIgnoreCase( |
|
258 type2.qualifiedTypeName()); |
|
259 } |
|
260 } |
|
261 |
|
262 /** |
|
263 * For the class return all implemented interfaces including the |
|
264 * superinterfaces of the implementing interfaces, also iterate over for |
|
265 * all the superclasses. For interface return all the extended interfaces |
|
266 * as well as superinterfaces for those extended interfaces. |
|
267 * |
|
268 * @param type type whose implemented or |
|
269 * super interfaces are sought. |
|
270 * @param configuration the current configuration of the doclet. |
|
271 * @param sort if true, return list of interfaces sorted alphabetically. |
|
272 * @return List of all the required interfaces. |
|
273 */ |
|
274 public static List<Type> getAllInterfaces(Type type, |
|
275 Configuration configuration, boolean sort) { |
|
276 Map<ClassDoc,Type> results = sort ? new TreeMap<ClassDoc,Type>() : new LinkedHashMap<ClassDoc,Type>(); |
|
277 Type[] interfaceTypes = null; |
|
278 Type superType = null; |
|
279 if (type instanceof ParameterizedType) { |
|
280 interfaceTypes = ((ParameterizedType) type).interfaceTypes(); |
|
281 superType = ((ParameterizedType) type).superclassType(); |
|
282 } else if (type instanceof ClassDoc) { |
|
283 interfaceTypes = ((ClassDoc) type).interfaceTypes(); |
|
284 superType = ((ClassDoc) type).superclassType(); |
|
285 } else { |
|
286 interfaceTypes = type.asClassDoc().interfaceTypes(); |
|
287 superType = type.asClassDoc().superclassType(); |
|
288 } |
|
289 |
|
290 for (int i = 0; i < interfaceTypes.length; i++) { |
|
291 Type interfaceType = interfaceTypes[i]; |
|
292 ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); |
|
293 if (! (interfaceClassDoc.isPublic() || |
|
294 (configuration == null || |
|
295 isLinkable(interfaceClassDoc, configuration)))) { |
|
296 continue; |
|
297 } |
|
298 results.put(interfaceClassDoc, interfaceType); |
|
299 List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration, sort); |
|
300 for (Iterator<Type> iter = superInterfaces.iterator(); iter.hasNext(); ) { |
|
301 Type t = iter.next(); |
|
302 results.put(t.asClassDoc(), t); |
|
303 } |
|
304 } |
|
305 if (superType == null) |
|
306 return new ArrayList<Type>(results.values()); |
|
307 //Try walking the tree. |
|
308 addAllInterfaceTypes(results, |
|
309 superType, |
|
310 interfaceTypesOf(superType), |
|
311 false, configuration); |
|
312 List<Type> resultsList = new ArrayList<Type>(results.values()); |
|
313 if (sort) { |
|
314 Collections.sort(resultsList, new TypeComparator()); |
|
315 } |
|
316 return resultsList; |
|
317 } |
|
318 |
|
319 private static Type[] interfaceTypesOf(Type type) { |
|
320 if (type instanceof AnnotatedType) |
|
321 type = ((AnnotatedType)type).underlyingType(); |
|
322 return type instanceof ClassDoc ? |
|
323 ((ClassDoc)type).interfaceTypes() : |
|
324 ((ParameterizedType)type).interfaceTypes(); |
|
325 } |
|
326 |
|
327 public static List<Type> getAllInterfaces(Type type, Configuration configuration) { |
|
328 return getAllInterfaces(type, configuration, true); |
|
329 } |
|
330 |
|
331 private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ClassDoc c, boolean raw, |
|
332 Configuration configuration) { |
|
333 Type superType = c.superclassType(); |
|
334 if (superType == null) |
|
335 return; |
|
336 addAllInterfaceTypes(results, superType, |
|
337 interfaceTypesOf(superType), |
|
338 raw, configuration); |
|
339 } |
|
340 |
|
341 private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ParameterizedType p, |
|
342 Configuration configuration) { |
|
343 Type superType = p.superclassType(); |
|
344 if (superType == null) |
|
345 return; |
|
346 addAllInterfaceTypes(results, superType, |
|
347 interfaceTypesOf(superType), |
|
348 false, configuration); |
|
349 } |
|
350 |
|
351 private static void addAllInterfaceTypes(Map<ClassDoc,Type> results, Type type, |
|
352 Type[] interfaceTypes, boolean raw, |
|
353 Configuration configuration) { |
|
354 for (int i = 0; i < interfaceTypes.length; i++) { |
|
355 Type interfaceType = interfaceTypes[i]; |
|
356 ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); |
|
357 if (! (interfaceClassDoc.isPublic() || |
|
358 (configuration != null && |
|
359 isLinkable(interfaceClassDoc, configuration)))) { |
|
360 continue; |
|
361 } |
|
362 if (raw) |
|
363 interfaceType = interfaceType.asClassDoc(); |
|
364 results.put(interfaceClassDoc, interfaceType); |
|
365 List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration); |
|
366 for (Iterator<Type> iter = superInterfaces.iterator(); iter.hasNext(); ) { |
|
367 Type superInterface = iter.next(); |
|
368 results.put(superInterface.asClassDoc(), superInterface); |
|
369 } |
|
370 } |
|
371 if (type instanceof AnnotatedType) |
|
372 type = ((AnnotatedType)type).underlyingType(); |
|
373 |
|
374 if (type instanceof ParameterizedType) |
|
375 findAllInterfaceTypes(results, (ParameterizedType) type, configuration); |
|
376 else if (((ClassDoc) type).typeParameters().length == 0) |
|
377 findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration); |
|
378 else |
|
379 findAllInterfaceTypes(results, (ClassDoc) type, true, configuration); |
|
380 } |
|
381 |
|
382 /** |
|
383 * Enclose in quotes, used for paths and filenames that contains spaces |
|
384 */ |
|
385 public static String quote(String filepath) { |
|
386 return ("\"" + filepath + "\""); |
|
387 } |
|
388 |
|
389 /** |
|
390 * Given a package, return its name. |
|
391 * @param packageDoc the package to check. |
|
392 * @return the name of the given package. |
|
393 */ |
|
394 public static String getPackageName(PackageDoc packageDoc) { |
|
395 return packageDoc == null || packageDoc.name().length() == 0 ? |
|
396 DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name(); |
|
397 } |
|
398 |
|
399 /** |
|
400 * Given a package, return its file name without the extension. |
|
401 * @param packageDoc the package to check. |
|
402 * @return the file name of the given package. |
|
403 */ |
|
404 public static String getPackageFileHeadName(PackageDoc packageDoc) { |
|
405 return packageDoc == null || packageDoc.name().length() == 0 ? |
|
406 DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name(); |
|
407 } |
|
408 |
|
409 /** |
|
410 * Given a string, replace all occurrences of 'newStr' with 'oldStr'. |
|
411 * @param originalStr the string to modify. |
|
412 * @param oldStr the string to replace. |
|
413 * @param newStr the string to insert in place of the old string. |
|
414 */ |
|
415 public static String replaceText(String originalStr, String oldStr, |
|
416 String newStr) { |
|
417 if (oldStr == null || newStr == null || oldStr.equals(newStr)) { |
|
418 return originalStr; |
|
419 } |
|
420 return originalStr.replace(oldStr, newStr); |
|
421 } |
|
422 |
|
423 /** |
|
424 * Given an annotation, return true if it should be documented and false |
|
425 * otherwise. |
|
426 * |
|
427 * @param annotationDoc the annotation to check. |
|
428 * |
|
429 * @return true return true if it should be documented and false otherwise. |
|
430 */ |
|
431 public static boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) { |
|
432 AnnotationDesc[] annotationDescList = annotationDoc.annotations(); |
|
433 for (int i = 0; i < annotationDescList.length; i++) { |
|
434 if (annotationDescList[i].annotationType().qualifiedName().equals( |
|
435 java.lang.annotation.Documented.class.getName())){ |
|
436 return true; |
|
437 } |
|
438 } |
|
439 return false; |
|
440 } |
|
441 |
|
442 private static boolean isDeclarationTarget(AnnotationDesc targetAnno) { |
|
443 // The error recovery steps here are analogous to TypeAnnotations |
|
444 ElementValuePair[] elems = targetAnno.elementValues(); |
|
445 if (elems == null |
|
446 || elems.length != 1 |
|
447 || !"value".equals(elems[0].element().name()) |
|
448 || !(elems[0].value().value() instanceof AnnotationValue[])) |
|
449 return true; // error recovery |
|
450 |
|
451 AnnotationValue[] values = (AnnotationValue[])elems[0].value().value(); |
|
452 for (int i = 0; i < values.length; i++) { |
|
453 Object value = values[i].value(); |
|
454 if (!(value instanceof FieldDoc)) |
|
455 return true; // error recovery |
|
456 |
|
457 FieldDoc eValue = (FieldDoc)value; |
|
458 if (Util.isJava5DeclarationElementType(eValue)) { |
|
459 return true; |
|
460 } |
|
461 } |
|
462 |
|
463 return false; |
|
464 } |
|
465 |
|
466 /** |
|
467 * Returns true if the {@code annotationDoc} is to be treated |
|
468 * as a declaration annotation, when targeting the |
|
469 * {@code elemType} element type. |
|
470 * |
|
471 * @param annotationDoc the annotationDoc to check |
|
472 * @param elemType the targeted elemType |
|
473 * @return true if annotationDoc is a declaration annotation |
|
474 */ |
|
475 public static boolean isDeclarationAnnotation(AnnotationTypeDoc annotationDoc, |
|
476 boolean isJava5DeclarationLocation) { |
|
477 if (!isJava5DeclarationLocation) |
|
478 return false; |
|
479 AnnotationDesc[] annotationDescList = annotationDoc.annotations(); |
|
480 // Annotations with no target are treated as declaration as well |
|
481 if (annotationDescList.length==0) |
|
482 return true; |
|
483 for (int i = 0; i < annotationDescList.length; i++) { |
|
484 if (annotationDescList[i].annotationType().qualifiedName().equals( |
|
485 java.lang.annotation.Target.class.getName())) { |
|
486 if (isDeclarationTarget(annotationDescList[i])) |
|
487 return true; |
|
488 } |
|
489 } |
|
490 return false; |
|
491 } |
|
492 |
|
493 /** |
|
494 * Return true if this class is linkable and false if we can't link to the |
|
495 * desired class. |
|
496 * <br> |
|
497 * <b>NOTE:</b> You can only link to external classes if they are public or |
|
498 * protected. |
|
499 * |
|
500 * @param classDoc the class to check. |
|
501 * @param configuration the current configuration of the doclet. |
|
502 * |
|
503 * @return true if this class is linkable and false if we can't link to the |
|
504 * desired class. |
|
505 */ |
|
506 public static boolean isLinkable(ClassDoc classDoc, |
|
507 Configuration configuration) { |
|
508 return |
|
509 ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) || |
|
510 (configuration.extern.isExternal(classDoc) && |
|
511 (classDoc.isPublic() || classDoc.isProtected())); |
|
512 } |
|
513 |
|
514 /** |
|
515 * Given a class, return the closest visible super class. |
|
516 * |
|
517 * @param classDoc the class we are searching the parent for. |
|
518 * @param configuration the current configuration of the doclet. |
|
519 * @return the closest visible super class. Return null if it cannot |
|
520 * be found (i.e. classDoc is java.lang.Object). |
|
521 */ |
|
522 public static Type getFirstVisibleSuperClass(ClassDoc classDoc, |
|
523 Configuration configuration) { |
|
524 if (classDoc == null) { |
|
525 return null; |
|
526 } |
|
527 Type sup = classDoc.superclassType(); |
|
528 ClassDoc supClassDoc = classDoc.superclass(); |
|
529 while (sup != null && |
|
530 (! (supClassDoc.isPublic() || |
|
531 isLinkable(supClassDoc, configuration))) ) { |
|
532 if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName())) |
|
533 break; |
|
534 sup = supClassDoc.superclassType(); |
|
535 supClassDoc = supClassDoc.superclass(); |
|
536 } |
|
537 if (classDoc.equals(supClassDoc)) { |
|
538 return null; |
|
539 } |
|
540 return sup; |
|
541 } |
|
542 |
|
543 /** |
|
544 * Given a class, return the closest visible super class. |
|
545 * |
|
546 * @param classDoc the class we are searching the parent for. |
|
547 * @param configuration the current configuration of the doclet. |
|
548 * @return the closest visible super class. Return null if it cannot |
|
549 * be found (i.e. classDoc is java.lang.Object). |
|
550 */ |
|
551 public static ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc, |
|
552 Configuration configuration) { |
|
553 if (classDoc == null) { |
|
554 return null; |
|
555 } |
|
556 ClassDoc supClassDoc = classDoc.superclass(); |
|
557 while (supClassDoc != null && |
|
558 (! (supClassDoc.isPublic() || |
|
559 isLinkable(supClassDoc, configuration))) ) { |
|
560 supClassDoc = supClassDoc.superclass(); |
|
561 } |
|
562 if (classDoc.equals(supClassDoc)) { |
|
563 return null; |
|
564 } |
|
565 return supClassDoc; |
|
566 } |
|
567 |
|
568 /** |
|
569 * Given a ClassDoc, return the name of its type (Class, Interface, etc.). |
|
570 * |
|
571 * @param cd the ClassDoc to check. |
|
572 * @param lowerCaseOnly true if you want the name returned in lower case. |
|
573 * If false, the first letter of the name is capitalized. |
|
574 * @return |
|
575 */ |
|
576 public static String getTypeName(Configuration config, |
|
577 ClassDoc cd, boolean lowerCaseOnly) { |
|
578 String typeName = ""; |
|
579 if (cd.isOrdinaryClass()) { |
|
580 typeName = "doclet.Class"; |
|
581 } else if (cd.isInterface()) { |
|
582 typeName = "doclet.Interface"; |
|
583 } else if (cd.isException()) { |
|
584 typeName = "doclet.Exception"; |
|
585 } else if (cd.isError()) { |
|
586 typeName = "doclet.Error"; |
|
587 } else if (cd.isAnnotationType()) { |
|
588 typeName = "doclet.AnnotationType"; |
|
589 } else if (cd.isEnum()) { |
|
590 typeName = "doclet.Enum"; |
|
591 } |
|
592 return config.getText( |
|
593 lowerCaseOnly ? StringUtils.toLowerCase(typeName) : typeName); |
|
594 } |
|
595 |
|
596 /** |
|
597 * Replace all tabs in a string with the appropriate number of spaces. |
|
598 * The string may be a multi-line string. |
|
599 * @param configuration the doclet configuration defining the setting for the |
|
600 * tab length. |
|
601 * @param text the text for which the tabs should be expanded |
|
602 * @return the text with all tabs expanded |
|
603 */ |
|
604 public static String replaceTabs(Configuration configuration, String text) { |
|
605 if (text.indexOf("\t") == -1) |
|
606 return text; |
|
607 |
|
608 final int tabLength = configuration.sourcetab; |
|
609 final String whitespace = configuration.tabSpaces; |
|
610 final int textLength = text.length(); |
|
611 StringBuilder result = new StringBuilder(textLength); |
|
612 int pos = 0; |
|
613 int lineLength = 0; |
|
614 for (int i = 0; i < textLength; i++) { |
|
615 char ch = text.charAt(i); |
|
616 switch (ch) { |
|
617 case '\n': case '\r': |
|
618 lineLength = 0; |
|
619 break; |
|
620 case '\t': |
|
621 result.append(text, pos, i); |
|
622 int spaceCount = tabLength - lineLength % tabLength; |
|
623 result.append(whitespace, 0, spaceCount); |
|
624 lineLength += spaceCount; |
|
625 pos = i + 1; |
|
626 break; |
|
627 default: |
|
628 lineLength++; |
|
629 } |
|
630 } |
|
631 result.append(text, pos, textLength); |
|
632 return result.toString(); |
|
633 } |
|
634 |
|
635 public static String normalizeNewlines(String text) { |
|
636 StringBuilder sb = new StringBuilder(); |
|
637 final int textLength = text.length(); |
|
638 final String NL = DocletConstants.NL; |
|
639 int pos = 0; |
|
640 for (int i = 0; i < textLength; i++) { |
|
641 char ch = text.charAt(i); |
|
642 switch (ch) { |
|
643 case '\n': |
|
644 sb.append(text, pos, i); |
|
645 sb.append(NL); |
|
646 pos = i + 1; |
|
647 break; |
|
648 case '\r': |
|
649 sb.append(text, pos, i); |
|
650 sb.append(NL); |
|
651 if (i + 1 < textLength && text.charAt(i + 1) == '\n') |
|
652 i++; |
|
653 pos = i + 1; |
|
654 break; |
|
655 } |
|
656 } |
|
657 sb.append(text, pos, textLength); |
|
658 return sb.toString(); |
|
659 } |
|
660 |
|
661 /** |
|
662 * The documentation for values() and valueOf() in Enums are set by the |
|
663 * doclet. |
|
664 */ |
|
665 public static void setEnumDocumentation(Configuration configuration, |
|
666 ClassDoc classDoc) { |
|
667 MethodDoc[] methods = classDoc.methods(); |
|
668 for (int j = 0; j < methods.length; j++) { |
|
669 MethodDoc currentMethod = methods[j]; |
|
670 if (currentMethod.name().equals("values") && |
|
671 currentMethod.parameters().length == 0) { |
|
672 StringBuilder sb = new StringBuilder(); |
|
673 sb.append(configuration.getText("doclet.enum_values_doc.main", classDoc.name())); |
|
674 sb.append("\n@return "); |
|
675 sb.append(configuration.getText("doclet.enum_values_doc.return")); |
|
676 currentMethod.setRawCommentText(sb.toString()); |
|
677 } else if (currentMethod.name().equals("valueOf") && |
|
678 currentMethod.parameters().length == 1) { |
|
679 Type paramType = currentMethod.parameters()[0].type(); |
|
680 if (paramType != null && |
|
681 paramType.qualifiedTypeName().equals(String.class.getName())) { |
|
682 StringBuilder sb = new StringBuilder(); |
|
683 sb.append(configuration.getText("doclet.enum_valueof_doc.main", classDoc.name())); |
|
684 sb.append("\n@param name "); |
|
685 sb.append(configuration.getText("doclet.enum_valueof_doc.param_name")); |
|
686 sb.append("\n@return "); |
|
687 sb.append(configuration.getText("doclet.enum_valueof_doc.return")); |
|
688 sb.append("\n@throws IllegalArgumentException "); |
|
689 sb.append(configuration.getText("doclet.enum_valueof_doc.throws_ila")); |
|
690 sb.append("\n@throws NullPointerException "); |
|
691 sb.append(configuration.getText("doclet.enum_valueof_doc.throws_npe")); |
|
692 currentMethod.setRawCommentText(sb.toString()); |
|
693 } |
|
694 } |
|
695 } |
|
696 } |
|
697 |
|
698 /** |
|
699 * Return true if the given Doc is deprecated. |
|
700 * |
|
701 * @param doc the Doc to check. |
|
702 * @return true if the given Doc is deprecated. |
|
703 */ |
|
704 public static boolean isDeprecated(Doc doc) { |
|
705 if (doc.tags("deprecated").length > 0) { |
|
706 return true; |
|
707 } |
|
708 AnnotationDesc[] annotationDescList; |
|
709 if (doc instanceof PackageDoc) |
|
710 annotationDescList = ((PackageDoc)doc).annotations(); |
|
711 else |
|
712 annotationDescList = ((ProgramElementDoc)doc).annotations(); |
|
713 for (int i = 0; i < annotationDescList.length; i++) { |
|
714 if (annotationDescList[i].annotationType().qualifiedName().equals( |
|
715 java.lang.Deprecated.class.getName())){ |
|
716 return true; |
|
717 } |
|
718 } |
|
719 return false; |
|
720 } |
|
721 |
|
722 /** |
|
723 * A convenience method to get property name from the name of the |
|
724 * getter or setter method. |
|
725 * @param name name of the getter or setter method. |
|
726 * @return the name of the property of the given setter of getter. |
|
727 */ |
|
728 public static String propertyNameFromMethodName(Configuration configuration, String name) { |
|
729 String propertyName = null; |
|
730 if (name.startsWith("get") || name.startsWith("set")) { |
|
731 propertyName = name.substring(3); |
|
732 } else if (name.startsWith("is")) { |
|
733 propertyName = name.substring(2); |
|
734 } |
|
735 if ((propertyName == null) || propertyName.isEmpty()){ |
|
736 return ""; |
|
737 } |
|
738 return propertyName.substring(0, 1).toLowerCase(configuration.getLocale()) |
|
739 + propertyName.substring(1); |
|
740 } |
|
741 |
|
742 /** |
|
743 * In case of JavaFX mode on, filters out classes that are private, |
|
744 * package private or having the @treatAsPrivate annotation. Those are not |
|
745 * documented in JavaFX mode. |
|
746 * |
|
747 * @param classes array of classes to be filtered. |
|
748 * @param javafx set to true if in JavaFX mode. |
|
749 * @return list of filtered classes. |
|
750 */ |
|
751 public static ClassDoc[] filterOutPrivateClasses(final ClassDoc[] classes, |
|
752 boolean javafx) { |
|
753 if (!javafx) { |
|
754 return classes; |
|
755 } |
|
756 final List<ClassDoc> filteredOutClasses = |
|
757 new ArrayList<ClassDoc>(classes.length); |
|
758 for (ClassDoc classDoc : classes) { |
|
759 if (classDoc.isPrivate() || classDoc.isPackagePrivate()) { |
|
760 continue; |
|
761 } |
|
762 Tag[] aspTags = classDoc.tags("treatAsPrivate"); |
|
763 if (aspTags != null && aspTags.length > 0) { |
|
764 continue; |
|
765 } |
|
766 filteredOutClasses.add(classDoc); |
|
767 } |
|
768 |
|
769 return filteredOutClasses.toArray(new ClassDoc[0]); |
|
770 } |
|
771 |
|
772 /** |
|
773 * Test whether the given FieldDoc is one of the declaration annotation ElementTypes |
|
774 * defined in Java 5. |
|
775 * Instead of testing for one of the new enum constants added in Java 8, test for |
|
776 * the old constants. This prevents bootstrapping problems. |
|
777 * |
|
778 * @param elt The FieldDoc to test |
|
779 * @return true, iff the given ElementType is one of the constants defined in Java 5 |
|
780 * @since 1.8 |
|
781 */ |
|
782 public static boolean isJava5DeclarationElementType(FieldDoc elt) { |
|
783 return elt.name().contentEquals(ElementType.ANNOTATION_TYPE.name()) || |
|
784 elt.name().contentEquals(ElementType.CONSTRUCTOR.name()) || |
|
785 elt.name().contentEquals(ElementType.FIELD.name()) || |
|
786 elt.name().contentEquals(ElementType.LOCAL_VARIABLE.name()) || |
|
787 elt.name().contentEquals(ElementType.METHOD.name()) || |
|
788 elt.name().contentEquals(ElementType.PACKAGE.name()) || |
|
789 elt.name().contentEquals(ElementType.PARAMETER.name()) || |
|
790 elt.name().contentEquals(ElementType.TYPE.name()); |
|
791 } |
|
792 } |