1/* 2 * Copyright (c) 1999, 2015, 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 26package com.sun.tools.doclets.internal.toolkit.util; 27 28import java.io.*; 29import java.lang.annotation.Documented; 30import java.lang.annotation.ElementType; 31import java.lang.annotation.Target; 32import java.text.Collator; 33import java.util.*; 34 35import javax.tools.JavaFileManager.Location; 36 37import com.sun.javadoc.*; 38import com.sun.tools.doclets.internal.toolkit.*; 39import com.sun.tools.javac.util.StringUtils; 40 41/** 42 * Utilities Class for Doclets. 43 * 44 * <p><b>This is NOT part of any supported API. 45 * If you write code that depends on this, you do so at your own risk. 46 * This code and its internal interfaces are subject to change or 47 * deletion without notice.</b> 48 * 49 * @author Atul M Dambalkar 50 * @author Jamie Ho 51 */ 52@Deprecated 53public 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 Location locn = configuration.getLocationForPackage(pd); 209 copyDocFiles(configuration, locn, DocPath.forPackage(pd).resolve(DocPaths.DOC_FILES)); 210 } 211 212 public void copyDocFiles(Configuration configuration, Location locn, DocPath dir) { 213 try { 214 boolean first = true; 215 for (DocFile f : DocFile.list(configuration, locn, dir)) { 216 if (!f.isDirectory()) { 217 continue; 218 } 219 DocFile srcdir = f; 220 DocFile destdir = DocFile.createFileForOutput(configuration, dir); 221 if (srcdir.isSameFile(destdir)) { 222 continue; 223 } 224 225 for (DocFile srcfile: srcdir.list()) { 226 DocFile destfile = destdir.resolve(srcfile.getName()); 227 if (srcfile.isFile()) { 228 if (destfile.exists() && !first) { 229 configuration.message.warning((SourcePosition) null, 230 "doclet.Copy_Overwrite_warning", 231 srcfile.getPath(), destdir.getPath()); 232 } else { 233 configuration.message.notice( 234 "doclet.Copying_File_0_To_Dir_1", 235 srcfile.getPath(), destdir.getPath()); 236 destfile.copyFile(srcfile); 237 } 238 } else if (srcfile.isDirectory()) { 239 if (configuration.copydocfilesubdirs 240 && !configuration.shouldExcludeDocFileDir(srcfile.getName())) { 241 copyDocFiles(configuration, locn, dir.resolve(srcfile.getName())); 242 } 243 } 244 } 245 246 first = false; 247 } 248 } catch (SecurityException | IOException exc) { 249 throw new DocletAbortException(exc); 250 } 251 } 252 /** 253 * Returns a TypeComparator object suitable for sorting Types. 254 * @return a TypeComparator objectt 255 */ 256 public Comparator<Type> makeTypeComparator() { 257 return new TypeComparator(); 258 } 259 /** 260 * We want the list of types in alphabetical order. However, types are not 261 * comparable. We need a comparator for now. 262 */ 263 private static class TypeComparator implements Comparator<Type> { 264 public int compare(Type type1, Type type2) { 265 return compareStrings(type1.qualifiedTypeName(), type2.qualifiedTypeName()); 266 } 267 } 268 269 /** 270 * For the class return all implemented interfaces including the 271 * superinterfaces of the implementing interfaces, also iterate over for 272 * all the superclasses. For interface return all the extended interfaces 273 * as well as superinterfaces for those extended interfaces. 274 * 275 * @param type type whose implemented or 276 * super interfaces are sought. 277 * @param configuration the current configuration of the doclet. 278 * @param sort if true, return list of interfaces sorted alphabetically. 279 * @return List of all the required interfaces. 280 */ 281 public List<Type> getAllInterfaces(Type type, 282 Configuration configuration, boolean sort) { 283 Map<ClassDoc,Type> results = sort ? 284 new TreeMap<ClassDoc,Type>() : 285 new LinkedHashMap<ClassDoc,Type>(); 286 Type[] interfaceTypes = null; 287 Type superType = null; 288 if (type instanceof ParameterizedType) { 289 interfaceTypes = ((ParameterizedType) type).interfaceTypes(); 290 superType = ((ParameterizedType) type).superclassType(); 291 } else if (type instanceof ClassDoc) { 292 interfaceTypes = ((ClassDoc) type).interfaceTypes(); 293 superType = ((ClassDoc) type).superclassType(); 294 } else { 295 interfaceTypes = type.asClassDoc().interfaceTypes(); 296 superType = type.asClassDoc().superclassType(); 297 } 298 299 for (Type interfaceType : interfaceTypes) { 300 ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); 301 if (!(interfaceClassDoc.isPublic() || 302 (configuration == null || 303 isLinkable(interfaceClassDoc, configuration)))) { 304 continue; 305 } 306 results.put(interfaceClassDoc, interfaceType); 307 for (Type t : getAllInterfaces(interfaceType, configuration, sort)) { 308 results.put(t.asClassDoc(), t); 309 } 310 } 311 if (superType == null) 312 return new ArrayList<>(results.values()); 313 //Try walking the tree. 314 addAllInterfaceTypes(results, 315 superType, 316 interfaceTypesOf(superType), 317 false, configuration); 318 List<Type> resultsList = new ArrayList<>(results.values()); 319 if (sort) { 320 Collections.sort(resultsList, new TypeComparator()); 321 } 322 return resultsList; 323 } 324 325 private Type[] interfaceTypesOf(Type type) { 326 if (type instanceof AnnotatedType) 327 type = ((AnnotatedType)type).underlyingType(); 328 return type instanceof ClassDoc ? 329 ((ClassDoc)type).interfaceTypes() : 330 ((ParameterizedType)type).interfaceTypes(); 331 } 332 333 public List<Type> getAllInterfaces(Type type, Configuration configuration) { 334 return getAllInterfaces(type, configuration, true); 335 } 336 337 private void findAllInterfaceTypes(Map<ClassDoc,Type> results, ClassDoc c, boolean raw, 338 Configuration configuration) { 339 Type superType = c.superclassType(); 340 if (superType == null) 341 return; 342 addAllInterfaceTypes(results, superType, 343 interfaceTypesOf(superType), 344 raw, configuration); 345 } 346 347 private void findAllInterfaceTypes(Map<ClassDoc,Type> results, ParameterizedType p, 348 Configuration configuration) { 349 Type superType = p.superclassType(); 350 if (superType == null) 351 return; 352 addAllInterfaceTypes(results, superType, 353 interfaceTypesOf(superType), 354 false, configuration); 355 } 356 357 private void addAllInterfaceTypes(Map<ClassDoc,Type> results, Type type, 358 Type[] interfaceTypes, boolean raw, 359 Configuration configuration) { 360 for (Type interfaceType : interfaceTypes) { 361 ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); 362 if (!(interfaceClassDoc.isPublic() || 363 (configuration != null && 364 isLinkable(interfaceClassDoc, configuration)))) { 365 continue; 366 } 367 if (raw) 368 interfaceType = interfaceType.asClassDoc(); 369 results.put(interfaceClassDoc, interfaceType); 370 List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration); 371 for (Type superInterface : superInterfaces) { 372 results.put(superInterface.asClassDoc(), superInterface); 373 } 374 } 375 if (type instanceof AnnotatedType) 376 type = ((AnnotatedType)type).underlyingType(); 377 378 if (type instanceof ParameterizedType) 379 findAllInterfaceTypes(results, (ParameterizedType) type, configuration); 380 else if (((ClassDoc) type).typeParameters().length == 0) 381 findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration); 382 else 383 findAllInterfaceTypes(results, (ClassDoc) type, true, configuration); 384 } 385 386 /** 387 * Enclose in quotes, used for paths and filenames that contains spaces 388 */ 389 public String quote(String filepath) { 390 return ("\"" + filepath + "\""); 391 } 392 393 /** 394 * Given a package, return its name. 395 * @param packageDoc the package to check. 396 * @return the name of the given package. 397 */ 398 public String getPackageName(PackageDoc packageDoc) { 399 return packageDoc == null || packageDoc.name().length() == 0 ? 400 DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name(); 401 } 402 403 /** 404 * Given a package, return its file name without the extension. 405 * @param packageDoc the package to check. 406 * @return the file name of the given package. 407 */ 408 public String getPackageFileHeadName(PackageDoc packageDoc) { 409 return packageDoc == null || packageDoc.name().length() == 0 ? 410 DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name(); 411 } 412 413 /** 414 * Given a string, replace all occurrences of 'newStr' with 'oldStr'. 415 * @param originalStr the string to modify. 416 * @param oldStr the string to replace. 417 * @param newStr the string to insert in place of the old string. 418 */ 419 public String replaceText(String originalStr, String oldStr, 420 String newStr) { 421 if (oldStr == null || newStr == null || oldStr.equals(newStr)) { 422 return originalStr; 423 } 424 return originalStr.replace(oldStr, newStr); 425 } 426 427 /** 428 * Given an annotation, return true if it should be documented and false 429 * otherwise. 430 * 431 * @param annotationDoc the annotation to check. 432 * 433 * @return true return true if it should be documented and false otherwise. 434 */ 435 public boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) { 436 for (AnnotationDesc anno : annotationDoc.annotations()) { 437 if (anno.annotationType().qualifiedName().equals( 438 Documented.class.getName())) { 439 return true; 440 } 441 } 442 return false; 443 } 444 445 private boolean isDeclarationTarget(AnnotationDesc targetAnno) { 446 // The error recovery steps here are analogous to TypeAnnotations 447 AnnotationDesc.ElementValuePair[] elems = targetAnno.elementValues(); 448 if (elems == null 449 || elems.length != 1 450 || !"value".equals(elems[0].element().name()) 451 || !(elems[0].value().value() instanceof AnnotationValue[])) 452 return true; // error recovery 453 454 for (AnnotationValue aValue : (AnnotationValue[])elems[0].value().value()) { 455 Object value = aValue.value(); 456 if (!(value instanceof FieldDoc)) 457 return true; // error recovery 458 459 FieldDoc eValue = (FieldDoc) value; 460 if (isJava5DeclarationElementType(eValue)) { 461 return true; 462 } 463 } 464 465 return false; 466 } 467 468 /** 469 * Returns true if the {@code annotationDoc} is to be treated 470 * as a declaration annotation, when targeting the 471 * {@code elemType} element type. 472 * 473 * @param annotationDoc the annotationDoc to check 474 * @param elemType the targeted elemType 475 * @return true if annotationDoc is a declaration annotation 476 */ 477 public boolean isDeclarationAnnotation(AnnotationTypeDoc annotationDoc, 478 boolean isJava5DeclarationLocation) { 479 if (!isJava5DeclarationLocation) 480 return false; 481 AnnotationDesc[] annotationDescList = annotationDoc.annotations(); 482 // Annotations with no target are treated as declaration as well 483 if (annotationDescList.length==0) 484 return true; 485 for (AnnotationDesc anno : annotationDescList) { 486 if (anno.annotationType().qualifiedName().equals( 487 Target.class.getName())) { 488 if (isDeclarationTarget(anno)) 489 return true; 490 } 491 } 492 return false; 493 } 494 495 /** 496 * Return true if this class is linkable and false if we can't link to the 497 * desired class. 498 * <br> 499 * <b>NOTE:</b> You can only link to external classes if they are public or 500 * protected. 501 * 502 * @param classDoc the class to check. 503 * @param configuration the current configuration of the doclet. 504 * 505 * @return true if this class is linkable and false if we can't link to the 506 * desired class. 507 */ 508 public boolean isLinkable(ClassDoc classDoc, 509 Configuration configuration) { 510 return 511 ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) || 512 (configuration.extern.isExternal(classDoc) && 513 (classDoc.isPublic() || classDoc.isProtected())); 514 } 515 516 /** 517 * Given a class, return the closest visible super class. 518 * 519 * @param classDoc the class we are searching the parent for. 520 * @param configuration the current configuration of the doclet. 521 * @return the closest visible super class. Return null if it cannot 522 * be found (i.e. classDoc is java.lang.Object). 523 */ 524 public Type getFirstVisibleSuperClass(ClassDoc classDoc, 525 Configuration configuration) { 526 if (classDoc == null) { 527 return null; 528 } 529 Type sup = classDoc.superclassType(); 530 ClassDoc supClassDoc = classDoc.superclass(); 531 while (sup != null && 532 (! (supClassDoc.isPublic() || 533 isLinkable(supClassDoc, configuration))) ) { 534 if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName())) 535 break; 536 sup = supClassDoc.superclassType(); 537 supClassDoc = supClassDoc.superclass(); 538 } 539 if (classDoc.equals(supClassDoc)) { 540 return null; 541 } 542 return sup; 543 } 544 545 /** 546 * Given a class, return the closest visible super class. 547 * 548 * @param classDoc the class we are searching the parent for. 549 * @param configuration the current configuration of the doclet. 550 * @return the closest visible super class. Return null if it cannot 551 * be found (i.e. classDoc is java.lang.Object). 552 */ 553 public ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc, 554 Configuration configuration) { 555 if (classDoc == null) { 556 return null; 557 } 558 ClassDoc supClassDoc = classDoc.superclass(); 559 while (supClassDoc != null && 560 (! (supClassDoc.isPublic() || 561 isLinkable(supClassDoc, configuration))) ) { 562 supClassDoc = supClassDoc.superclass(); 563 } 564 if (classDoc.equals(supClassDoc)) { 565 return null; 566 } 567 return supClassDoc; 568 } 569 570 /** 571 * Given a ClassDoc, return the name of its type (Class, Interface, etc.). 572 * 573 * @param cd the ClassDoc to check. 574 * @param lowerCaseOnly true if you want the name returned in lower case. 575 * If false, the first letter of the name is capitalized. 576 * @return 577 */ 578 public String getTypeName(Configuration config, 579 ClassDoc cd, boolean lowerCaseOnly) { 580 String typeName = ""; 581 if (cd.isOrdinaryClass()) { 582 typeName = "doclet.Class"; 583 } else if (cd.isInterface()) { 584 typeName = "doclet.Interface"; 585 } else if (cd.isException()) { 586 typeName = "doclet.Exception"; 587 } else if (cd.isError()) { 588 typeName = "doclet.Error"; 589 } else if (cd.isAnnotationType()) { 590 typeName = "doclet.AnnotationType"; 591 } else if (cd.isEnum()) { 592 typeName = "doclet.Enum"; 593 } 594 return config.getText( 595 lowerCaseOnly ? StringUtils.toLowerCase(typeName) : typeName); 596 } 597 598 /** 599 * Replace all tabs in a string with the appropriate number of spaces. 600 * The string may be a multi-line string. 601 * @param configuration the doclet configuration defining the setting for the 602 * tab length. 603 * @param text the text for which the tabs should be expanded 604 * @return the text with all tabs expanded 605 */ 606 public String replaceTabs(Configuration configuration, String text) { 607 if (!text.contains("\t")) 608 return text; 609 610 final int tabLength = configuration.sourcetab; 611 final String whitespace = configuration.tabSpaces; 612 final int textLength = text.length(); 613 StringBuilder result = new StringBuilder(textLength); 614 int pos = 0; 615 int lineLength = 0; 616 for (int i = 0; i < textLength; i++) { 617 char ch = text.charAt(i); 618 switch (ch) { 619 case '\n': case '\r': 620 lineLength = 0; 621 break; 622 case '\t': 623 result.append(text, pos, i); 624 int spaceCount = tabLength - lineLength % tabLength; 625 result.append(whitespace, 0, spaceCount); 626 lineLength += spaceCount; 627 pos = i + 1; 628 break; 629 default: 630 lineLength++; 631 } 632 } 633 result.append(text, pos, textLength); 634 return result.toString(); 635 } 636 637 public String normalizeNewlines(String text) { 638 StringBuilder sb = new StringBuilder(); 639 final int textLength = text.length(); 640 final String NL = DocletConstants.NL; 641 int pos = 0; 642 for (int i = 0; i < textLength; i++) { 643 char ch = text.charAt(i); 644 switch (ch) { 645 case '\n': 646 sb.append(text, pos, i); 647 sb.append(NL); 648 pos = i + 1; 649 break; 650 case '\r': 651 sb.append(text, pos, i); 652 sb.append(NL); 653 if (i + 1 < textLength && text.charAt(i + 1) == '\n') 654 i++; 655 pos = i + 1; 656 break; 657 } 658 } 659 sb.append(text, pos, textLength); 660 return sb.toString(); 661 } 662 663 /** 664 * The documentation for values() and valueOf() in Enums are set by the 665 * doclet. 666 */ 667 public void setEnumDocumentation(Configuration configuration, 668 ClassDoc classDoc) { 669 for (MethodDoc currentMethod : classDoc.methods()) { 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 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 (AnnotationDesc anno : annotationDescList) { 714 if (anno.annotationType().qualifiedName().equals( 715 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 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 ClassDoc[] filterOutPrivateClasses(final ClassDoc[] classes, 752 boolean javafx) { 753 if (!javafx) { 754 return classes; 755 } 756 final List<ClassDoc> filteredOutClasses = new ArrayList<>(classes.length); 757 for (ClassDoc classDoc : classes) { 758 if (classDoc.isPrivate() || classDoc.isPackagePrivate()) { 759 continue; 760 } 761 Tag[] aspTags = classDoc.tags("treatAsPrivate"); 762 if (aspTags != null && aspTags.length > 0) { 763 continue; 764 } 765 filteredOutClasses.add(classDoc); 766 } 767 768 return filteredOutClasses.toArray(new ClassDoc[0]); 769 } 770 771 /** 772 * Test whether the given FieldDoc is one of the declaration annotation ElementTypes 773 * defined in Java 5. 774 * Instead of testing for one of the new enum constants added in Java 8, test for 775 * the old constants. This prevents bootstrapping problems. 776 * 777 * @param elt The FieldDoc to test 778 * @return true, iff the given ElementType is one of the constants defined in Java 5 779 * @since 1.8 780 */ 781 public boolean isJava5DeclarationElementType(FieldDoc elt) { 782 return elt.name().contentEquals(ElementType.ANNOTATION_TYPE.name()) || 783 elt.name().contentEquals(ElementType.CONSTRUCTOR.name()) || 784 elt.name().contentEquals(ElementType.FIELD.name()) || 785 elt.name().contentEquals(ElementType.LOCAL_VARIABLE.name()) || 786 elt.name().contentEquals(ElementType.METHOD.name()) || 787 elt.name().contentEquals(ElementType.PACKAGE.name()) || 788 elt.name().contentEquals(ElementType.PARAMETER.name()) || 789 elt.name().contentEquals(ElementType.TYPE.name()); 790 } 791 792 /** 793 * A general purpose case insensitive String comparator, which compares two Strings using a Collator 794 * strength of "TERTIARY". 795 * 796 * @param s1 first String to compare. 797 * @param s2 second String to compare. 798 * @return a negative integer, zero, or a positive integer as the first 799 * argument is less than, equal to, or greater than the second. 800 */ 801 public static int compareStrings(String s1, String s2) { 802 return compareStrings(true, s1, s2); 803 } 804 /** 805 * A general purpose case sensitive String comparator, which compares two Strings using a Collator 806 * strength of "SECONDARY". 807 * 808 * @param s1 first String to compare. 809 * @param s2 second String to compare. 810 * @return a negative integer, zero, or a positive integer as the first 811 * argument is less than, equal to, or greater than the second. 812 */ 813 public static int compareCaseCompare(String s1, String s2) { 814 return compareStrings(false, s1, s2); 815 } 816 private static int compareStrings(boolean caseSensitive, String s1, String s2) { 817 Collator collator = Collator.getInstance(); 818 collator.setStrength(caseSensitive ? Collator.TERTIARY : Collator.SECONDARY); 819 return collator.compare(s1, s2); 820 } 821 /** 822 * A simple comparator which compares simple names, then the fully qualified names 823 * and finally the kinds, ClassUse comparator works well for this purpose. 824 * 825 * @return a simple general purpose doc comparator 826 */ 827 public Comparator<Doc> makeGeneralPurposeComparator() { 828 return makeComparatorForClassUse(); 829 } 830 /** 831 * A comparator for index file presentations, and are sorted as follows: 832 * 1. sort on simple names of entities 833 * 2. if equal, then compare the DocKind ex: Package, Interface etc. 834 * 3a. if equal and if the type is of ExecutableMemberDoc(Constructor, Methods), 835 * a case insensitive comparison of parameter the type signatures 836 * 3b. if equal, case sensitive comparison of the type signatures 837 * 4. finally, if equal, compare the FQNs of the entities 838 * @return a comparator for index file use 839 */ 840 public Comparator<Doc> makeComparatorForIndexUse() { 841 return new Utils.DocComparator<Doc>() { 842 /** 843 * Compare two given Doc entities, first sort on names, then on the kinds, 844 * then on the parameters only if the type is an instance of ExecutableMemberDocs, 845 * the parameters are compared and finally the fully qualified names. 846 * 847 * @param d1 - a Doc element. 848 * @param d2 - a Doc element. 849 * @return a negative integer, zero, or a positive integer as the first 850 * argument is less than, equal to, or greater than the second. 851 */ 852 public int compare(Doc d1, Doc d2) { 853 int result = compareNames(d1, d2); 854 if (result != 0) { 855 return result; 856 } 857 result = compareDocKinds(d1, d2); 858 if (result != 0) { 859 return result; 860 } 861 if (hasParameters(d1)) { 862 Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters(); 863 Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters(); 864 result = compareParameters(false, param1, param2); 865 if (result != 0) { 866 return result; 867 } 868 result = compareParameters(true, param1, param2); 869 if (result != 0) { 870 return result; 871 } 872 } 873 return compareFullyQualifiedNames(d1, d2); 874 } 875 }; 876 } 877 /** 878 * Comparator for ClassUse presentations, and sorted as follows, 879 * 1. compares simple names of entities 880 * 2. if equal, the fully qualified names of the entities 881 * 3. if equal and if applicable, the string representation of parameter types 882 * 3a. first by using case insensitive comparison 883 * 3b. second by using a case sensitive comparison 884 * 4. finally the Doc kinds ie. package, class, interface etc. 885 * @return a comparator to sort classes and members for class use 886 */ 887 public Comparator<Doc> makeComparatorForClassUse() { 888 return new Utils.DocComparator<Doc>() { 889 /** 890 * Compares two given Doc entities, first sort on name, and if 891 * applicable on the fully qualified name, and if applicable 892 * on the parameter types, and finally the DocKind. 893 * @param d1 - a Doc element. 894 * @param d2 - a Doc element. 895 * @return a negative integer, zero, or a positive integer as the first 896 * argument is less than, equal to, or greater than the second. 897 */ 898 public int compare(Doc d1, Doc d2) { 899 int result = compareNames(d1, d2); 900 if (result != 0) { 901 return result; 902 } 903 result = compareFullyQualifiedNames(d1, d2); 904 if (result != 0) { 905 return result; 906 } 907 if (hasParameters(d1) && hasParameters(d2)) { 908 Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters(); 909 Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters(); 910 result = compareParameters(false, param1, param2); 911 if (result != 0) { 912 return result; 913 } 914 return compareParameters(true, param1, param2); 915 } 916 return compareDocKinds(d1, d2); 917 } 918 }; 919 } 920 /** 921 * A general purpose comparator to sort Doc entities, basically provides the building blocks 922 * for creating specific comparators for an use-case. 923 * @param <T> a Doc entity 924 */ 925 static abstract class DocComparator<T extends Doc> implements Comparator<Doc> { 926 static enum DocKind { 927 PACKAGE, 928 CLASS, 929 ENUM, 930 INTERFACE, 931 ANNOTATION, 932 FIELD, 933 CONSTRUCTOR, 934 METHOD 935 }; 936 boolean hasParameters(Doc d) { 937 return d instanceof ExecutableMemberDoc; 938 } 939 DocKind getDocKind(Doc d) { 940 if (d.isAnnotationType() || d.isAnnotationTypeElement()) { 941 return DocKind.ANNOTATION; 942 } else if (d.isEnum() || d.isEnumConstant()) { 943 return DocKind.ENUM; 944 } else if (d.isField()) { 945 return DocKind.FIELD; 946 } else if (d.isInterface()) { 947 return DocKind.INTERFACE; 948 } else if (d.isClass()) { 949 return DocKind.CLASS; 950 } else if (d.isConstructor()) { 951 return DocKind.CONSTRUCTOR; 952 } else if (d.isMethod()) { 953 return DocKind.METHOD; 954 } else { 955 return DocKind.PACKAGE; 956 } 957 } 958 /** 959 * Compares two Doc entities' kinds, and these are ordered as defined in 960 * the DocKind enumeration. 961 * @param d1 the first Doc object 962 * @param d2 the second Doc object 963 * @return a negative integer, zero, or a positive integer as the first 964 * argument is less than, equal to, or greater than the second. 965 */ 966 protected int compareDocKinds(Doc d1, Doc d2) { 967 return getDocKind(d1).compareTo(getDocKind(d2)); 968 } 969 /** 970 * Compares arrays of parameters as a string representation of their types. 971 * 972 * @param ignoreCase specifies case sensitive or insensitive comparison. 973 * @param params1 the first parameter array. 974 * @param params2 the first parameter array. 975 * @return a negative integer, zero, or a positive integer as the first argument is less 976 * than, equal to, or greater than the second. 977 */ 978 protected int compareParameters(boolean caseSensitive, 979 Parameter[] params1, 980 Parameter[] params2) { 981 String s1 = getParametersAsString(params1); 982 String s2 = getParametersAsString(params2); 983 return compareStrings(caseSensitive, s1, s2); 984 } 985 /* 986 * This method returns a string representation solely for comparison purposes. 987 */ 988 protected String getParametersAsString(Parameter[] params) { 989 StringBuilder sb = new StringBuilder(); 990 for (Parameter param : params) { 991 Type t = param.type(); 992 // add parameter type to arrays, as TypeMirror does. 993 String tname = (t.asParameterizedType() != null && t.getElementType() != null) 994 ? t.getElementType() + t.dimension() 995 : t.toString(); 996 // prefix P for primitive and R for reference types, thus items will 997 // be ordered naturally. 998 sb.append(t.isPrimitive() ? "P" : "R").append("-").append(tname).append("-"); 999 } 1000 return sb.toString(); 1001 } 1002 1003 /** 1004 * Compares two Doc entities typically the simple name of a method, 1005 * field, constructor etc. 1006 * @param d1 the first Doc. 1007 * @param d2 the second Doc. 1008 * @return a negative integer, zero, or a positive integer as the first 1009 * argument is less than, equal to, or greater than the second. 1010 */ 1011 protected int compareNames(Doc d1, Doc d2) { 1012 return compareStrings(d1.name(), d2.name()); 1013 } 1014 1015 /** 1016 * Compares the fully qualified names of the entities 1017 * @param d1 the first entity 1018 * @param d2 the second entity 1019 * @return a negative integer, zero, or a positive integer as the first 1020 * argument is less than, equal to, or greater than the second. 1021 */ 1022 protected int compareFullyQualifiedNames(Doc d1, Doc d2) { 1023 String name1 = (d1 instanceof ProgramElementDoc) 1024 ? ((ProgramElementDoc)d1).qualifiedName() 1025 : d1.name(); 1026 String name2 = (d2 instanceof ProgramElementDoc) 1027 ? ((ProgramElementDoc)d2).qualifiedName() 1028 : d2.name(); 1029 return compareStrings(name1, name2); 1030 } 1031 } 1032} 1033