1 /* 2 * Copyright (c) 1999, 2014, 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.Documented; 30 import java.lang.annotation.ElementType; 31 import java.lang.annotation.Target; 32 import java.text.Collator; 33 import java.util.*; 34 35 import javax.tools.StandardLocation; 36 37 import com.sun.javadoc.*; 38 import com.sun.javadoc.AnnotationDesc.ElementValuePair; 39 import com.sun.tools.doclets.internal.toolkit.*; 40 import com.sun.tools.javac.util.StringUtils; 41 42 /** 43 * Utilities Class for Doclets. 44 * 45 * <p><b>This is NOT part of any supported API. 46 * If you write code that depends on this, you do so at your own risk. 47 * This code and its internal interfaces are subject to change or 48 * deletion without notice.</b> 49 * 50 * @author Atul M Dambalkar 51 * @author Jamie Ho 52 */ 53 public class Utils { 54 /** 55 * Return array of class members whose documentation is to be generated. 56 * If the member is deprecated do not include such a member in the 57 * returned array. 58 * 59 * @param members Array of members to choose from. 60 * @return ProgramElementDoc[] Array of eligible members for whom 61 * documentation is getting generated. 62 */ 63 public ProgramElementDoc[] excludeDeprecatedMembers( 64 ProgramElementDoc[] members) { 65 return 66 toProgramElementDocArray(excludeDeprecatedMembersAsList(members)); 67 } 68 69 /** 70 * Return array of class members whose documentation is to be generated. 71 * If the member is deprecated do not include such a member in the 72 * returned array. 73 * 74 * @param members Array of members to choose from. 75 * @return List List of eligible members for whom 76 * documentation is getting generated. 77 */ 78 public List<ProgramElementDoc> excludeDeprecatedMembersAsList( 79 ProgramElementDoc[] members) { 80 List<ProgramElementDoc> list = new ArrayList<>(); 81 for (ProgramElementDoc member : members) { 82 if (member.tags("deprecated").length == 0) { 83 list.add(member); 84 } 85 } 86 Collections.sort(list); 87 return list; 88 } 89 90 /** 91 * Return the list of ProgramElementDoc objects as Array. 92 */ 93 public ProgramElementDoc[] toProgramElementDocArray(List<ProgramElementDoc> list) { 94 ProgramElementDoc[] pgmarr = new ProgramElementDoc[list.size()]; 95 for (int i = 0; i < list.size(); i++) { 96 pgmarr[i] = list.get(i); 97 } 98 return pgmarr; 99 } 100 101 /** 102 * Return true if a non-public member found in the given array. 103 * 104 * @param members Array of members to look into. 105 * @return boolean True if non-public member found, false otherwise. 106 */ 107 public boolean nonPublicMemberFound(ProgramElementDoc[] members) { 108 for (ProgramElementDoc member : members) { 109 if (!member.isPublic()) { 110 return true; 111 } 112 } 113 return false; 114 } 115 116 /** 117 * Search for the given method in the given class. 118 * 119 * @param cd Class to search into. 120 * @param method Method to be searched. 121 * @return MethodDoc Method found, null otherwise. 122 */ 123 public MethodDoc findMethod(ClassDoc cd, MethodDoc method) { 124 MethodDoc[] methods = cd.methods(); 125 for (MethodDoc m : methods) { 126 if (executableMembersEqual(method, m)) { 127 return m; 128 129 } 130 } 131 return null; 132 } 133 134 /** 135 * @param member1 the first method to compare. 136 * @param member2 the second method to compare. 137 * @return true if member1 overrides/hides or is overriden/hidden by member2. 138 */ 139 public boolean executableMembersEqual(ExecutableMemberDoc member1, 140 ExecutableMemberDoc member2) { 141 if (! (member1 instanceof MethodDoc && member2 instanceof MethodDoc)) 142 return false; 143 144 MethodDoc method1 = (MethodDoc) member1; 145 MethodDoc method2 = (MethodDoc) member2; 146 if (method1.isStatic() && method2.isStatic()) { 147 Parameter[] targetParams = method1.parameters(); 148 Parameter[] currentParams; 149 if (method1.name().equals(method2.name()) && 150 (currentParams = method2.parameters()).length == 151 targetParams.length) { 152 int j; 153 for (j = 0; j < targetParams.length; j++) { 154 if (! (targetParams[j].typeName().equals( 155 currentParams[j].typeName()) || 156 currentParams[j].type() instanceof TypeVariable || 157 targetParams[j].type() instanceof TypeVariable)) { 158 break; 159 } 160 } 161 if (j == targetParams.length) { 162 return true; 163 } 164 } 165 return false; 166 } else { 167 return method1.overrides(method2) || 168 method2.overrides(method1) || 169 member1 == member2; 170 } 171 } 172 173 /** 174 * According to 175 * <cite>The Java™ Language Specification</cite>, 176 * all the outer classes and static inner classes are core classes. 177 */ 178 public boolean isCoreClass(ClassDoc cd) { 179 return cd.containingClass() == null || cd.isStatic(); 180 } 181 182 public boolean matches(ProgramElementDoc doc1, 183 ProgramElementDoc doc2) { 184 if (doc1 instanceof ExecutableMemberDoc && 185 doc2 instanceof ExecutableMemberDoc) { 186 ExecutableMemberDoc ed1 = (ExecutableMemberDoc)doc1; 187 ExecutableMemberDoc ed2 = (ExecutableMemberDoc)doc2; 188 return executableMembersEqual(ed1, ed2); 189 } else { 190 return doc1.name().equals(doc2.name()); 191 } 192 } 193 194 /** 195 * Copy the given directory contents from the source package directory 196 * to the generated documentation directory. For example for a package 197 * java.lang this method find out the source location of the package using 198 * {@link SourcePath} and if given directory is found in the source 199 * directory structure, copy the entire directory, to the generated 200 * documentation hierarchy. 201 * 202 * @param configuration The configuration of the current doclet. 203 * @param path The relative path to the directory to be copied. 204 * @param dir The original directory name to copy from. 205 * @param overwrite Overwrite files if true. 206 */ 207 public void copyDocFiles(Configuration configuration, PackageDoc pd) { 208 copyDocFiles(configuration, DocPath.forPackage(pd).resolve(DocPaths.DOC_FILES)); 209 } 210 211 public void copyDocFiles(Configuration configuration, DocPath dir) { 212 try { 213 boolean first = true; 214 for (DocFile f : DocFile.list(configuration, StandardLocation.SOURCE_PATH, dir)) { 215 if (!f.isDirectory()) { 216 continue; 217 } 218 DocFile srcdir = f; 219 DocFile destdir = DocFile.createFileForOutput(configuration, dir); 220 if (srcdir.isSameFile(destdir)) { 221 continue; 222 } 223 224 for (DocFile srcfile: srcdir.list()) { 225 DocFile destfile = destdir.resolve(srcfile.getName()); 226 if (srcfile.isFile()) { 227 if (destfile.exists() && !first) { 228 configuration.message.warning((SourcePosition) null, 229 "doclet.Copy_Overwrite_warning", 230 srcfile.getPath(), destdir.getPath()); 231 } else { 232 configuration.message.notice( 233 "doclet.Copying_File_0_To_Dir_1", 234 srcfile.getPath(), destdir.getPath()); 235 destfile.copyFile(srcfile); 236 } 237 } else if (srcfile.isDirectory()) { 238 if (configuration.copydocfilesubdirs 239 && !configuration.shouldExcludeDocFileDir(srcfile.getName())) { 240 copyDocFiles(configuration, dir.resolve(srcfile.getName())); 241 } 242 } 243 } 244 245 first = false; 246 } 247 } catch (SecurityException | IOException exc) { 248 throw new DocletAbortException(exc); 249 } 250 } 251 /** 252 * Returns a TypeComparator object suitable for sorting Types. 253 * @return a TypeComparator objectt 254 */ 255 public Comparator<Type> makeTypeComparator() { 256 return new TypeComparator(); 257 } 258 /** 259 * We want the list of types in alphabetical order. However, types are not 260 * comparable. We need a comparator for now. 261 */ 262 private static class TypeComparator implements Comparator<Type> { 263 public int compare(Type type1, Type type2) { 264 return compareStrings(type1.qualifiedTypeName(), type2.qualifiedTypeName()); 265 } 266 } 267 268 /** 269 * For the class return all implemented interfaces including the 270 * superinterfaces of the implementing interfaces, also iterate over for 271 * all the superclasses. For interface return all the extended interfaces 272 * as well as superinterfaces for those extended interfaces. 273 * 274 * @param type type whose implemented or 275 * super interfaces are sought. 276 * @param configuration the current configuration of the doclet. 277 * @param sort if true, return list of interfaces sorted alphabetically. 278 * @return List of all the required interfaces. 279 */ 280 public List<Type> getAllInterfaces(Type type, 281 Configuration configuration, boolean sort) { 282 Map<ClassDoc,Type> results = sort ? 283 new TreeMap<ClassDoc,Type>() : 284 new LinkedHashMap<ClassDoc,Type>(); 285 Type[] interfaceTypes = null; 286 Type superType = null; 287 if (type instanceof ParameterizedType) { 288 interfaceTypes = ((ParameterizedType) type).interfaceTypes(); 289 superType = ((ParameterizedType) type).superclassType(); 290 } else if (type instanceof ClassDoc) { 291 interfaceTypes = ((ClassDoc) type).interfaceTypes(); 292 superType = ((ClassDoc) type).superclassType(); 293 } else { 294 interfaceTypes = type.asClassDoc().interfaceTypes(); 295 superType = type.asClassDoc().superclassType(); 296 } 297 298 for (Type interfaceType : interfaceTypes) { 299 ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); 300 if (!(interfaceClassDoc.isPublic() || 301 (configuration == null || 302 isLinkable(interfaceClassDoc, configuration)))) { 303 continue; 304 } 305 results.put(interfaceClassDoc, interfaceType); 306 for (Type t : getAllInterfaces(interfaceType, configuration, sort)) { 307 results.put(t.asClassDoc(), t); 308 } 309 } 310 if (superType == null) 311 return new ArrayList<>(results.values()); 312 //Try walking the tree. 313 addAllInterfaceTypes(results, 314 superType, 315 interfaceTypesOf(superType), 316 false, configuration); 317 List<Type> resultsList = new ArrayList<>(results.values()); 318 if (sort) { 319 Collections.sort(resultsList, new TypeComparator()); 320 } 321 return resultsList; 322 } 323 324 private Type[] interfaceTypesOf(Type type) { 325 if (type instanceof AnnotatedType) 326 type = ((AnnotatedType)type).underlyingType(); 327 return type instanceof ClassDoc ? 328 ((ClassDoc)type).interfaceTypes() : 329 ((ParameterizedType)type).interfaceTypes(); 330 } 331 332 public List<Type> getAllInterfaces(Type type, Configuration configuration) { 333 return getAllInterfaces(type, configuration, true); 334 } 335 336 private void findAllInterfaceTypes(Map<ClassDoc,Type> results, ClassDoc c, boolean raw, 337 Configuration configuration) { 338 Type superType = c.superclassType(); 339 if (superType == null) 340 return; 341 addAllInterfaceTypes(results, superType, 342 interfaceTypesOf(superType), 343 raw, configuration); 344 } 345 346 private void findAllInterfaceTypes(Map<ClassDoc,Type> results, ParameterizedType p, 347 Configuration configuration) { 348 Type superType = p.superclassType(); 349 if (superType == null) 350 return; 351 addAllInterfaceTypes(results, superType, 352 interfaceTypesOf(superType), 353 false, configuration); 354 } 355 356 private void addAllInterfaceTypes(Map<ClassDoc,Type> results, Type type, 357 Type[] interfaceTypes, boolean raw, 358 Configuration configuration) { 359 for (Type interfaceType : interfaceTypes) { 360 ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); 361 if (!(interfaceClassDoc.isPublic() || 362 (configuration != null && 363 isLinkable(interfaceClassDoc, configuration)))) { 364 continue; 365 } 366 if (raw) 367 interfaceType = interfaceType.asClassDoc(); 368 results.put(interfaceClassDoc, interfaceType); 369 List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration); 370 for (Type superInterface : superInterfaces) { 371 results.put(superInterface.asClassDoc(), superInterface); 372 } 373 } 374 if (type instanceof AnnotatedType) 375 type = ((AnnotatedType)type).underlyingType(); 376 377 if (type instanceof ParameterizedType) 378 findAllInterfaceTypes(results, (ParameterizedType) type, configuration); 379 else if (((ClassDoc) type).typeParameters().length == 0) 380 findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration); 381 else 382 findAllInterfaceTypes(results, (ClassDoc) type, true, configuration); 383 } 384 385 /** 386 * Enclose in quotes, used for paths and filenames that contains spaces 387 */ 388 public String quote(String filepath) { 389 return ("\"" + filepath + "\""); 390 } 391 392 /** 393 * Given a package, return its name. 394 * @param packageDoc the package to check. 395 * @return the name of the given package. 396 */ 397 public String getPackageName(PackageDoc packageDoc) { 398 return packageDoc == null || packageDoc.name().length() == 0 ? 399 DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name(); 400 } 401 402 /** 403 * Given a package, return its file name without the extension. 404 * @param packageDoc the package to check. 405 * @return the file name of the given package. 406 */ 407 public String getPackageFileHeadName(PackageDoc packageDoc) { 408 return packageDoc == null || packageDoc.name().length() == 0 ? 409 DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name(); 410 } 411 412 /** 413 * Given a string, replace all occurrences of 'newStr' with 'oldStr'. 414 * @param originalStr the string to modify. 415 * @param oldStr the string to replace. 416 * @param newStr the string to insert in place of the old string. 417 */ 418 public String replaceText(String originalStr, String oldStr, 419 String newStr) { 420 if (oldStr == null || newStr == null || oldStr.equals(newStr)) { 421 return originalStr; 422 } 423 return originalStr.replace(oldStr, newStr); 424 } 425 426 /** 427 * Given an annotation, return true if it should be documented and false 428 * otherwise. 429 * 430 * @param annotationDoc the annotation to check. 431 * 432 * @return true return true if it should be documented and false otherwise. 433 */ 434 public boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) { 435 for (AnnotationDesc anno : annotationDoc.annotations()) { 436 if (anno.annotationType().qualifiedName().equals( 437 Documented.class.getName())) { 438 return true; 439 } 440 } 441 return false; 442 } 443 444 private boolean isDeclarationTarget(AnnotationDesc targetAnno) { 445 // The error recovery steps here are analogous to TypeAnnotations 446 ElementValuePair[] elems = targetAnno.elementValues(); 447 if (elems == null 448 || elems.length != 1 449 || !"value".equals(elems[0].element().name()) 450 || !(elems[0].value().value() instanceof AnnotationValue[])) 451 return true; // error recovery 452 453 for (AnnotationValue aValue : (AnnotationValue[])elems[0].value().value()) { 454 Object value = aValue.value(); 455 if (!(value instanceof FieldDoc)) 456 return true; // error recovery 457 458 FieldDoc eValue = (FieldDoc) value; 459 if (isJava5DeclarationElementType(eValue)) { 460 return true; 461 } 462 } 463 464 return false; 465 } 466 467 /** 468 * Returns true if the {@code annotationDoc} is to be treated 469 * as a declaration annotation, when targeting the 470 * {@code elemType} element type. 471 * 472 * @param annotationDoc the annotationDoc to check 473 * @param elemType the targeted elemType 474 * @return true if annotationDoc is a declaration annotation 475 */ 476 public boolean isDeclarationAnnotation(AnnotationTypeDoc annotationDoc, 477 boolean isJava5DeclarationLocation) { 478 if (!isJava5DeclarationLocation) 479 return false; 480 AnnotationDesc[] annotationDescList = annotationDoc.annotations(); 481 // Annotations with no target are treated as declaration as well 482 if (annotationDescList.length==0) 483 return true; 484 for (AnnotationDesc anno : annotationDescList) { 485 if (anno.annotationType().qualifiedName().equals( 486 Target.class.getName())) { 487 if (isDeclarationTarget(anno)) 488 return true; 489 } 490 } 491 return false; 492 } 493 494 /** 495 * Return true if this class is linkable and false if we can't link to the 496 * desired class. 497 * <br> 498 * <b>NOTE:</b> You can only link to external classes if they are public or 499 * protected. 500 * 501 * @param classDoc the class to check. 502 * @param configuration the current configuration of the doclet. 503 * 504 * @return true if this class is linkable and false if we can't link to the 505 * desired class. 506 */ 507 public boolean isLinkable(ClassDoc classDoc, 508 Configuration configuration) { 509 return 510 ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) || 511 (configuration.extern.isExternal(classDoc) && 512 (classDoc.isPublic() || classDoc.isProtected())); 513 } 514 515 /** 516 * Given a class, return the closest visible super class. 517 * 518 * @param classDoc the class we are searching the parent for. 519 * @param configuration the current configuration of the doclet. 520 * @return the closest visible super class. Return null if it cannot 521 * be found (i.e. classDoc is java.lang.Object). 522 */ 523 public Type getFirstVisibleSuperClass(ClassDoc classDoc, 524 Configuration configuration) { 525 if (classDoc == null) { 526 return null; 527 } 528 Type sup = classDoc.superclassType(); 529 ClassDoc supClassDoc = classDoc.superclass(); 530 while (sup != null && 531 (! (supClassDoc.isPublic() || 532 isLinkable(supClassDoc, configuration))) ) { 533 if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName())) 534 break; 535 sup = supClassDoc.superclassType(); 536 supClassDoc = supClassDoc.superclass(); 537 } 538 if (classDoc.equals(supClassDoc)) { 539 return null; 540 } 541 return sup; 542 } 543 544 /** 545 * Given a class, return the closest visible super class. 546 * 547 * @param classDoc the class we are searching the parent for. 548 * @param configuration the current configuration of the doclet. 549 * @return the closest visible super class. Return null if it cannot 550 * be found (i.e. classDoc is java.lang.Object). 551 */ 552 public ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc, 553 Configuration configuration) { 554 if (classDoc == null) { 555 return null; 556 } 557 ClassDoc supClassDoc = classDoc.superclass(); 558 while (supClassDoc != null && 559 (! (supClassDoc.isPublic() || 560 isLinkable(supClassDoc, configuration))) ) { 561 supClassDoc = supClassDoc.superclass(); 562 } 563 if (classDoc.equals(supClassDoc)) { 564 return null; 565 } 566 return supClassDoc; 567 } 568 569 /** 570 * Given a ClassDoc, return the name of its type (Class, Interface, etc.). 571 * 572 * @param cd the ClassDoc to check. 573 * @param lowerCaseOnly true if you want the name returned in lower case. 574 * If false, the first letter of the name is capitalized. 575 * @return 576 */ 577 public String getTypeName(Configuration config, 578 ClassDoc cd, boolean lowerCaseOnly) { 579 String typeName = ""; 580 if (cd.isOrdinaryClass()) { 581 typeName = "doclet.Class"; 582 } else if (cd.isInterface()) { 583 typeName = "doclet.Interface"; 584 } else if (cd.isException()) { 585 typeName = "doclet.Exception"; 586 } else if (cd.isError()) { 587 typeName = "doclet.Error"; 588 } else if (cd.isAnnotationType()) { 589 typeName = "doclet.AnnotationType"; 590 } else if (cd.isEnum()) { 591 typeName = "doclet.Enum"; 592 } 593 return config.getText( 594 lowerCaseOnly ? StringUtils.toLowerCase(typeName) : typeName); 595 } 596 597 /** 598 * Replace all tabs in a string with the appropriate number of spaces. 599 * The string may be a multi-line string. 600 * @param configuration the doclet configuration defining the setting for the 601 * tab length. 602 * @param text the text for which the tabs should be expanded 603 * @return the text with all tabs expanded 604 */ 605 public String replaceTabs(Configuration configuration, String text) { 606 if (!text.contains("\t")) 607 return text; 608 609 final int tabLength = configuration.sourcetab; 610 final String whitespace = configuration.tabSpaces; 611 final int textLength = text.length(); 612 StringBuilder result = new StringBuilder(textLength); 613 int pos = 0; 614 int lineLength = 0; 615 for (int i = 0; i < textLength; i++) { 616 char ch = text.charAt(i); 617 switch (ch) { 618 case '\n': case '\r': 619 lineLength = 0; 620 break; 621 case '\t': 622 result.append(text, pos, i); 623 int spaceCount = tabLength - lineLength % tabLength; 624 result.append(whitespace, 0, spaceCount); 625 lineLength += spaceCount; 626 pos = i + 1; 627 break; 628 default: 629 lineLength++; 630 } 631 } 632 result.append(text, pos, textLength); 633 return result.toString(); 634 } 635 636 public String normalizeNewlines(String text) { 637 StringBuilder sb = new StringBuilder(); 638 final int textLength = text.length(); 639 final String NL = DocletConstants.NL; 640 int pos = 0; 641 for (int i = 0; i < textLength; i++) { 642 char ch = text.charAt(i); 643 switch (ch) { 644 case '\n': 645 sb.append(text, pos, i); 646 sb.append(NL); 647 pos = i + 1; 648 break; 649 case '\r': 650 sb.append(text, pos, i); 651 sb.append(NL); 652 if (i + 1 < textLength && text.charAt(i + 1) == '\n') 653 i++; 654 pos = i + 1; 655 break; 656 } 657 } 658 sb.append(text, pos, textLength); 659 return sb.toString(); 660 } 661 662 /** 663 * The documentation for values() and valueOf() in Enums are set by the 664 * doclet. 665 */ 666 public void setEnumDocumentation(Configuration configuration, 667 ClassDoc classDoc) { 668 for (MethodDoc currentMethod : classDoc.methods()) { 669 if (currentMethod.name().equals("values") && 670 currentMethod.parameters().length == 0) { 671 StringBuilder sb = new StringBuilder(); 672 sb.append(configuration.getText("doclet.enum_values_doc.main", classDoc.name())); 673 sb.append("\n@return "); 674 sb.append(configuration.getText("doclet.enum_values_doc.return")); 675 currentMethod.setRawCommentText(sb.toString()); 676 } else if (currentMethod.name().equals("valueOf") && 677 currentMethod.parameters().length == 1) { 678 Type paramType = currentMethod.parameters()[0].type(); 679 if (paramType != null && 680 paramType.qualifiedTypeName().equals(String.class.getName())) { 681 StringBuilder sb = new StringBuilder(); 682 sb.append(configuration.getText("doclet.enum_valueof_doc.main", classDoc.name())); 683 sb.append("\n@param name "); 684 sb.append(configuration.getText("doclet.enum_valueof_doc.param_name")); 685 sb.append("\n@return "); 686 sb.append(configuration.getText("doclet.enum_valueof_doc.return")); 687 sb.append("\n@throws IllegalArgumentException "); 688 sb.append(configuration.getText("doclet.enum_valueof_doc.throws_ila")); 689 sb.append("\n@throws NullPointerException "); 690 sb.append(configuration.getText("doclet.enum_valueof_doc.throws_npe")); 691 currentMethod.setRawCommentText(sb.toString()); 692 } 693 } 694 } 695 } 696 697 /** 698 * Return true if the given Doc is deprecated. 699 * 700 * @param doc the Doc to check. 701 * @return true if the given Doc is deprecated. 702 */ 703 public boolean isDeprecated(Doc doc) { 704 if (doc.tags("deprecated").length > 0) { 705 return true; 706 } 707 AnnotationDesc[] annotationDescList; 708 if (doc instanceof PackageDoc) 709 annotationDescList = ((PackageDoc)doc).annotations(); 710 else 711 annotationDescList = ((ProgramElementDoc)doc).annotations(); 712 for (AnnotationDesc anno : annotationDescList) { 713 if (anno.annotationType().qualifiedName().equals( 714 Deprecated.class.getName())) { 715 return true; 716 } 717 } 718 return false; 719 } 720 721 /** 722 * A convenience method to get property name from the name of the 723 * getter or setter method. 724 * @param name name of the getter or setter method. 725 * @return the name of the property of the given setter of getter. 726 */ 727 public String propertyNameFromMethodName(Configuration configuration, String name) { 728 String propertyName = null; 729 if (name.startsWith("get") || name.startsWith("set")) { 730 propertyName = name.substring(3); 731 } else if (name.startsWith("is")) { 732 propertyName = name.substring(2); 733 } 734 if ((propertyName == null) || propertyName.isEmpty()){ 735 return ""; 736 } 737 return propertyName.substring(0, 1).toLowerCase(configuration.getLocale()) 738 + propertyName.substring(1); 739 } 740 741 /** 742 * In case of JavaFX mode on, filters out classes that are private, 743 * package private or having the @treatAsPrivate annotation. Those are not 744 * documented in JavaFX mode. 745 * 746 * @param classes array of classes to be filtered. 747 * @param javafx set to true if in JavaFX mode. 748 * @return list of filtered classes. 749 */ 750 public ClassDoc[] filterOutPrivateClasses(final ClassDoc[] classes, 751 boolean javafx) { 752 if (!javafx) { 753 return classes; 754 } 755 final List<ClassDoc> filteredOutClasses = new ArrayList<>(classes.length); 756 for (ClassDoc classDoc : classes) { 757 if (classDoc.isPrivate() || classDoc.isPackagePrivate()) { 758 continue; 759 } 760 Tag[] aspTags = classDoc.tags("treatAsPrivate"); 761 if (aspTags != null && aspTags.length > 0) { 762 continue; 763 } 764 filteredOutClasses.add(classDoc); 765 } 766 767 return filteredOutClasses.toArray(new ClassDoc[0]); 768 } 769 770 /** 771 * Test whether the given FieldDoc is one of the declaration annotation ElementTypes 772 * defined in Java 5. 773 * Instead of testing for one of the new enum constants added in Java 8, test for 774 * the old constants. This prevents bootstrapping problems. 775 * 776 * @param elt The FieldDoc to test 777 * @return true, iff the given ElementType is one of the constants defined in Java 5 778 * @since 1.8 779 */ 780 public boolean isJava5DeclarationElementType(FieldDoc elt) { 781 return elt.name().contentEquals(ElementType.ANNOTATION_TYPE.name()) || 782 elt.name().contentEquals(ElementType.CONSTRUCTOR.name()) || 783 elt.name().contentEquals(ElementType.FIELD.name()) || 784 elt.name().contentEquals(ElementType.LOCAL_VARIABLE.name()) || 785 elt.name().contentEquals(ElementType.METHOD.name()) || 786 elt.name().contentEquals(ElementType.PACKAGE.name()) || 787 elt.name().contentEquals(ElementType.PARAMETER.name()) || 788 elt.name().contentEquals(ElementType.TYPE.name()); 789 } 790 791 /** 792 * A general purpose case insensitive String comparator, which compares two Strings using a Collator 793 * strength of "TERTIARY". 794 * 795 * @param s1 first String to compare. 796 * @param s2 second String to compare. 797 * @return a negative integer, zero, or a positive integer as the first 798 * argument is less than, equal to, or greater than the second. 799 */ 800 public static int compareStrings(String s1, String s2) { 801 return compareStrings(true, s1, s2); 802 } 803 /** 804 * A general purpose case sensitive String comparator, which compares two Strings using a Collator 805 * strength of "SECONDARY". 806 * 807 * @param s1 first String to compare. 808 * @param s2 second String to compare. 809 * @return a negative integer, zero, or a positive integer as the first 810 * argument is less than, equal to, or greater than the second. 811 */ 812 public static int compareCaseCompare(String s1, String s2) { 813 return compareStrings(false, s1, s2); 814 } 815 private static int compareStrings(boolean caseSensitive, String s1, String s2) { 816 Collator collator = Collator.getInstance(); 817 collator.setStrength(caseSensitive ? Collator.TERTIARY : Collator.SECONDARY); 818 return collator.compare(s1, s2); 819 } 820 /** 821 * A simple comparator which compares simple names, then the fully qualified names 822 * and finally the kinds, ClassUse comparator works well for this purpose. 823 * 824 * @return a simple general purpose doc comparator 825 */ 826 public Comparator<Doc> makeGeneralPurposeComparator() { 827 return makeComparatorForClassUse(); 828 } 829 /** 830 * A comparator for index file presentations, and are sorted as follows: 831 * 1. sort on simple names of entities 832 * 2. if equal, then compare the DocKind ex: Package, Interface etc. 833 * 3a. if equal and if the type is of ExecutableMemberDoc(Constructor, Methods), 834 * a case insensitive comparison of parameter the type signatures 835 * 3b. if equal, case sensitive comparison of the type signatures 836 * 4. finally, if equal, compare the FQNs of the entities 837 * @return a comparator for index file use 838 */ 839 public Comparator<Doc> makeComparatorForIndexUse() { 840 return new Utils.DocComparator<Doc>() { 841 /** 842 * Compare two given Doc entities, first sort on names, then on the kinds, 843 * then on the parameters only if the type is an instance of ExecutableMemberDocs, 844 * the parameters are compared and finally the fully qualified names. 845 * 846 * @param d1 - a Doc element. 847 * @param d2 - a Doc element. 848 * @return a negative integer, zero, or a positive integer as the first 849 * argument is less than, equal to, or greater than the second. 850 */ 851 public int compare(Doc d1, Doc d2) { 852 int result = compareNames(d1, d2); 853 if (result != 0) { 854 return result; 855 } 856 result = compareDocKinds(d1, d2); 857 if (result != 0) { 858 return result; 859 } 860 if (hasParameters(d1)) { 861 Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters(); 862 Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters(); 863 result = compareParameters(false, param1, param2); 864 if (result != 0) { 865 return result; 866 } 867 result = compareParameters(true, param1, param2); 868 if (result != 0) { 869 return result; 870 } 871 } 872 return compareFullyQualifiedNames(d1, d2); 873 } 874 }; 875 } 876 /** 877 * Comparator for ClassUse presentations, and sorted as follows, 878 * 1. compares simple names of entities 879 * 2. if equal, the fully qualified names of the entities 880 * 3. if equal and if applicable, the string representation of parameter types 881 * 3a. first by using case insensitive comparison 882 * 3b. second by using a case sensitive comparison 883 * 4. finally the Doc kinds ie. package, class, interface etc. 884 * @return a comparator to sort classes and members for class use 885 */ 886 public Comparator<Doc> makeComparatorForClassUse() { 887 return new Utils.DocComparator<Doc>() { 888 /** 889 * Compares two given Doc entities, first sort on name, and if 890 * applicable on the fully qualified name, and if applicable 891 * on the parameter types, and finally the DocKind. 892 * @param d1 - a Doc element. 893 * @param d2 - a Doc element. 894 * @return a negative integer, zero, or a positive integer as the first 895 * argument is less than, equal to, or greater than the second. 896 */ 897 public int compare(Doc d1, Doc d2) { 898 int result = compareNames(d1, d2); 899 if (result != 0) { 900 return result; 901 } 902 result = compareFullyQualifiedNames(d1, d2); 903 if (result != 0) { 904 return result; 905 } 906 if (hasParameters(d1) && hasParameters(d2)) { 907 Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters(); 908 Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters(); 909 result = compareParameters(false, param1, param2); 910 if (result != 0) { 911 return result; 912 } 913 return compareParameters(true, param1, param2); 914 } 915 return compareDocKinds(d1, d2); 916 } 917 }; 918 } 919 /** 920 * A general purpose comparator to sort Doc entities, basically provides the building blocks 921 * for creating specific comparators for an use-case. 922 * @param <T> a Doc entity 923 */ 924 static abstract class DocComparator<T extends Doc> implements Comparator<Doc> { 925 static enum DocKind { 926 PACKAGE, 927 CLASS, 928 ENUM, 929 INTERFACE, 930 ANNOTATION, 931 FIELD, 932 CONSTRUCTOR, 933 METHOD 934 }; 935 boolean hasParameters(Doc d) { 936 return d instanceof ExecutableMemberDoc; 937 } 938 DocKind getDocKind(Doc d) { 939 if (d.isAnnotationType() || d.isAnnotationTypeElement()) { 940 return DocKind.ANNOTATION; 941 } else if (d.isEnum() || d.isEnumConstant()) { 942 return DocKind.ENUM; 943 } else if (d.isField()) { 944 return DocKind.FIELD; 945 } else if (d.isInterface()) { 946 return DocKind.INTERFACE; 947 } else if (d.isClass()) { 948 return DocKind.CLASS; 949 } else if (d.isConstructor()) { 950 return DocKind.CONSTRUCTOR; 951 } else if (d.isMethod()) { 952 return DocKind.METHOD; 953 } else { 954 return DocKind.PACKAGE; 955 } 956 } 957 /** 958 * Compares two Doc entities' kinds, and these are ordered as defined in 959 * the DocKind enumeration. 960 * @param d1 the first Doc object 961 * @param d2 the second Doc object 962 * @return a negative integer, zero, or a positive integer as the first 963 * argument is less than, equal to, or greater than the second. 964 */ 965 protected int compareDocKinds(Doc d1, Doc d2) { 966 return getDocKind(d1).compareTo(getDocKind(d2)); 967 } 968 /** 969 * Compares arrays of parameters as a string representation of their types. 970 * 971 * @param ignoreCase specifies case sensitive or insensitive comparison. 972 * @param params1 the first parameter array. 973 * @param params2 the first parameter array. 974 * @return a negative integer, zero, or a positive integer as the first argument is less 975 * than, equal to, or greater than the second. 976 */ 977 protected int compareParameters(boolean caseSensitive, 978 Parameter[] params1, 979 Parameter[] params2) { 980 String s1 = getParametersAsString(params1); 981 String s2 = getParametersAsString(params2); 982 return compareStrings(caseSensitive, s1, s2); 983 } 984 /* 985 * This method returns a string representation solely for comparison purposes. 986 */ 987 protected String getParametersAsString(Parameter[] params) { 988 StringBuilder sb = new StringBuilder(); 989 for (Parameter param : params) { 990 Type t = param.type(); 991 // add parameter type to arrays, as TypeMirror does. 992 String tname = (t.asParameterizedType() != null && t.getElementType() != null) 993 ? t.getElementType() + t.dimension() 994 : t.toString(); 995 // prefix P for primitive and R for reference types, thus items will 996 // be ordered naturally. 997 sb.append(t.isPrimitive() ? "P" : "R").append("-").append(tname).append("-"); 998 } 999 return sb.toString(); 1000 } 1001 1002 /** 1003 * Compares two Doc entities typically the simple name of a method, 1004 * field, constructor etc. 1005 * @param d1 the first Doc. 1006 * @param d2 the second Doc. 1007 * @return a negative integer, zero, or a positive integer as the first 1008 * argument is less than, equal to, or greater than the second. 1009 */ 1010 protected int compareNames(Doc d1, Doc d2) { 1011 return compareStrings(d1.name(), d2.name()); 1012 } 1013 1014 /** 1015 * Compares the fully qualified names of the entities 1016 * @param d1 the first entity 1017 * @param d2 the second entity 1018 * @return a negative integer, zero, or a positive integer as the first 1019 * argument is less than, equal to, or greater than the second. 1020 */ 1021 protected int compareFullyQualifiedNames(Doc d1, Doc d2) { 1022 String name1 = (d1 instanceof ProgramElementDoc) 1023 ? ((ProgramElementDoc)d1).qualifiedName() 1024 : d1.name(); 1025 String name2 = (d2 instanceof ProgramElementDoc) 1026 ? ((ProgramElementDoc)d2).qualifiedName() 1027 : d2.name(); 1028 return compareStrings(name1, name2); 1029 } 1030 } 1031 }