Utils.java revision 2571:10fc81ac75b4
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 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.StandardLocation; 36 37import com.sun.javadoc.*; 38import com.sun.javadoc.AnnotationDesc.ElementValuePair; 39import com.sun.tools.doclets.internal.toolkit.*; 40import 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 */ 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 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 /** 253 * We want the list of types in alphabetical order. However, types are not 254 * comparable. We need a comparator for now. 255 */ 256 private static class TypeComparator implements Comparator<Type> { 257 public int compare(Type type1, Type type2) { 258 return type1.qualifiedTypeName().compareToIgnoreCase( 259 type2.qualifiedTypeName()); 260 } 261 } 262 263 /** 264 * For the class return all implemented interfaces including the 265 * superinterfaces of the implementing interfaces, also iterate over for 266 * all the superclasses. For interface return all the extended interfaces 267 * as well as superinterfaces for those extended interfaces. 268 * 269 * @param type type whose implemented or 270 * super interfaces are sought. 271 * @param configuration the current configuration of the doclet. 272 * @param sort if true, return list of interfaces sorted alphabetically. 273 * @return List of all the required interfaces. 274 */ 275 public List<Type> getAllInterfaces(Type type, 276 Configuration configuration, boolean sort) { 277 Map<ClassDoc,Type> results = sort ? 278 new TreeMap<ClassDoc,Type>() : 279 new LinkedHashMap<ClassDoc,Type>(); 280 Type[] interfaceTypes = null; 281 Type superType = null; 282 if (type instanceof ParameterizedType) { 283 interfaceTypes = ((ParameterizedType) type).interfaceTypes(); 284 superType = ((ParameterizedType) type).superclassType(); 285 } else if (type instanceof ClassDoc) { 286 interfaceTypes = ((ClassDoc) type).interfaceTypes(); 287 superType = ((ClassDoc) type).superclassType(); 288 } else { 289 interfaceTypes = type.asClassDoc().interfaceTypes(); 290 superType = type.asClassDoc().superclassType(); 291 } 292 293 for (Type interfaceType : interfaceTypes) { 294 ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); 295 if (!(interfaceClassDoc.isPublic() || 296 (configuration == null || 297 isLinkable(interfaceClassDoc, configuration)))) { 298 continue; 299 } 300 results.put(interfaceClassDoc, interfaceType); 301 for (Type t : getAllInterfaces(interfaceType, configuration, sort)) { 302 results.put(t.asClassDoc(), t); 303 } 304 } 305 if (superType == null) 306 return new ArrayList<>(results.values()); 307 //Try walking the tree. 308 addAllInterfaceTypes(results, 309 superType, 310 interfaceTypesOf(superType), 311 false, configuration); 312 List<Type> resultsList = new ArrayList<>(results.values()); 313 if (sort) { 314 Collections.sort(resultsList, new TypeComparator()); 315 } 316 return resultsList; 317 } 318 319 private 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 List<Type> getAllInterfaces(Type type, Configuration configuration) { 328 return getAllInterfaces(type, configuration, true); 329 } 330 331 private 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 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 void addAllInterfaceTypes(Map<ClassDoc,Type> results, Type type, 352 Type[] interfaceTypes, boolean raw, 353 Configuration configuration) { 354 for (Type interfaceType : interfaceTypes) { 355 ClassDoc interfaceClassDoc = interfaceType.asClassDoc(); 356 if (!(interfaceClassDoc.isPublic() || 357 (configuration != null && 358 isLinkable(interfaceClassDoc, configuration)))) { 359 continue; 360 } 361 if (raw) 362 interfaceType = interfaceType.asClassDoc(); 363 results.put(interfaceClassDoc, interfaceType); 364 List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration); 365 for (Type superInterface : superInterfaces) { 366 results.put(superInterface.asClassDoc(), superInterface); 367 } 368 } 369 if (type instanceof AnnotatedType) 370 type = ((AnnotatedType)type).underlyingType(); 371 372 if (type instanceof ParameterizedType) 373 findAllInterfaceTypes(results, (ParameterizedType) type, configuration); 374 else if (((ClassDoc) type).typeParameters().length == 0) 375 findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration); 376 else 377 findAllInterfaceTypes(results, (ClassDoc) type, true, configuration); 378 } 379 380 /** 381 * Enclose in quotes, used for paths and filenames that contains spaces 382 */ 383 public String quote(String filepath) { 384 return ("\"" + filepath + "\""); 385 } 386 387 /** 388 * Given a package, return its name. 389 * @param packageDoc the package to check. 390 * @return the name of the given package. 391 */ 392 public String getPackageName(PackageDoc packageDoc) { 393 return packageDoc == null || packageDoc.name().length() == 0 ? 394 DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name(); 395 } 396 397 /** 398 * Given a package, return its file name without the extension. 399 * @param packageDoc the package to check. 400 * @return the file name of the given package. 401 */ 402 public String getPackageFileHeadName(PackageDoc packageDoc) { 403 return packageDoc == null || packageDoc.name().length() == 0 ? 404 DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name(); 405 } 406 407 /** 408 * Given a string, replace all occurrences of 'newStr' with 'oldStr'. 409 * @param originalStr the string to modify. 410 * @param oldStr the string to replace. 411 * @param newStr the string to insert in place of the old string. 412 */ 413 public String replaceText(String originalStr, String oldStr, 414 String newStr) { 415 if (oldStr == null || newStr == null || oldStr.equals(newStr)) { 416 return originalStr; 417 } 418 return originalStr.replace(oldStr, newStr); 419 } 420 421 /** 422 * Given an annotation, return true if it should be documented and false 423 * otherwise. 424 * 425 * @param annotationDoc the annotation to check. 426 * 427 * @return true return true if it should be documented and false otherwise. 428 */ 429 public boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) { 430 for (AnnotationDesc anno : annotationDoc.annotations()) { 431 if (anno.annotationType().qualifiedName().equals( 432 Documented.class.getName())) { 433 return true; 434 } 435 } 436 return false; 437 } 438 439 private boolean isDeclarationTarget(AnnotationDesc targetAnno) { 440 // The error recovery steps here are analogous to TypeAnnotations 441 ElementValuePair[] elems = targetAnno.elementValues(); 442 if (elems == null 443 || elems.length != 1 444 || !"value".equals(elems[0].element().name()) 445 || !(elems[0].value().value() instanceof AnnotationValue[])) 446 return true; // error recovery 447 448 for (AnnotationValue aValue : (AnnotationValue[])elems[0].value().value()) { 449 Object value = aValue.value(); 450 if (!(value instanceof FieldDoc)) 451 return true; // error recovery 452 453 FieldDoc eValue = (FieldDoc) value; 454 if (isJava5DeclarationElementType(eValue)) { 455 return true; 456 } 457 } 458 459 return false; 460 } 461 462 /** 463 * Returns true if the {@code annotationDoc} is to be treated 464 * as a declaration annotation, when targeting the 465 * {@code elemType} element type. 466 * 467 * @param annotationDoc the annotationDoc to check 468 * @param elemType the targeted elemType 469 * @return true if annotationDoc is a declaration annotation 470 */ 471 public boolean isDeclarationAnnotation(AnnotationTypeDoc annotationDoc, 472 boolean isJava5DeclarationLocation) { 473 if (!isJava5DeclarationLocation) 474 return false; 475 AnnotationDesc[] annotationDescList = annotationDoc.annotations(); 476 // Annotations with no target are treated as declaration as well 477 if (annotationDescList.length==0) 478 return true; 479 for (AnnotationDesc anno : annotationDescList) { 480 if (anno.annotationType().qualifiedName().equals( 481 Target.class.getName())) { 482 if (isDeclarationTarget(anno)) 483 return true; 484 } 485 } 486 return false; 487 } 488 489 /** 490 * Return true if this class is linkable and false if we can't link to the 491 * desired class. 492 * <br> 493 * <b>NOTE:</b> You can only link to external classes if they are public or 494 * protected. 495 * 496 * @param classDoc the class to check. 497 * @param configuration the current configuration of the doclet. 498 * 499 * @return true if this class is linkable and false if we can't link to the 500 * desired class. 501 */ 502 public boolean isLinkable(ClassDoc classDoc, 503 Configuration configuration) { 504 return 505 ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) || 506 (configuration.extern.isExternal(classDoc) && 507 (classDoc.isPublic() || classDoc.isProtected())); 508 } 509 510 /** 511 * Given a class, return the closest visible super class. 512 * 513 * @param classDoc the class we are searching the parent for. 514 * @param configuration the current configuration of the doclet. 515 * @return the closest visible super class. Return null if it cannot 516 * be found (i.e. classDoc is java.lang.Object). 517 */ 518 public Type getFirstVisibleSuperClass(ClassDoc classDoc, 519 Configuration configuration) { 520 if (classDoc == null) { 521 return null; 522 } 523 Type sup = classDoc.superclassType(); 524 ClassDoc supClassDoc = classDoc.superclass(); 525 while (sup != null && 526 (! (supClassDoc.isPublic() || 527 isLinkable(supClassDoc, configuration))) ) { 528 if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName())) 529 break; 530 sup = supClassDoc.superclassType(); 531 supClassDoc = supClassDoc.superclass(); 532 } 533 if (classDoc.equals(supClassDoc)) { 534 return null; 535 } 536 return sup; 537 } 538 539 /** 540 * Given a class, return the closest visible super class. 541 * 542 * @param classDoc the class we are searching the parent for. 543 * @param configuration the current configuration of the doclet. 544 * @return the closest visible super class. Return null if it cannot 545 * be found (i.e. classDoc is java.lang.Object). 546 */ 547 public ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc, 548 Configuration configuration) { 549 if (classDoc == null) { 550 return null; 551 } 552 ClassDoc supClassDoc = classDoc.superclass(); 553 while (supClassDoc != null && 554 (! (supClassDoc.isPublic() || 555 isLinkable(supClassDoc, configuration))) ) { 556 supClassDoc = supClassDoc.superclass(); 557 } 558 if (classDoc.equals(supClassDoc)) { 559 return null; 560 } 561 return supClassDoc; 562 } 563 564 /** 565 * Given a ClassDoc, return the name of its type (Class, Interface, etc.). 566 * 567 * @param cd the ClassDoc to check. 568 * @param lowerCaseOnly true if you want the name returned in lower case. 569 * If false, the first letter of the name is capitalized. 570 * @return 571 */ 572 public String getTypeName(Configuration config, 573 ClassDoc cd, boolean lowerCaseOnly) { 574 String typeName = ""; 575 if (cd.isOrdinaryClass()) { 576 typeName = "doclet.Class"; 577 } else if (cd.isInterface()) { 578 typeName = "doclet.Interface"; 579 } else if (cd.isException()) { 580 typeName = "doclet.Exception"; 581 } else if (cd.isError()) { 582 typeName = "doclet.Error"; 583 } else if (cd.isAnnotationType()) { 584 typeName = "doclet.AnnotationType"; 585 } else if (cd.isEnum()) { 586 typeName = "doclet.Enum"; 587 } 588 return config.getText( 589 lowerCaseOnly ? StringUtils.toLowerCase(typeName) : typeName); 590 } 591 592 /** 593 * Replace all tabs in a string with the appropriate number of spaces. 594 * The string may be a multi-line string. 595 * @param configuration the doclet configuration defining the setting for the 596 * tab length. 597 * @param text the text for which the tabs should be expanded 598 * @return the text with all tabs expanded 599 */ 600 public String replaceTabs(Configuration configuration, String text) { 601 if (!text.contains("\t")) 602 return text; 603 604 final int tabLength = configuration.sourcetab; 605 final String whitespace = configuration.tabSpaces; 606 final int textLength = text.length(); 607 StringBuilder result = new StringBuilder(textLength); 608 int pos = 0; 609 int lineLength = 0; 610 for (int i = 0; i < textLength; i++) { 611 char ch = text.charAt(i); 612 switch (ch) { 613 case '\n': case '\r': 614 lineLength = 0; 615 break; 616 case '\t': 617 result.append(text, pos, i); 618 int spaceCount = tabLength - lineLength % tabLength; 619 result.append(whitespace, 0, spaceCount); 620 lineLength += spaceCount; 621 pos = i + 1; 622 break; 623 default: 624 lineLength++; 625 } 626 } 627 result.append(text, pos, textLength); 628 return result.toString(); 629 } 630 631 public String normalizeNewlines(String text) { 632 StringBuilder sb = new StringBuilder(); 633 final int textLength = text.length(); 634 final String NL = DocletConstants.NL; 635 int pos = 0; 636 for (int i = 0; i < textLength; i++) { 637 char ch = text.charAt(i); 638 switch (ch) { 639 case '\n': 640 sb.append(text, pos, i); 641 sb.append(NL); 642 pos = i + 1; 643 break; 644 case '\r': 645 sb.append(text, pos, i); 646 sb.append(NL); 647 if (i + 1 < textLength && text.charAt(i + 1) == '\n') 648 i++; 649 pos = i + 1; 650 break; 651 } 652 } 653 sb.append(text, pos, textLength); 654 return sb.toString(); 655 } 656 657 /** 658 * The documentation for values() and valueOf() in Enums are set by the 659 * doclet. 660 */ 661 public void setEnumDocumentation(Configuration configuration, 662 ClassDoc classDoc) { 663 for (MethodDoc currentMethod : classDoc.methods()) { 664 if (currentMethod.name().equals("values") && 665 currentMethod.parameters().length == 0) { 666 StringBuilder sb = new StringBuilder(); 667 sb.append(configuration.getText("doclet.enum_values_doc.main", classDoc.name())); 668 sb.append("\n@return "); 669 sb.append(configuration.getText("doclet.enum_values_doc.return")); 670 currentMethod.setRawCommentText(sb.toString()); 671 } else if (currentMethod.name().equals("valueOf") && 672 currentMethod.parameters().length == 1) { 673 Type paramType = currentMethod.parameters()[0].type(); 674 if (paramType != null && 675 paramType.qualifiedTypeName().equals(String.class.getName())) { 676 StringBuilder sb = new StringBuilder(); 677 sb.append(configuration.getText("doclet.enum_valueof_doc.main", classDoc.name())); 678 sb.append("\n@param name "); 679 sb.append(configuration.getText("doclet.enum_valueof_doc.param_name")); 680 sb.append("\n@return "); 681 sb.append(configuration.getText("doclet.enum_valueof_doc.return")); 682 sb.append("\n@throws IllegalArgumentException "); 683 sb.append(configuration.getText("doclet.enum_valueof_doc.throws_ila")); 684 sb.append("\n@throws NullPointerException "); 685 sb.append(configuration.getText("doclet.enum_valueof_doc.throws_npe")); 686 currentMethod.setRawCommentText(sb.toString()); 687 } 688 } 689 } 690 } 691 692 /** 693 * Return true if the given Doc is deprecated. 694 * 695 * @param doc the Doc to check. 696 * @return true if the given Doc is deprecated. 697 */ 698 public boolean isDeprecated(Doc doc) { 699 if (doc.tags("deprecated").length > 0) { 700 return true; 701 } 702 AnnotationDesc[] annotationDescList; 703 if (doc instanceof PackageDoc) 704 annotationDescList = ((PackageDoc)doc).annotations(); 705 else 706 annotationDescList = ((ProgramElementDoc)doc).annotations(); 707 for (AnnotationDesc anno : annotationDescList) { 708 if (anno.annotationType().qualifiedName().equals( 709 Deprecated.class.getName())) { 710 return true; 711 } 712 } 713 return false; 714 } 715 716 /** 717 * A convenience method to get property name from the name of the 718 * getter or setter method. 719 * @param name name of the getter or setter method. 720 * @return the name of the property of the given setter of getter. 721 */ 722 public String propertyNameFromMethodName(Configuration configuration, String name) { 723 String propertyName = null; 724 if (name.startsWith("get") || name.startsWith("set")) { 725 propertyName = name.substring(3); 726 } else if (name.startsWith("is")) { 727 propertyName = name.substring(2); 728 } 729 if ((propertyName == null) || propertyName.isEmpty()){ 730 return ""; 731 } 732 return propertyName.substring(0, 1).toLowerCase(configuration.getLocale()) 733 + propertyName.substring(1); 734 } 735 736 /** 737 * In case of JavaFX mode on, filters out classes that are private, 738 * package private or having the @treatAsPrivate annotation. Those are not 739 * documented in JavaFX mode. 740 * 741 * @param classes array of classes to be filtered. 742 * @param javafx set to true if in JavaFX mode. 743 * @return list of filtered classes. 744 */ 745 public ClassDoc[] filterOutPrivateClasses(final ClassDoc[] classes, 746 boolean javafx) { 747 if (!javafx) { 748 return classes; 749 } 750 final List<ClassDoc> filteredOutClasses = new ArrayList<>(classes.length); 751 for (ClassDoc classDoc : classes) { 752 if (classDoc.isPrivate() || classDoc.isPackagePrivate()) { 753 continue; 754 } 755 Tag[] aspTags = classDoc.tags("treatAsPrivate"); 756 if (aspTags != null && aspTags.length > 0) { 757 continue; 758 } 759 filteredOutClasses.add(classDoc); 760 } 761 762 return filteredOutClasses.toArray(new ClassDoc[0]); 763 } 764 765 /** 766 * Test whether the given FieldDoc is one of the declaration annotation ElementTypes 767 * defined in Java 5. 768 * Instead of testing for one of the new enum constants added in Java 8, test for 769 * the old constants. This prevents bootstrapping problems. 770 * 771 * @param elt The FieldDoc to test 772 * @return true, iff the given ElementType is one of the constants defined in Java 5 773 * @since 1.8 774 */ 775 public boolean isJava5DeclarationElementType(FieldDoc elt) { 776 return elt.name().contentEquals(ElementType.ANNOTATION_TYPE.name()) || 777 elt.name().contentEquals(ElementType.CONSTRUCTOR.name()) || 778 elt.name().contentEquals(ElementType.FIELD.name()) || 779 elt.name().contentEquals(ElementType.LOCAL_VARIABLE.name()) || 780 elt.name().contentEquals(ElementType.METHOD.name()) || 781 elt.name().contentEquals(ElementType.PACKAGE.name()) || 782 elt.name().contentEquals(ElementType.PARAMETER.name()) || 783 elt.name().contentEquals(ElementType.TYPE.name()); 784 } 785 786 /** 787 * A general purpose case insensitive String comparator, which compares two Strings using a Collator 788 * strength of "TERTIARY". 789 * 790 * @param s1 first String to compare. 791 * @param s2 second String to compare. 792 * @return a negative integer, zero, or a positive integer as the first 793 * argument is less than, equal to, or greater than the second. 794 */ 795 public static int compareStrings(String s1, String s2) { 796 return compareStrings(true, s1, s2); 797 } 798 /** 799 * A general purpose case sensitive String comparator, which compares two Strings using a Collator 800 * strength of "SECONDARY". 801 * 802 * @param s1 first String to compare. 803 * @param s2 second String to compare. 804 * @return a negative integer, zero, or a positive integer as the first 805 * argument is less than, equal to, or greater than the second. 806 */ 807 public static int compareCaseCompare(String s1, String s2) { 808 return compareStrings(false, s1, s2); 809 } 810 private static int compareStrings(boolean caseSensitive, String s1, String s2) { 811 Collator collator = Collator.getInstance(); 812 collator.setStrength(caseSensitive ? Collator.TERTIARY : Collator.SECONDARY); 813 return collator.compare(s1, s2); 814 } 815 816 /** 817 * A comparator for index file presentations, and are sorted as follows: 818 * 1. sort on simple names of entities 819 * 2. if equal, then compare the DocKind ex: Package, Interface etc. 820 * 3a. if equal and if the type is of ExecutableMemberDoc(Constructor, Methods), 821 * a case insensitive comparison of parameter the type signatures 822 * 3b. if equal, case sensitive comparison of the type signatures 823 * 4. finally, if equal, compare the FQNs of the entities 824 * @return a comparator for index file use 825 */ 826 public Comparator<Doc> makeComparatorForIndexUse() { 827 return new Utils.DocComparator<Doc>() { 828 /** 829 * Compare two given Doc entities, first sort on names, then on the kinds, 830 * then on the parameters only if the type is an instance of ExecutableMemberDocs, 831 * the parameters are compared and finally the fully qualified names. 832 * 833 * @param d1 - a Doc element. 834 * @param d2 - a Doc element. 835 * @return a negative integer, zero, or a positive integer as the first 836 * argument is less than, equal to, or greater than the second. 837 */ 838 public int compare(Doc d1, Doc d2) { 839 int result = compareNames(d1, d2); 840 if (result != 0) { 841 return result; 842 } 843 result = compareDocKinds(d1, d2); 844 if (result != 0) { 845 return result; 846 } 847 if (hasParameters(d1)) { 848 Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters(); 849 Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters(); 850 result = compareParameters(false, param1, param2); 851 if (result != 0) { 852 return result; 853 } 854 result = compareParameters(true, param1, param2); 855 if (result != 0) { 856 return result; 857 } 858 } 859 return compareFullyQualifiedNames(d1, d2); 860 } 861 }; 862 } 863 /** 864 * Comparator for ClassUse presentations, and sorted as follows, 865 * 1. compares simple names of entities 866 * 2. if equal, the fully qualified names of the entities 867 * 3. if equal and if applicable, the string representation of parameter types 868 * 3a. first by using case insensitive comparison 869 * 3b. second by using a case sensitive comparison 870 * 4. finally the Doc kinds ie. package, class, interface etc. 871 * @return a comparator to sort classes and members for class use 872 */ 873 public Comparator<Doc> makeComparatorForClassUse() { 874 return new Utils.DocComparator<Doc>() { 875 /** 876 * Compares two given Doc entities, first sort on name, and if 877 * applicable on the fully qualified name, and if applicable 878 * on the parameter types, and finally the DocKind. 879 * @param d1 - a Doc element. 880 * @param d2 - a Doc element. 881 * @return a negative integer, zero, or a positive integer as the first 882 * argument is less than, equal to, or greater than the second. 883 */ 884 public int compare(Doc d1, Doc d2) { 885 int result = compareNames(d1, d2); 886 if (result != 0) { 887 return result; 888 } 889 result = compareFullyQualifiedNames(d1, d2); 890 if (result != 0) { 891 return result; 892 } 893 if (hasParameters(d1) && hasParameters(d2)) { 894 Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters(); 895 Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters(); 896 result = compareParameters(false, param1, param2); 897 if (result != 0) { 898 return result; 899 } 900 return compareParameters(true, param1, param2); 901 } 902 return compareDocKinds(d1, d2); 903 } 904 }; 905 } 906 /** 907 * A general purpose comparator to sort Doc entities, basically provides the building blocks 908 * for creating specific comparators for an use-case. 909 * @param <T> a Doc entity 910 */ 911 static abstract class DocComparator<T extends Doc> implements Comparator<Doc> { 912 static enum DocKind { 913 PACKAGE, 914 CLASS, 915 ENUM, 916 INTERFACE, 917 ANNOTATION, 918 FIELD, 919 CONSTRUCTOR, 920 METHOD 921 }; 922 boolean hasParameters(Doc d) { 923 return d instanceof ExecutableMemberDoc; 924 } 925 DocKind getDocKind(Doc d) { 926 if (d.isAnnotationType() || d.isAnnotationTypeElement()) { 927 return DocKind.ANNOTATION; 928 } else if (d.isEnum() || d.isEnumConstant()) { 929 return DocKind.ENUM; 930 } else if (d.isField()) { 931 return DocKind.FIELD; 932 } else if (d.isInterface()) { 933 return DocKind.INTERFACE; 934 } else if (d.isClass()) { 935 return DocKind.CLASS; 936 } else if (d.isConstructor()) { 937 return DocKind.CONSTRUCTOR; 938 } else if (d.isMethod()) { 939 return DocKind.METHOD; 940 } else { 941 return DocKind.PACKAGE; 942 } 943 } 944 /** 945 * Compares two Doc entities' kinds, and these are ordered as defined in 946 * the DocKind enumeration. 947 * @param d1 the first Doc object 948 * @param d2 the second Doc object 949 * @return a negative integer, zero, or a positive integer as the first 950 * argument is less than, equal to, or greater than the second. 951 */ 952 protected int compareDocKinds(Doc d1, Doc d2) { 953 return getDocKind(d1).compareTo(getDocKind(d2)); 954 } 955 /** 956 * Compares arrays of parameters as a string representation of their types. 957 * 958 * @param ignoreCase specifies case sensitive or insensitive comparison. 959 * @param params1 the first parameter array. 960 * @param params2 the first parameter array. 961 * @return a negative integer, zero, or a positive integer as the first argument is less 962 * than, equal to, or greater than the second. 963 */ 964 protected int compareParameters(boolean caseSensitive, 965 Parameter[] params1, 966 Parameter[] params2) { 967 String s1 = getParametersAsString(params1); 968 String s2 = getParametersAsString(params2); 969 return compareStrings(caseSensitive, s1, s2); 970 } 971 /* 972 * This method returns a string representation solely for comparison purposes. 973 */ 974 protected String getParametersAsString(Parameter[] params) { 975 StringBuilder sb = new StringBuilder(); 976 for (Parameter param : params) { 977 Type t = param.type(); 978 // add parameter type to arrays, as TypeMirror does. 979 String tname = (t.asParameterizedType() != null && t.getElementType() != null) 980 ? t.getElementType() + t.dimension() 981 : t.toString(); 982 // prefix P for primitive and R for reference types, thus items will 983 // be ordered naturally. 984 sb.append(t.isPrimitive() ? "P" : "R").append("-").append(tname).append("-"); 985 } 986 return sb.toString(); 987 } 988 989 /** 990 * Compares two Doc entities typically the simple name of a method, 991 * field, constructor etc. 992 * @param d1 the first Doc. 993 * @param d2 the second Doc. 994 * @return a negative integer, zero, or a positive integer as the first 995 * argument is less than, equal to, or greater than the second. 996 */ 997 protected int compareNames(Doc d1, Doc d2) { 998 return compareStrings(d1.name(), d2.name()); 999 } 1000 1001 /** 1002 * Compares the fully qualified names of the entities 1003 * @param d1 the first entity 1004 * @param d2 the second entity 1005 * @return a negative integer, zero, or a positive integer as the first 1006 * argument is less than, equal to, or greater than the second. 1007 */ 1008 protected int compareFullyQualifiedNames(Doc d1, Doc d2) { 1009 String name1 = (d1 instanceof ProgramElementDoc) 1010 ? ((ProgramElementDoc)d1).qualifiedName() 1011 : d1.name(); 1012 String name2 = (d2 instanceof ProgramElementDoc) 1013 ? ((ProgramElementDoc)d2).qualifiedName() 1014 : d2.name(); 1015 return compareStrings(name1, name2); 1016 } 1017 } 1018} 1019