Utils.java revision 3827:44bdefe64114
1/* 2 * Copyright (c) 1999, 2016, 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 jdk.javadoc.internal.doclets.toolkit.util; 27 28import java.lang.annotation.Documented; 29import java.lang.ref.SoftReference; 30import java.text.CollationKey; 31import java.text.Collator; 32import java.util.*; 33import java.util.AbstractMap.SimpleEntry; 34import java.util.Map.Entry; 35import java.util.stream.Collectors; 36 37import javax.lang.model.SourceVersion; 38import javax.lang.model.element.AnnotationMirror; 39import javax.lang.model.element.AnnotationValue; 40import javax.lang.model.element.Element; 41import javax.lang.model.element.ElementKind; 42import javax.lang.model.element.ExecutableElement; 43import javax.lang.model.element.Modifier; 44import javax.lang.model.element.ModuleElement; 45import javax.lang.model.element.PackageElement; 46import javax.lang.model.element.TypeElement; 47import javax.lang.model.element.TypeParameterElement; 48import javax.lang.model.element.VariableElement; 49import javax.lang.model.type.ArrayType; 50import javax.lang.model.type.DeclaredType; 51import javax.lang.model.type.ErrorType; 52import javax.lang.model.type.ExecutableType; 53import javax.lang.model.type.NoType; 54import javax.lang.model.type.PrimitiveType; 55import javax.lang.model.type.TypeMirror; 56import javax.lang.model.util.ElementFilter; 57import javax.lang.model.util.ElementKindVisitor9; 58import javax.lang.model.util.Elements; 59import javax.lang.model.util.SimpleElementVisitor9; 60import javax.lang.model.util.SimpleTypeVisitor9; 61import javax.lang.model.util.TypeKindVisitor9; 62import javax.lang.model.util.Types; 63import javax.tools.FileObject; 64import javax.tools.JavaFileManager; 65import javax.tools.JavaFileManager.Location; 66import javax.tools.StandardLocation; 67 68import com.sun.source.doctree.DocCommentTree; 69import com.sun.source.doctree.DocTree; 70import com.sun.source.doctree.DocTree.Kind; 71import com.sun.source.doctree.ParamTree; 72import com.sun.source.doctree.SerialFieldTree; 73import com.sun.source.tree.CompilationUnitTree; 74import com.sun.source.tree.LineMap; 75import com.sun.source.util.DocSourcePositions; 76import com.sun.source.util.DocTrees; 77import com.sun.source.util.TreePath; 78import com.sun.tools.javac.model.JavacTypes; 79import jdk.javadoc.internal.doclets.toolkit.CommentUtils.DocCommentDuo; 80import jdk.javadoc.internal.doclets.toolkit.Configuration; 81import jdk.javadoc.internal.doclets.toolkit.Messages; 82import jdk.javadoc.internal.doclets.toolkit.WorkArounds; 83import jdk.javadoc.internal.tool.DocEnvImpl; 84 85import static javax.lang.model.element.ElementKind.*; 86import static javax.lang.model.element.Modifier.*; 87import static javax.lang.model.type.TypeKind.*; 88 89import static com.sun.source.doctree.DocTree.Kind.*; 90import static jdk.javadoc.internal.doclets.toolkit.builders.ConstantsSummaryBuilder.MAX_CONSTANT_VALUE_INDEX_LENGTH; 91 92 93/** 94 * Utilities Class for Doclets. 95 * 96 * <p><b>This is NOT part of any supported API. 97 * If you write code that depends on this, you do so at your own risk. 98 * This code and its internal interfaces are subject to change or 99 * deletion without notice.</b> 100 * 101 * @author Atul M Dambalkar 102 * @author Jamie Ho 103 */ 104public class Utils { 105 public final Configuration configuration; 106 public final Messages messages; 107 public final DocTrees docTrees; 108 public final Elements elementUtils; 109 public final Types typeUtils; 110 111 public Utils(Configuration c) { 112 configuration = c; 113 messages = configuration.getMessages(); 114 elementUtils = c.docEnv.getElementUtils(); 115 typeUtils = c.docEnv.getTypeUtils(); 116 docTrees = c.docEnv.getDocTrees(); 117 } 118 119 // our own little symbol table 120 private HashMap<String, TypeMirror> symtab = new HashMap<>(); 121 122 public TypeMirror getSymbol(String signature) { 123 TypeMirror type = symtab.get(signature); 124 if (type == null) { 125 TypeElement typeElement = elementUtils.getTypeElement(signature); 126 if (typeElement == null) 127 return null; 128 type = typeElement.asType(); 129 if (type == null) 130 return null; 131 symtab.put(signature, type); 132 } 133 return type; 134 } 135 136 public TypeMirror getObjectType() { 137 return getSymbol("java.lang.Object"); 138 } 139 140 public TypeMirror getExceptionType() { 141 return getSymbol("java.lang.Exception"); 142 } 143 144 public TypeMirror getErrorType() { 145 return getSymbol("java.lang.Error"); 146 } 147 148 public TypeMirror getSerializableType() { 149 return getSymbol("java.io.Serializable"); 150 } 151 152 public TypeMirror getExternalizableType() { 153 return getSymbol("java.io.Externalizable"); 154 } 155 156 public TypeMirror getIllegalArgumentExceptionType() { 157 return getSymbol("java.lang.IllegalArgumentException"); 158 } 159 160 public TypeMirror getNullPointerExceptionType() { 161 return getSymbol("java.lang.NullPointerException"); 162 } 163 164 public TypeMirror getDeprecatedType() { 165 return getSymbol("java.lang.Deprecated"); 166 } 167 168 public TypeMirror getFunctionalInterface() { 169 return getSymbol("java.lang.FunctionalInterface"); 170 } 171 172 /** 173 * Return array of class members whose documentation is to be generated. 174 * If the member is deprecated do not include such a member in the 175 * returned array. 176 * 177 * @param members Array of members to choose from. 178 * @return List List of eligible members for whom 179 * documentation is getting generated. 180 */ 181 public List<Element> excludeDeprecatedMembers(List<? extends Element> members) { 182 List<Element> excludeList = members.stream() 183 .filter((member) -> (!isDeprecated(member))) 184 .sorted(makeGeneralPurposeComparator()) 185 .collect(Collectors.<Element, List<Element>>toCollection(ArrayList::new)); 186 return excludeList; 187 } 188 189 /** 190 * Search for the given method in the given class. 191 * 192 * @param te Class to search into. 193 * @param method Method to be searched. 194 * @return ExecutableElement Method found, null otherwise. 195 */ 196 public ExecutableElement findMethod(TypeElement te, ExecutableElement method) { 197 for (Element m : getMethods(te)) { 198 if (executableMembersEqual(method, (ExecutableElement)m)) { 199 return (ExecutableElement)m; 200 } 201 } 202 return null; 203 } 204 205 /** 206 * Test whether a class is a subclass of another class. 207 * 208 * @param t1 the candidate superclass. 209 * @param t2 the target 210 * @return true if t1 is a superclass of t2. 211 */ 212 public boolean isSubclassOf(TypeElement t1, TypeElement t2) { 213 return typeUtils.isSubtype(t1.asType(), t2.asType()); 214 } 215 216 /** 217 * @param e1 the first method to compare. 218 * @param e2 the second method to compare. 219 * @return true if member1 overrides/hides or is overriden/hidden by member2. 220 */ 221 222 public boolean executableMembersEqual(ExecutableElement e1, ExecutableElement e2) { 223 // TODO: investigate if Elements.hides(..) will work here. 224 if (isStatic(e1) && isStatic(e2)) { 225 List<? extends VariableElement> parameters1 = e1.getParameters(); 226 List<? extends VariableElement> parameters2 = e2.getParameters(); 227 if (e1.getSimpleName().equals(e2.getSimpleName()) && 228 parameters1.size() == parameters2.size()) { 229 int j; 230 for (j = 0 ; j < parameters1.size(); j++) { 231 VariableElement v1 = parameters1.get(j); 232 VariableElement v2 = parameters2.get(j); 233 String t1 = getTypeName(v1.asType(), true); 234 String t2 = getTypeName(v2.asType(), true); 235 if (!(t1.equals(t2) || 236 isTypeVariable(v1.asType()) || isTypeVariable(v2.asType()))) { 237 break; 238 } 239 } 240 if (j == parameters1.size()) { 241 return true; 242 } 243 } 244 return false; 245 } else { 246 return elementUtils.overrides(e1, e2, getEnclosingTypeElement(e1)) || 247 elementUtils.overrides(e2, e1, getEnclosingTypeElement(e2)) || 248 e1.equals(e2); 249 } 250 } 251 252 /** 253 * According to 254 * <cite>The Java™ Language Specification</cite>, 255 * all the outer classes and static inner classes are core classes. 256 */ 257 public boolean isCoreClass(TypeElement e) { 258 return getEnclosingTypeElement(e) == null || isStatic(e); 259 } 260 261 /** 262 * Copy doc-files directory and its contents from the source 263 * package directory to the generated documentation directory. 264 * For example, given a package java.lang, this method will copy 265 * the doc-files directory, found in the package directory to the 266 * generated documentation hierarchy. 267 * 268 * @param pe the package containing the doc files to be copied 269 * @throws DocFileIOException if there is a problem while copying 270 * the documentation files 271 */ 272 public void copyDocFiles(PackageElement pe) throws DocFileIOException { 273 Location sourceLoc = getLocationForPackage(pe); 274 copyDirectory(sourceLoc, DocPath.forPackage(pe).resolve(DocPaths.DOC_FILES)); 275 } 276 277 /** 278 * Copy the given directory contents from the source package directory 279 * to the generated documentation directory. For example, given a package 280 * java.lang, this method will copy the entire directory, to the generated 281 * documentation hierarchy. 282 * 283 * @param pe the package containing the directory to be copied 284 * @param dir the directory to be copied 285 * @throws DocFileIOException if there is a problem while copying 286 * the documentation files 287 */ 288 public void copyDirectory(PackageElement pe, DocPath dir) throws DocFileIOException { 289 copyDirectory(getLocationForPackage(pe), dir); 290 } 291 292 /** 293 * Copy the given directory and its contents from the source 294 * module directory to the generated documentation directory. 295 * For example, given a package java.lang, this method will 296 * copy the entire directory, to the generated documentation 297 * hierarchy. 298 * 299 * @param mdle the module containing the directory to be copied 300 * @param dir the directory to be copied 301 * @throws DocFileIOException if there is a problem while copying 302 * the documentation files 303 */ 304 public void copyDirectory(ModuleElement mdle, DocPath dir) throws DocFileIOException { 305 copyDirectory(getLocationForModule(mdle), dir); 306 } 307 308 /** 309 * Copy files from a doc path location to the output. 310 * 311 * @param locn the location from which to read files 312 * @param dir the directory to be copied 313 * @throws DocFileIOException if there is a problem 314 * copying the files 315 */ 316 public void copyDirectory(Location locn, DocPath dir) throws DocFileIOException { 317 boolean first = true; 318 for (DocFile f : DocFile.list(configuration, locn, dir)) { 319 if (!f.isDirectory()) { 320 continue; 321 } 322 DocFile srcdir = f; 323 DocFile destdir = DocFile.createFileForOutput(configuration, dir); 324 if (srcdir.isSameFile(destdir)) { 325 continue; 326 } 327 328 for (DocFile srcfile: srcdir.list()) { 329 DocFile destfile = destdir.resolve(srcfile.getName()); 330 if (srcfile.isFile()) { 331 if (destfile.exists() && !first) { 332 messages.warning("doclet.Copy_Overwrite_warning", 333 srcfile.getPath(), destdir.getPath()); 334 } else { 335 messages.notice("doclet.Copying_File_0_To_Dir_1", 336 srcfile.getPath(), destdir.getPath()); 337 destfile.copyFile(srcfile); 338 } 339 } else if (srcfile.isDirectory()) { 340 if (configuration.copydocfilesubdirs 341 && !configuration.shouldExcludeDocFileDir(srcfile.getName())) { 342 copyDirectory(locn, dir.resolve(srcfile.getName())); 343 } 344 } 345 } 346 347 first = false; 348 } 349 } 350 351 protected Location getLocationForPackage(PackageElement pd) { 352 ModuleElement mdle = configuration.docEnv.getElementUtils().getModuleOf(pd); 353 354 if (mdle == null) 355 return defaultLocation(); 356 357 return getLocationForModule(mdle); 358 } 359 360 protected Location getLocationForModule(ModuleElement mdle) { 361 Location loc = configuration.workArounds.getLocationForModule(mdle); 362 if (loc != null) 363 return loc; 364 365 return defaultLocation(); 366 } 367 368 private Location defaultLocation() { 369 JavaFileManager fm = configuration.docEnv.getJavaFileManager(); 370 return fm.hasLocation(StandardLocation.SOURCE_PATH) 371 ? StandardLocation.SOURCE_PATH 372 : StandardLocation.CLASS_PATH; 373 } 374 375 public boolean isAnnotated(TypeMirror e) { 376 return !e.getAnnotationMirrors().isEmpty(); 377 } 378 379 public boolean isAnnotated(Element e) { 380 return !e.getAnnotationMirrors().isEmpty(); 381 } 382 383 public boolean isAnnotationType(Element e) { 384 return new SimpleElementVisitor9<Boolean, Void>() { 385 @Override 386 public Boolean visitExecutable(ExecutableElement e, Void p) { 387 return visit(e.getEnclosingElement()); 388 } 389 390 @Override 391 public Boolean visitUnknown(Element e, Void p) { 392 return false; 393 } 394 395 @Override 396 protected Boolean defaultAction(Element e, Void p) { 397 return e.getKind() == ANNOTATION_TYPE; 398 } 399 }.visit(e); 400 } 401 402 /** 403 * An Enum implementation is almost identical, thus this method returns if 404 * this element represents a CLASS or an ENUM 405 * @param e element 406 * @return true if class or enum 407 */ 408 public boolean isClass(Element e) { 409 return e.getKind().isClass(); 410 } 411 412 public boolean isConstructor(Element e) { 413 return e.getKind() == CONSTRUCTOR; 414 } 415 416 public boolean isEnum(Element e) { 417 return e.getKind() == ENUM; 418 } 419 420 boolean isEnumConstant(Element e) { 421 return e.getKind() == ENUM_CONSTANT; 422 } 423 424 public boolean isField(Element e) { 425 return e.getKind() == FIELD; 426 } 427 428 public boolean isInterface(Element e) { 429 return e.getKind() == INTERFACE; 430 } 431 432 public boolean isMethod(Element e) { 433 return e.getKind() == METHOD; 434 } 435 436 public boolean isModule(Element e) { 437 return e.getKind() == ElementKind.MODULE; 438 } 439 440 public boolean isPackage(Element e) { 441 return e.getKind() == ElementKind.PACKAGE; 442 } 443 444 public boolean isAbstract(Element e) { 445 return e.getModifiers().contains(Modifier.ABSTRACT); 446 } 447 448 public boolean isDefault(Element e) { 449 return e.getModifiers().contains(Modifier.DEFAULT); 450 } 451 452 public boolean isPackagePrivate(Element e) { 453 return !(isPublic(e) || isPrivate(e) || isProtected(e)); 454 } 455 456 public boolean isPrivate(Element e) { 457 return e.getModifiers().contains(Modifier.PRIVATE); 458 } 459 460 public boolean isProtected(Element e) { 461 return e.getModifiers().contains(Modifier.PROTECTED); 462 } 463 464 public boolean isPublic(Element e) { 465 return e.getModifiers().contains(Modifier.PUBLIC); 466 } 467 468 public boolean isProperty(String name) { 469 return configuration.javafx && name.endsWith("Property"); 470 } 471 472 public String getPropertyName(String name) { 473 return isProperty(name) 474 ? name.substring(0, name.length() - "Property".length()) 475 : name; 476 } 477 478 public String getPropertyLabel(String name) { 479 return name.substring(0, name.lastIndexOf("Property")); 480 } 481 482 public boolean isOverviewElement(Element e) { 483 return e.getKind() == ElementKind.OTHER; 484 } 485 486 public boolean isStatic(Element e) { 487 return e.getModifiers().contains(Modifier.STATIC); 488 } 489 490 public boolean isSerializable(TypeElement e) { 491 return typeUtils.isSubtype(e.asType(), getSerializableType()); 492 } 493 494 public boolean isExternalizable(TypeElement e) { 495 return typeUtils.isSubtype(e.asType(), getExternalizableType()); 496 } 497 498 public SortedSet<VariableElement> serializableFields(TypeElement aclass) { 499 return configuration.workArounds.getSerializableFields(this, aclass); 500 } 501 502 public SortedSet<ExecutableElement> serializationMethods(TypeElement aclass) { 503 return configuration.workArounds.getSerializationMethods(this, aclass); 504 } 505 506 public boolean definesSerializableFields(TypeElement aclass) { 507 return configuration.workArounds.definesSerializableFields(this, aclass); 508 } 509 510 public String modifiersToString(Element e, boolean trailingSpace) { 511 SortedSet<Modifier> set = new TreeSet<>(e.getModifiers()); 512 set.remove(Modifier.NATIVE); 513 set.remove(Modifier.STRICTFP); 514 set.remove(Modifier.SYNCHRONIZED); 515 516 return new ElementKindVisitor9<String, SortedSet<Modifier>>() { 517 final StringBuilder sb = new StringBuilder(); 518 519 void addVisibilityModifier(Set<Modifier> modifiers) { 520 if (modifiers.contains(PUBLIC)) { 521 sb.append("public").append(" "); 522 } else if (modifiers.contains(PROTECTED)) { 523 sb.append("protected").append(" "); 524 } else if (modifiers.contains(PRIVATE)) { 525 sb.append("private").append(" "); 526 } 527 } 528 529 void addStatic(Set<Modifier> modifiers) { 530 if (modifiers.contains(STATIC)) { 531 sb.append("static").append(" "); 532 } 533 } 534 535 void addModifers(Set<Modifier> modifiers) { 536 String s = set.stream().map(Modifier::toString).collect(Collectors.joining(" ")); 537 sb.append(s); 538 if (!s.isEmpty()) 539 sb.append(" "); 540 } 541 542 String finalString(String s) { 543 sb.append(s); 544 if (trailingSpace) { 545 if (sb.lastIndexOf(" ") == sb.length() - 1) { 546 return sb.toString(); 547 } else { 548 return sb.append(" ").toString(); 549 } 550 } else { 551 return sb.toString().trim(); 552 } 553 } 554 555 @Override 556 public String visitTypeAsInterface(TypeElement e, SortedSet<Modifier> p) { 557 addVisibilityModifier(p); 558 addStatic(p); 559 return finalString("interface"); 560 } 561 562 @Override 563 public String visitTypeAsEnum(TypeElement e, SortedSet<Modifier> p) { 564 addVisibilityModifier(p); 565 addStatic(p); 566 return finalString("enum"); 567 } 568 569 @Override 570 public String visitTypeAsAnnotationType(TypeElement e, SortedSet<Modifier> p) { 571 addVisibilityModifier(p); 572 addStatic(p); 573 return finalString("@interface"); 574 } 575 576 @Override 577 public String visitTypeAsClass(TypeElement e, SortedSet<Modifier> p) { 578 addModifers(p); 579 return finalString("class"); 580 } 581 582 @Override 583 protected String defaultAction(Element e, SortedSet<Modifier> p) { 584 addModifers(p); 585 return sb.toString().trim(); 586 } 587 588 }.visit(e, set); 589 } 590 591 public boolean isFunctionalInterface(AnnotationMirror amirror) { 592 return amirror.getAnnotationType().equals(getFunctionalInterface()) && 593 configuration.docEnv.getSourceVersion() 594 .compareTo(SourceVersion.RELEASE_8) >= 0; 595 } 596 597 public boolean isNoType(TypeMirror t) { 598 return t.getKind() == NONE; 599 } 600 601 public boolean isOrdinaryClass(TypeElement te) { 602 if (isEnum(te) || isInterface(te) || isAnnotationType(te)) { 603 return false; 604 } 605 if (isError(te) || isException(te)) { 606 return false; 607 } 608 return true; 609 } 610 611 public boolean isError(TypeElement te) { 612 if (isEnum(te) || isInterface(te) || isAnnotationType(te)) { 613 return false; 614 } 615 return typeUtils.isSubtype(te.asType(), getErrorType()); 616 } 617 618 public boolean isException(TypeElement te) { 619 if (isEnum(te) || isInterface(te) || isAnnotationType(te)) { 620 return false; 621 } 622 return typeUtils.isSubtype(te.asType(), getExceptionType()); 623 } 624 625 public boolean isPrimitive(TypeMirror t) { 626 return new SimpleTypeVisitor9<Boolean, Void>() { 627 628 @Override 629 public Boolean visitNoType(NoType t, Void p) { 630 return t.getKind() == VOID; 631 } 632 @Override 633 public Boolean visitPrimitive(PrimitiveType t, Void p) { 634 return true; 635 } 636 @Override 637 public Boolean visitArray(ArrayType t, Void p) { 638 return visit(t.getComponentType()); 639 } 640 @Override 641 protected Boolean defaultAction(TypeMirror e, Void p) { 642 return false; 643 } 644 }.visit(t); 645 } 646 647 public boolean isExecutableElement(Element e) { 648 ElementKind kind = e.getKind(); 649 switch (kind) { 650 case CONSTRUCTOR: case METHOD: case INSTANCE_INIT: 651 return true; 652 default: 653 return false; 654 } 655 } 656 657 public boolean isVariableElement(Element e) { 658 ElementKind kind = e.getKind(); 659 switch(kind) { 660 case ENUM_CONSTANT: case EXCEPTION_PARAMETER: case FIELD: 661 case LOCAL_VARIABLE: case PARAMETER: 662 case RESOURCE_VARIABLE: 663 return true; 664 default: 665 return false; 666 } 667 } 668 669 public boolean isTypeElement(Element e) { 670 switch (e.getKind()) { 671 case CLASS: case ENUM: case INTERFACE: case ANNOTATION_TYPE: 672 return true; 673 default: 674 return false; 675 } 676 } 677 678 /** 679 * Get the signature. It is the parameter list, type is qualified. 680 * For instance, for a method {@code mymethod(String x, int y)}, 681 * it will return {@code(java.lang.String,int)}. 682 * @param e 683 * @return String 684 */ 685 public String signature(ExecutableElement e) { 686 return makeSignature(e, true); 687 } 688 689 /** 690 * Get flat signature. All types are not qualified. 691 * Return a String, which is the flat signature of this member. 692 * It is the parameter list, type is not qualified. 693 * For instance, for a method {@code mymethod(String x, int y)}, 694 * it will return {@code (String, int)}. 695 */ 696 public String flatSignature(ExecutableElement e) { 697 return makeSignature(e, false); 698 } 699 700 public String makeSignature(ExecutableElement e, boolean full) { 701 return makeSignature(e, full, false); 702 } 703 704 public String makeSignature(ExecutableElement e, boolean full, boolean ignoreTypeParameters) { 705 StringBuilder result = new StringBuilder(); 706 result.append("("); 707 Iterator<? extends VariableElement> iterator = e.getParameters().iterator(); 708 while (iterator.hasNext()) { 709 VariableElement next = iterator.next(); 710 TypeMirror type = next.asType(); 711 result.append(getTypeSignature(type, full, ignoreTypeParameters)); 712 if (iterator.hasNext()) { 713 result.append(", "); 714 } 715 } 716 if (e.isVarArgs()) { 717 int len = result.length(); 718 result.replace(len - 2, len, "..."); 719 } 720 result.append(")"); 721 return result.toString(); 722 } 723 724 private String getTypeSignature(TypeMirror t, boolean qualifiedName, boolean noTypeParameters) { 725 return new SimpleTypeVisitor9<StringBuilder, Void>() { 726 final StringBuilder sb = new StringBuilder(); 727 728 @Override 729 public StringBuilder visitArray(ArrayType t, Void p) { 730 TypeMirror componentType = t.getComponentType(); 731 visit(componentType); 732 sb.append("[]"); 733 return sb; 734 } 735 736 @Override 737 public StringBuilder visitDeclared(DeclaredType t, Void p) { 738 Element e = t.asElement(); 739 sb.append(qualifiedName ? getFullyQualifiedName(e) : getSimpleName(e)); 740 List<? extends TypeMirror> typeArguments = t.getTypeArguments(); 741 if (typeArguments.isEmpty() || noTypeParameters) { 742 return sb; 743 } 744 sb.append("<"); 745 Iterator<? extends TypeMirror> iterator = typeArguments.iterator(); 746 while (iterator.hasNext()) { 747 TypeMirror ta = iterator.next(); 748 visit(ta); 749 if (iterator.hasNext()) { 750 sb.append(", "); 751 } 752 } 753 sb.append(">"); 754 return sb; 755 } 756 757 @Override 758 public StringBuilder visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 759 Element e = t.asElement(); 760 sb.append(qualifiedName ? getFullyQualifiedName(e, false) : getSimpleName(e)); 761 return sb; 762 } 763 764 @Override 765 public StringBuilder visitWildcard(javax.lang.model.type.WildcardType t, Void p) { 766 sb.append("?"); 767 TypeMirror upperBound = t.getExtendsBound(); 768 if (upperBound != null) { 769 sb.append(" extends "); 770 visit(upperBound); 771 } 772 TypeMirror superBound = t.getSuperBound(); 773 if (superBound != null) { 774 sb.append(" super "); 775 visit(superBound); 776 } 777 return sb; 778 } 779 780 @Override 781 protected StringBuilder defaultAction(TypeMirror e, Void p) { 782 return sb.append(e); 783 } 784 }.visit(t).toString(); 785 } 786 787 public boolean isArrayType(TypeMirror t) { 788 return t.getKind() == ARRAY; 789 } 790 791 public boolean isDeclaredType(TypeMirror t) { 792 return t.getKind() == DECLARED; 793 } 794 795 public boolean isErrorType(TypeMirror t) { 796 return t.getKind() == ERROR; 797 } 798 799 public boolean isIntersectionType(TypeMirror t) { 800 return t.getKind() == INTERSECTION; 801 } 802 803 public boolean isTypeParameterElement(Element e) { 804 return e.getKind() == TYPE_PARAMETER; 805 } 806 807 public boolean isTypeVariable(TypeMirror t) { 808 return t.getKind() == TYPEVAR; 809 } 810 811 public boolean isVoid(TypeMirror t) { 812 return t.getKind() == VOID; 813 } 814 815 public boolean isWildCard(TypeMirror t) { 816 return t.getKind() == WILDCARD; 817 } 818 819 public boolean ignoreBounds(TypeMirror bound) { 820 return bound.equals(getObjectType()) && !isAnnotated(bound); 821 } 822 823 /* 824 * a direct port of TypeVariable.getBounds 825 */ 826 public List<? extends TypeMirror> getBounds(TypeParameterElement tpe) { 827 List<? extends TypeMirror> bounds = tpe.getBounds(); 828 if (!bounds.isEmpty()) { 829 TypeMirror upperBound = bounds.get(bounds.size() - 1); 830 if (ignoreBounds(upperBound)) { 831 return Collections.emptyList(); 832 } 833 } 834 return bounds; 835 } 836 837 /** 838 * Returns the TypeMirror of the ExecutableElement for all methods, 839 * a null if constructor. 840 * @param ee the ExecutableElement 841 * @return 842 */ 843 public TypeMirror getReturnType(ExecutableElement ee) { 844 return ee.getKind() == CONSTRUCTOR ? null : ee.getReturnType(); 845 } 846 847 /** 848 * Return the type containing the method that this method overrides. 849 * It may be a {@code TypeElement} or a {@code TypeParameterElement}. 850 */ 851 public TypeMirror overriddenType(ExecutableElement method) { 852 return configuration.workArounds.overriddenType(method); 853 } 854 855 private TypeMirror getType(TypeMirror t) { 856 return (isNoType(t)) ? getObjectType() : t; 857 } 858 859 public TypeMirror getSuperType(TypeElement te) { 860 TypeMirror t = te.getSuperclass(); 861 return getType(t); 862 } 863 864 /** 865 * Return the class that originally defined the method that 866 * is overridden by the current definition, or null if no 867 * such class exists. 868 * 869 * @return a TypeElement representing the superclass that 870 * originally defined this method, null if this method does 871 * not override a definition in a superclass. 872 */ 873 public TypeElement overriddenClass(ExecutableElement ee) { 874 TypeMirror type = overriddenType(ee); 875 return (type != null) ? asTypeElement(type) : null; 876 } 877 878 public ExecutableElement overriddenMethod(ExecutableElement method) { 879 if (isStatic(method)) { 880 return null; 881 } 882 final TypeElement origin = getEnclosingTypeElement(method); 883 for (TypeMirror t = getSuperType(origin); 884 t.getKind() == DECLARED; 885 t = getSuperType(asTypeElement(t))) { 886 TypeElement te = asTypeElement(t); 887 if (te == null) { 888 return null; 889 } 890 List<? extends Element> methods = te.getEnclosedElements(); 891 for (ExecutableElement ee : ElementFilter.methodsIn(methods)) { 892 if (elementUtils.overrides(method, ee, origin)) { 893 return ee; 894 } 895 } 896 if (t.equals(getObjectType())) 897 return null; 898 } 899 return null; 900 } 901 902 public SortedSet<TypeElement> getTypeElementsAsSortedSet(Iterable<TypeElement> typeElements) { 903 SortedSet<TypeElement> set = new TreeSet<>(makeGeneralPurposeComparator()); 904 for (TypeElement te : typeElements) { 905 set.add(te); 906 } 907 return set; 908 } 909 910 public List<? extends DocTree> getSerialDataTrees(ExecutableElement member) { 911 return getBlockTags(member, SERIAL_DATA); 912 } 913 914 public FileObject getFileObject(TypeElement te) { 915 return docTrees.getPath(te).getCompilationUnit().getSourceFile(); 916 } 917 918 public TypeMirror getDeclaredType(TypeElement enclosing, TypeMirror target) { 919 return getDeclaredType(Collections.emptyList(), enclosing, target); 920 } 921 922 /** 923 * Finds the declaration of the enclosing's type parameter. 924 * 925 * @param values 926 * @param enclosing a TypeElement whose type arguments we desire 927 * @param target the TypeMirror of the type as described by the enclosing 928 * @return 929 */ 930 public TypeMirror getDeclaredType(Collection<TypeMirror> values, 931 TypeElement enclosing, TypeMirror target) { 932 TypeElement targetElement = asTypeElement(target); 933 List<? extends TypeParameterElement> targetTypeArgs = targetElement.getTypeParameters(); 934 if (targetTypeArgs.isEmpty()) { 935 return target; 936 } 937 938 List<? extends TypeParameterElement> enclosingTypeArgs = enclosing.getTypeParameters(); 939 List<TypeMirror> targetTypeArgTypes = new ArrayList<>(targetTypeArgs.size()); 940 941 if (enclosingTypeArgs.isEmpty()) { 942 for (TypeMirror te : values) { 943 List<? extends TypeMirror> typeArguments = ((DeclaredType)te).getTypeArguments(); 944 if (typeArguments.size() >= targetTypeArgs.size()) { 945 for (int i = 0 ; i < targetTypeArgs.size(); i++) { 946 targetTypeArgTypes.add(typeArguments.get(i)); 947 } 948 break; 949 } 950 } 951 // we found no matches in the hierarchy 952 if (targetTypeArgTypes.isEmpty()) { 953 return target; 954 } 955 } else { 956 if (targetTypeArgs.size() > enclosingTypeArgs.size()) { 957 return target; 958 } 959 for (int i = 0; i < targetTypeArgs.size(); i++) { 960 TypeParameterElement tpe = enclosingTypeArgs.get(i); 961 targetTypeArgTypes.add(tpe.asType()); 962 } 963 } 964 TypeMirror dt = typeUtils.getDeclaredType(targetElement, 965 targetTypeArgTypes.toArray(new TypeMirror[targetTypeArgTypes.size()])); 966 return dt; 967 } 968 969 /** 970 * For the class return all implemented interfaces including the 971 * superinterfaces of the implementing interfaces, also iterate over for 972 * all the superclasses. For interface return all the extended interfaces 973 * as well as superinterfaces for those extended interfaces. 974 * 975 * @param te the class to get the interfaces for 976 * @return List of all the required interfaces. 977 */ 978 public Set<TypeMirror> getAllInterfaces(TypeElement te) { 979 Set<TypeMirror> results = new LinkedHashSet<>(); 980 981 List<? extends TypeMirror> interfaceTypes = te.getInterfaces(); 982 983 for (TypeMirror interfaceType : interfaceTypes) { 984 TypeElement intfc = asTypeElement(interfaceType); 985 986 if (isPublic(intfc) || isLinkable(intfc)) { 987 results.add(interfaceType); 988 TypeElement klass = asTypeElement(interfaceType); 989 for (TypeMirror t : getAllInterfaces(klass)) { 990 t = getDeclaredType(results, te, t); 991 results.add(t); 992 } 993 } 994 } 995 // TypeMirror contains the modified TypeParameterElement's types represented 996 // in the local Class'es elements types. ex: Foo<E> implements Bar<V> and the 997 // class being considered is Foo then TypeParameters will be represented as <E> 998 // note that any conversion might revert back to the old signature. For this 999 // very reason we get the superType, and find its interfaces. 1000 TypeMirror superType = getSuperType(te); 1001 if (superType == getObjectType()) 1002 return results; 1003 // Try walking the tree 1004 addAllInterfaceTypes(results, te, superType, 1005 configuration.workArounds.interfaceTypesOf(superType)); 1006 return results; 1007 } 1008 1009 private void findAllInterfaceTypes(Set<TypeMirror> results, final TypeElement baseClass, 1010 TypeMirror p) { 1011 TypeMirror superType = getSuperType(asTypeElement(p)); 1012 if (superType == p) { 1013 return; 1014 } 1015 addAllInterfaceTypes(results, baseClass, superType, 1016 configuration.workArounds.interfaceTypesOf(superType)); 1017 } 1018 1019 private void addAllInterfaceTypes(Set<TypeMirror> results, 1020 final TypeElement baseClass, TypeMirror type, 1021 List<TypeMirror> interfaceTypes) { 1022 for (TypeMirror interfaceType : interfaceTypes) { 1023 TypeElement iElement = asTypeElement(interfaceType); 1024 if (isPublic(iElement) && isLinkable(iElement)) { 1025 interfaceType = getDeclaredType(results, baseClass, interfaceType); 1026 results.add(interfaceType); 1027 Set<TypeMirror> superInterfaces = getAllInterfaces(iElement); 1028 for (TypeMirror superInterface : superInterfaces) { 1029 superInterface = getDeclaredType(results, baseClass, superInterface); 1030 results.add(superInterface); 1031 } 1032 } 1033 } 1034 findAllInterfaceTypes(results, baseClass, type); 1035 } 1036 1037 /** 1038 * Lookup for a class within this package. 1039 * 1040 * @return TypeElement of found class, or null if not found. 1041 */ 1042 public TypeElement findClassInPackageElement(PackageElement pkg, String className) { 1043 for (TypeElement c : getAllClasses(pkg)) { 1044 if (getSimpleName(c).equals(className)) { 1045 return c; 1046 } 1047 } 1048 return null; 1049 } 1050 1051 /** 1052 * TODO: FIXME: port to javax.lang.model 1053 * Find a class within the context of this class. Search order: qualified name, in this class 1054 * (inner), in this package, in the class imports, in the package imports. Return the 1055 * TypeElement if found, null if not found. 1056 */ 1057 //### The specified search order is not the normal rule the 1058 //### compiler would use. Leave as specified or change it? 1059 public TypeElement findClass(Element element, String className) { 1060 TypeElement encl = getEnclosingTypeElement(element); 1061 TypeElement searchResult = configuration.workArounds.searchClass(encl, className); 1062 if (searchResult == null) { 1063 encl = getEnclosingTypeElement(encl); 1064 //Expand search space to include enclosing class. 1065 while (encl != null && getEnclosingTypeElement(encl) != null) { 1066 encl = getEnclosingTypeElement(encl); 1067 } 1068 searchResult = encl == null 1069 ? null 1070 : configuration.workArounds.searchClass(encl, className); 1071 } 1072 return searchResult; 1073 } 1074 1075 /** 1076 * Enclose in quotes, used for paths and filenames that contains spaces 1077 */ 1078 public String quote(String filepath) { 1079 return ("\"" + filepath + "\""); 1080 } 1081 1082 /** 1083 * Parse the package name. We only want to display package name up to 1084 * 2 levels. 1085 */ 1086 public String parsePackageName(PackageElement p) { 1087 String pkgname = p.isUnnamed() ? "" : getPackageName(p); 1088 int index = -1; 1089 for (int j = 0; j < MAX_CONSTANT_VALUE_INDEX_LENGTH; j++) { 1090 index = pkgname.indexOf(".", index + 1); 1091 } 1092 if (index != -1) { 1093 pkgname = pkgname.substring(0, index); 1094 } 1095 return pkgname; 1096 } 1097 1098 /** 1099 * Given a string, replace all occurrences of 'newStr' with 'oldStr'. 1100 * @param originalStr the string to modify. 1101 * @param oldStr the string to replace. 1102 * @param newStr the string to insert in place of the old string. 1103 */ 1104 public String replaceText(String originalStr, String oldStr, 1105 String newStr) { 1106 if (oldStr == null || newStr == null || oldStr.equals(newStr)) { 1107 return originalStr; 1108 } 1109 return originalStr.replace(oldStr, newStr); 1110 } 1111 1112 /** 1113 * Given an annotation, return true if it should be documented and false 1114 * otherwise. 1115 * 1116 * @param annotation the annotation to check. 1117 * 1118 * @return true return true if it should be documented and false otherwise. 1119 */ 1120 public boolean isDocumentedAnnotation(TypeElement annotation) { 1121 for (AnnotationMirror anno : annotation.getAnnotationMirrors()) { 1122 if (getFullyQualifiedName(anno.getAnnotationType().asElement()).equals( 1123 Documented.class.getName())) { 1124 return true; 1125 } 1126 } 1127 return false; 1128 } 1129 1130 /** 1131 * Return true if this class is linkable and false if we can't link to the 1132 * desired class. 1133 * <br> 1134 * <b>NOTE:</b> You can only link to external classes if they are public or 1135 * protected. 1136 * 1137 * @return true if this class is linkable and false if we can't link to the 1138 * desired class. 1139 */ 1140 public boolean isLinkable(TypeElement typeElem) { 1141 return 1142 (typeElem != null && 1143 (isIncluded(typeElem) && configuration.isGeneratedDoc(typeElem))) || 1144 (configuration.extern.isExternal(typeElem) && 1145 (isPublic(typeElem) || isProtected(typeElem))); 1146 } 1147 1148 List<TypeMirror> asErasureTypes(Collection<TypeElement> inList) { 1149 List<TypeMirror> out = new ArrayList<>(inList.size()); 1150 inList.stream().forEach((te) -> { 1151 out.add(typeUtils.erasure(te.asType())); 1152 }); 1153 return out; 1154 } 1155 1156 List<TypeMirror> asTypes(Collection<TypeElement> inList) { 1157 List<TypeMirror> out = new ArrayList<>(inList.size()); 1158 inList.stream().forEach((te) -> { 1159 out.add(te.asType()); 1160 }); 1161 return out; 1162 } 1163 1164 /** 1165 * Return this type as a {@code TypeElement} if it represents a class 1166 * interface or annotation. Array dimensions are ignored. 1167 * If this type {@code ParameterizedType} or {@code WildcardType}, return 1168 * the {@code TypeElement} of the type's erasure. If this is an 1169 * annotation, return this as a {@code TypeElement}. 1170 * If this is a primitive type, return null. 1171 * 1172 * @return the {@code TypeElement} of this type, 1173 * or null if it is a primitive type. 1174 */ 1175 public TypeElement asTypeElement(TypeMirror t) { 1176 return new SimpleTypeVisitor9<TypeElement, Void>() { 1177 1178 @Override 1179 public TypeElement visitDeclared(DeclaredType t, Void p) { 1180 return (TypeElement) t.asElement(); 1181 } 1182 1183 @Override 1184 public TypeElement visitArray(ArrayType t, Void p) { 1185 return visit(t.getComponentType()); 1186 } 1187 1188 @Override 1189 public TypeElement visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 1190 /* 1191 * TODO: Check with JJG. 1192 * if we have an annotated type @A $B T, then erasure returns a 1193 * none, in this case we use asElement instead. 1194 */ 1195 if (isAnnotated(t)) { 1196 return visit(typeUtils.asElement(t).asType()); 1197 } 1198 return visit(typeUtils.erasure(t)); 1199 } 1200 1201 @Override 1202 public TypeElement visitWildcard(javax.lang.model.type.WildcardType t, Void p) { 1203 return visit(typeUtils.erasure(t)); 1204 } 1205 1206 @Override 1207 public TypeElement visitError(ErrorType t, Void p) { 1208 return (TypeElement)t.asElement(); 1209 } 1210 1211 @Override 1212 protected TypeElement defaultAction(TypeMirror e, Void p) { 1213 return super.defaultAction(e, p); 1214 } 1215 }.visit(t); 1216 } 1217 1218 public TypeMirror getComponentType(TypeMirror t) { 1219 while (isArrayType(t)) { 1220 t = ((ArrayType) t).getComponentType(); 1221 } 1222 return t; 1223 } 1224 1225 /** 1226 * Return the type's dimension information, as a string. 1227 * <p> 1228 * For example, a two dimensional array of String returns "{@code [][]}". 1229 * 1230 * @return the type's dimension information as a string. 1231 */ 1232 public String getDimension(TypeMirror t) { 1233 return new SimpleTypeVisitor9<String, Void>() { 1234 StringBuilder dimension = new StringBuilder(""); 1235 @Override 1236 public String visitArray(ArrayType t, Void p) { 1237 dimension.append("[]"); 1238 return visit(t.getComponentType()); 1239 } 1240 1241 @Override 1242 protected String defaultAction(TypeMirror e, Void p) { 1243 return dimension.toString(); 1244 } 1245 1246 }.visit(t); 1247 } 1248 1249 public TypeElement getSuperClass(TypeElement te) { 1250 if (isInterface(te) || isAnnotationType(te) || 1251 te.asType().equals(getObjectType())) { 1252 return null; 1253 } 1254 TypeMirror superclass = te.getSuperclass(); 1255 if (isNoType(superclass) && isClass(te)) { 1256 superclass = getObjectType(); 1257 } 1258 return asTypeElement(superclass); 1259 } 1260 1261 public TypeElement getFirstVisibleSuperClassAsTypeElement(TypeElement te) { 1262 if (isAnnotationType(te) || isInterface(te) || 1263 te.asType().equals(getObjectType())) { 1264 return null; 1265 } 1266 TypeMirror firstVisibleSuperClass = getFirstVisibleSuperClass(te); 1267 return firstVisibleSuperClass == null ? null : asTypeElement(firstVisibleSuperClass); 1268 } 1269 1270 /** 1271 * Given a class, return the closest visible super class. 1272 * @param type the TypeMirror to be interrogated 1273 * @return the closest visible super class. Return null if it cannot 1274 * be found. 1275 */ 1276 1277 public TypeMirror getFirstVisibleSuperClass(TypeMirror type) { 1278 return getFirstVisibleSuperClass(asTypeElement(type)); 1279 } 1280 1281 1282 /** 1283 * Given a class, return the closest visible super class. 1284 * 1285 * @param te the TypeElement to be interrogated 1286 * @return the closest visible super class. Return null if it cannot 1287 * be found.. 1288 */ 1289 public TypeMirror getFirstVisibleSuperClass(TypeElement te) { 1290 TypeMirror superType = te.getSuperclass(); 1291 if (isNoType(superType)) { 1292 superType = getObjectType(); 1293 } 1294 TypeElement superClass = asTypeElement(superType); 1295 // skip "hidden" classes 1296 while ((superClass != null && isHidden(superClass)) 1297 || (superClass != null && !isPublic(superClass) && !isLinkable(superClass))) { 1298 TypeMirror supersuperType = superClass.getSuperclass(); 1299 TypeElement supersuperClass = asTypeElement(supersuperType); 1300 if (supersuperClass == null 1301 || supersuperClass.getQualifiedName().equals(superClass.getQualifiedName())) { 1302 break; 1303 } 1304 superType = supersuperType; 1305 superClass = supersuperClass; 1306 } 1307 if (te.asType().equals(superType)) { 1308 return null; 1309 } 1310 return superType; 1311 } 1312 1313 /** 1314 * Given a TypeElement, return the name of its type (Class, Interface, etc.). 1315 * 1316 * @param te the TypeElement to check. 1317 * @param lowerCaseOnly true if you want the name returned in lower case. 1318 * If false, the first letter of the name is capitalized. 1319 * @return 1320 */ 1321 1322 public String getTypeElementName(TypeElement te, boolean lowerCaseOnly) { 1323 String typeName = ""; 1324 if (isInterface(te)) { 1325 typeName = "doclet.Interface"; 1326 } else if (isException(te)) { 1327 typeName = "doclet.Exception"; 1328 } else if (isError(te)) { 1329 typeName = "doclet.Error"; 1330 } else if (isAnnotationType(te)) { 1331 typeName = "doclet.AnnotationType"; 1332 } else if (isEnum(te)) { 1333 typeName = "doclet.Enum"; 1334 } else if (isOrdinaryClass(te)) { 1335 typeName = "doclet.Class"; 1336 } 1337 typeName = lowerCaseOnly ? toLowerCase(typeName) : typeName; 1338 return typeNameMap.computeIfAbsent(typeName, configuration :: getText); 1339 } 1340 1341 private final Map<String, String> typeNameMap = new HashMap<>(); 1342 1343 public String getTypeName(TypeMirror t, boolean fullyQualified) { 1344 return new SimpleTypeVisitor9<String, Void>() { 1345 1346 @Override 1347 public String visitArray(ArrayType t, Void p) { 1348 return visit(t.getComponentType()); 1349 } 1350 1351 @Override 1352 public String visitDeclared(DeclaredType t, Void p) { 1353 TypeElement te = asTypeElement(t); 1354 return fullyQualified 1355 ? te.getQualifiedName().toString() 1356 : getSimpleName(te); 1357 } 1358 1359 @Override 1360 public String visitExecutable(ExecutableType t, Void p) { 1361 return t.toString(); 1362 } 1363 1364 @Override 1365 public String visitPrimitive(PrimitiveType t, Void p) { 1366 return t.toString(); 1367 } 1368 1369 @Override 1370 public String visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 1371 return getSimpleName(t.asElement()); 1372 } 1373 1374 @Override 1375 public String visitWildcard(javax.lang.model.type.WildcardType t, Void p) { 1376 return t.toString(); 1377 } 1378 1379 @Override 1380 protected String defaultAction(TypeMirror e, Void p) { 1381 return e.toString(); 1382 } 1383 }.visit(t); 1384 } 1385 1386 /** 1387 * Replace all tabs in a string with the appropriate number of spaces. 1388 * The string may be a multi-line string. 1389 * @param text the text for which the tabs should be expanded 1390 * @return the text with all tabs expanded 1391 */ 1392 public String replaceTabs(String text) { 1393 if (!text.contains("\t")) 1394 return text; 1395 1396 final int tabLength = configuration.sourcetab; 1397 final String whitespace = configuration.tabSpaces; 1398 final int textLength = text.length(); 1399 StringBuilder result = new StringBuilder(textLength); 1400 int pos = 0; 1401 int lineLength = 0; 1402 for (int i = 0; i < textLength; i++) { 1403 char ch = text.charAt(i); 1404 switch (ch) { 1405 case '\n': case '\r': 1406 lineLength = 0; 1407 break; 1408 case '\t': 1409 result.append(text, pos, i); 1410 int spaceCount = tabLength - lineLength % tabLength; 1411 result.append(whitespace, 0, spaceCount); 1412 lineLength += spaceCount; 1413 pos = i + 1; 1414 break; 1415 default: 1416 lineLength++; 1417 } 1418 } 1419 result.append(text, pos, textLength); 1420 return result.toString(); 1421 } 1422 1423 public CharSequence normalizeNewlines(CharSequence text) { 1424 StringBuilder sb = new StringBuilder(); 1425 final int textLength = text.length(); 1426 final String NL = DocletConstants.NL; 1427 int pos = 0; 1428 for (int i = 0; i < textLength; i++) { 1429 char ch = text.charAt(i); 1430 switch (ch) { 1431 case '\n': 1432 sb.append(text, pos, i); 1433 sb.append(NL); 1434 pos = i + 1; 1435 break; 1436 case '\r': 1437 sb.append(text, pos, i); 1438 sb.append(NL); 1439 if (i + 1 < textLength && text.charAt(i + 1) == '\n') 1440 i++; 1441 pos = i + 1; 1442 break; 1443 } 1444 } 1445 sb.append(text, pos, textLength); 1446 return sb; 1447 } 1448 1449 /** 1450 * The documentation for values() and valueOf() in Enums are set by the 1451 * doclet only iff the user or overridden methods are missing. 1452 * @param elem 1453 */ 1454 public void setEnumDocumentation(TypeElement elem) { 1455 for (Element e : getMethods(elem)) { 1456 ExecutableElement ee = (ExecutableElement)e; 1457 if (!getFullBody(e).isEmpty()) // ignore if already set 1458 continue; 1459 if (ee.getSimpleName().contentEquals("values") && ee.getParameters().isEmpty()) { 1460 configuration.cmtUtils.setEnumValuesTree(configuration, e); 1461 } 1462 if (ee.getSimpleName().contentEquals("valueOf") && ee.getParameters().size() == 1) { 1463 configuration.cmtUtils.setEnumValueOfTree(configuration, e); 1464 } 1465 } 1466 } 1467 1468 /** 1469 * Returns a locale independent lower cased String. That is, it 1470 * always uses US locale, this is a clone of the one in StringUtils. 1471 * @param s to convert 1472 * @return converted String 1473 */ 1474 public static String toLowerCase(String s) { 1475 return s.toLowerCase(Locale.US); 1476 } 1477 1478 /** 1479 * Return true if the given Element is deprecated. 1480 * 1481 * @param e the Element to check. 1482 * @return true if the given Element is deprecated. 1483 */ 1484 public boolean isDeprecated(Element e) { 1485 if (isPackage(e)) { 1486 return configuration.workArounds.isDeprecated0(e); 1487 } 1488 return elementUtils.isDeprecated(e); 1489 } 1490 1491 /** 1492 * Return true if the given Element is deprecated for removal. 1493 * 1494 * @param e the Element to check. 1495 * @return true if the given Element is deprecated for removal. 1496 */ 1497 public boolean isDeprecatedForRemoval(Element e) { 1498 List<? extends AnnotationMirror> annotationList = e.getAnnotationMirrors(); 1499 JavacTypes jctypes = ((DocEnvImpl) configuration.docEnv).toolEnv.typeutils; 1500 for (AnnotationMirror anno : annotationList) { 1501 if (jctypes.isSameType(anno.getAnnotationType().asElement().asType(), getDeprecatedType())) { 1502 Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = anno.getElementValues(); 1503 if (!pairs.isEmpty()) { 1504 for (ExecutableElement element : pairs.keySet()) { 1505 if (element.getSimpleName().contentEquals("forRemoval")) { 1506 return Boolean.parseBoolean((pairs.get(element)).toString()); 1507 } 1508 } 1509 } 1510 } 1511 } 1512 return false; 1513 } 1514 1515 /** 1516 * A convenience method to get property name from the name of the 1517 * getter or setter method. 1518 * @param e the input method. 1519 * @return the name of the property of the given setter of getter. 1520 */ 1521 public String propertyName(ExecutableElement e) { 1522 String name = getSimpleName(e); 1523 String propertyName = null; 1524 if (name.startsWith("get") || name.startsWith("set")) { 1525 propertyName = name.substring(3); 1526 } else if (name.startsWith("is")) { 1527 propertyName = name.substring(2); 1528 } 1529 if ((propertyName == null) || propertyName.isEmpty()){ 1530 return ""; 1531 } 1532 return propertyName.substring(0, 1).toLowerCase(configuration.getLocale()) 1533 + propertyName.substring(1); 1534 } 1535 1536 /** 1537 * Returns true if the element is included, contains @hidden tag, 1538 * or if javafx flag is present and element contains @treatAsPrivate 1539 * tag. 1540 * @param e the queried element 1541 * @return true if it exists, false otherwise 1542 */ 1543 public boolean isHidden(Element e) { 1544 // prevent needless tests on elements which are not included 1545 if (!isIncluded(e)) { 1546 return false; 1547 } 1548 if (configuration.javafx && 1549 hasBlockTag(e, DocTree.Kind.UNKNOWN_BLOCK_TAG, "treatAsPrivate")) { 1550 return true; 1551 } 1552 return hasBlockTag(e, DocTree.Kind.HIDDEN); 1553 } 1554 1555 /** 1556 * In case of JavaFX mode on, filters out classes that are private, 1557 * package private, these are not documented in JavaFX mode, also 1558 * remove those classes that have @hidden or @treatAsPrivate comment tag. 1559 * 1560 * @param classlist a collection of TypeElements 1561 * @param javafx set to true if in JavaFX mode. 1562 * @return list of filtered classes. 1563 */ 1564 public SortedSet<TypeElement> filterOutPrivateClasses(Iterable<TypeElement> classlist, 1565 boolean javafx) { 1566 SortedSet<TypeElement> filteredOutClasses = 1567 new TreeSet<>(makeGeneralPurposeComparator()); 1568 if (!javafx) { 1569 for (Element te : classlist) { 1570 if (!isHidden(te)) { 1571 filteredOutClasses.add((TypeElement)te); 1572 } 1573 } 1574 return filteredOutClasses; 1575 } 1576 for (Element e : classlist) { 1577 if (isPrivate(e) || isPackagePrivate(e) || isHidden(e)) { 1578 continue; 1579 } 1580 filteredOutClasses.add((TypeElement)e); 1581 } 1582 return filteredOutClasses; 1583 } 1584 1585 /** 1586 * Compares two elements. 1587 * @param e1 first Element 1588 * @param e2 second Element 1589 * @return a true if they are the same, false otherwise. 1590 */ 1591 public boolean elementsEqual(Element e1, Element e2) { 1592 if (e1.getKind() != e2.getKind()) { 1593 return false; 1594 } 1595 String s1 = getSimpleName(e1); 1596 String s2 = getSimpleName(e2); 1597 if (compareStrings(s1, s2) == 0) { 1598 String f1 = getFullyQualifiedName(e1, true); 1599 String f2 = getFullyQualifiedName(e2, true); 1600 return compareStrings(f1, f2) == 0; 1601 } 1602 return false; 1603 } 1604 1605 /** 1606 * A general purpose case insensitive String comparator, which compares 1607 * two Strings using a Collator strength of "TERTIARY". 1608 * 1609 * @param s1 first String to compare. 1610 * @param s2 second String to compare. 1611 * @return a negative integer, zero, or a positive integer as the first 1612 * argument is less than, equal to, or greater than the second. 1613 */ 1614 public int compareStrings(String s1, String s2) { 1615 return compareStrings(true, s1, s2); 1616 } 1617 1618 /** 1619 * A general purpose case sensitive String comparator, which 1620 * compares two Strings using a Collator strength of "SECONDARY". 1621 * 1622 * @param s1 first String to compare. 1623 * @param s2 second String to compare. 1624 * @return a negative integer, zero, or a positive integer as the first 1625 * argument is less than, equal to, or greater than the second. 1626 */ 1627 public int compareCaseCompare(String s1, String s2) { 1628 return compareStrings(false, s1, s2); 1629 } 1630 1631 private DocCollator tertiaryCollator = null; 1632 private DocCollator secondaryCollator = null; 1633 1634 private int compareStrings(boolean caseSensitive, String s1, String s2) { 1635 if (caseSensitive) { 1636 if (tertiaryCollator == null) { 1637 tertiaryCollator = new DocCollator(configuration.locale, Collator.TERTIARY); 1638 } 1639 return tertiaryCollator.compare(s1, s2); 1640 } 1641 if (secondaryCollator == null) { 1642 secondaryCollator = new DocCollator(configuration.locale, Collator.SECONDARY); 1643 } 1644 return secondaryCollator.compare(s1, s2); 1645 } 1646 1647 private static class DocCollator { 1648 private final Map<String, CollationKey> keys; 1649 private final Collator instance; 1650 private final int MAX_SIZE = 1000; 1651 private DocCollator(Locale locale, int strength) { 1652 instance = Collator.getInstance(locale); 1653 instance.setStrength(strength); 1654 1655 keys = new LinkedHashMap<String, CollationKey>(MAX_SIZE + 1, 0.75f, true) { 1656 private static final long serialVersionUID = 1L; 1657 @Override 1658 protected boolean removeEldestEntry(Entry<String, CollationKey> eldest) { 1659 return size() > MAX_SIZE; 1660 } 1661 }; 1662 } 1663 1664 CollationKey getKey(String s) { 1665 return keys.computeIfAbsent(s, instance :: getCollationKey); 1666 } 1667 1668 public int compare(String s1, String s2) { 1669 return getKey(s1).compareTo(getKey(s2)); 1670 } 1671 } 1672 1673 /** 1674 * Comparator for ModuleElements, simply compares the fully qualified names 1675 * @return a Comparator 1676 */ 1677 public Comparator<Element> makeModuleComparator() { 1678 return new Utils.ElementComparator<Element>() { 1679 @Override 1680 public int compare(Element mod1, Element mod2) { 1681 return compareFullyQualifiedNames(mod1, mod2); 1682 } 1683 }; 1684 } 1685 1686 /** 1687 * Returns a Comparator for all classes, compares the simple names of 1688 * TypeElement, if equal then the fully qualified names. 1689 * 1690 * @return Comparator 1691 */ 1692 public Comparator<Element> makeAllClassesComparator() { 1693 return new Utils.ElementComparator<Element>() { 1694 @Override 1695 public int compare(Element e1, Element e2) { 1696 int result = compareNames(e1, e2); 1697 if (result == 0) 1698 result = compareFullyQualifiedNames(e1, e2); 1699 1700 return result; 1701 } 1702 }; 1703 } 1704 1705 /** 1706 * Returns a Comparator for packages, by comparing the fully qualified names. 1707 * 1708 * @return a Comparator 1709 */ 1710 public Comparator<Element> makePackageComparator() { 1711 return new Utils.ElementComparator<Element>() { 1712 @Override 1713 public int compare(Element pkg1, Element pkg2) { 1714 return compareFullyQualifiedNames(pkg1, pkg2); 1715 } 1716 }; 1717 } 1718 1719 /** 1720 * Returns a Comparator for SerialFieldTree. 1721 * @return a Comparator 1722 */ 1723 public Comparator<SerialFieldTree> makeSerialFieldTreeComparator() { 1724 return (SerialFieldTree o1, SerialFieldTree o2) -> { 1725 String s1 = o1.getName().toString(); 1726 String s2 = o2.getName().toString(); 1727 return s1.compareTo(s2); 1728 }; 1729 } 1730 1731 /** 1732 * Returns a general purpose comparator. 1733 * @return a Comparator 1734 */ 1735 public Comparator<Element> makeGeneralPurposeComparator() { 1736 return makeClassUseComparator(); 1737 } 1738 1739 /** 1740 * Returns a Comparator for overrides and implements, 1741 * used primarily on methods, compares the name first, 1742 * then compares the simple names of the enclosing 1743 * TypeElement and the fully qualified name of the enclosing TypeElement. 1744 * @return a Comparator 1745 */ 1746 public Comparator<Element> makeOverrideUseComparator() { 1747 return new Utils.ElementComparator<Element>() { 1748 @Override 1749 public int compare(Element o1, Element o2) { 1750 int result = compareStrings(getSimpleName(o1), getSimpleName(o2)); 1751 if (result != 0) { 1752 return result; 1753 } 1754 if (!isTypeElement(o1) && !isTypeElement(o2) && !isPackage(o1) && !isPackage(o2)) { 1755 TypeElement t1 = getEnclosingTypeElement(o1); 1756 TypeElement t2 = getEnclosingTypeElement(o2); 1757 result = compareStrings(getSimpleName(t1), getSimpleName(t2)); 1758 if (result != 0) 1759 return result; 1760 } 1761 result = compareStrings(getFullyQualifiedName(o1), getFullyQualifiedName(o2)); 1762 if (result != 0) 1763 return result; 1764 return compareElementTypeKinds(o1, o2); 1765 } 1766 }; 1767 } 1768 1769 /** 1770 * Returns a Comparator for index file presentations, and are sorted as follows. 1771 * If comparing modules then simply compare the simple names, 1772 * comparing packages then simply compare the qualified names, otherwise 1773 * 1. if equal, then compare the ElementKind ex: Module, Package, Interface etc. 1774 * 2. sort on simple names of entities 1775 * 3a. if equal and if the type is of ExecutableElement(Constructor, Methods), 1776 * a case insensitive comparison of parameter the type signatures 1777 * 3b. if equal, case sensitive comparison of the type signatures 1778 * 4. finally, if equal, compare the FQNs of the entities 1779 * Iff comparing packages then simply sort on qualified names. 1780 * @return a comparator for index file use 1781 */ 1782 public Comparator<Element> makeIndexUseComparator() { 1783 return new Utils.ElementComparator<Element>() { 1784 /** 1785 * Compare two given elements, if comparing two modules, return the 1786 * comparison of SimpleName, if comparing two packages, return the 1787 * comparison of FullyQualifiedName, first sort on kinds, then on the 1788 * names, then on the parameters only if the type is an ExecutableElement, 1789 * the parameters are compared and finally the qualified names. 1790 * 1791 * @param e1 - an element. 1792 * @param e2 - an element. 1793 * @return a negative integer, zero, or a positive integer as the first 1794 * argument is less than, equal to, or greater than the second. 1795 */ 1796 @Override 1797 public int compare(Element e1, Element e2) { 1798 int result = 0; 1799 if (isModule(e1) && isModule(e2)) { 1800 return compareNames(e1, e2); 1801 } 1802 if (isPackage(e1) && isPackage(e2)) { 1803 return compareFullyQualifiedNames(e1, e2); 1804 } 1805 result = compareElementTypeKinds(e1, e2); 1806 if (result != 0) { 1807 return result; 1808 } 1809 result = compareNames(e1, e2); 1810 if (result != 0) { 1811 return result; 1812 } 1813 if (hasParameters(e1)) { 1814 List<? extends VariableElement> parameters1 = ((ExecutableElement)e1).getParameters(); 1815 List<? extends VariableElement> parameters2 = ((ExecutableElement)e2).getParameters(); 1816 result = compareParameters(false, parameters1, parameters2); 1817 if (result != 0) { 1818 return result; 1819 } 1820 result = compareParameters(true, parameters1, parameters2); 1821 if (result != 0) { 1822 return result; 1823 } 1824 } 1825 return compareFullyQualifiedNames(e1, e2); 1826 } 1827 }; 1828 } 1829 1830 /** 1831 * Compares the FullyQualifiedNames of two TypeMirrors 1832 * @return 1833 */ 1834 public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() { 1835 return (TypeMirror type1, TypeMirror type2) -> { 1836 String s1 = getQualifiedTypeName(type1); 1837 String s2 = getQualifiedTypeName(type2); 1838 return compareStrings(s1, s2); 1839 }; 1840 } 1841 1842 /** 1843 * Compares the SimpleNames of TypeMirrors if equal then the 1844 * FullyQualifiedNames of TypeMirrors. 1845 * 1846 * @return 1847 */ 1848 public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() { 1849 return (TypeMirror t1, TypeMirror t2) -> { 1850 int result = compareStrings(getTypeName(t1, false), getTypeName(t2, false)); 1851 if (result != 0) 1852 return result; 1853 return compareStrings(getQualifiedTypeName(t1), getQualifiedTypeName(t2)); 1854 }; 1855 } 1856 1857 /** 1858 * Get the qualified type name of a TypeMiror compatible with the Element's 1859 * getQualified name, returns the qualified name of the Reference type 1860 * otherwise the primitive name. 1861 * @param t the type whose name is to be obtained. 1862 * @return the fully qualified name of Reference type or the primitive name 1863 */ 1864 public String getQualifiedTypeName(TypeMirror t) { 1865 return new SimpleTypeVisitor9<String, Void>() { 1866 @Override 1867 public String visitDeclared(DeclaredType t, Void p) { 1868 return getFullyQualifiedName(t.asElement()); 1869 } 1870 1871 @Override 1872 public String visitArray(ArrayType t, Void p) { 1873 return visit(t.getComponentType()); 1874 } 1875 1876 @Override 1877 public String visitPrimitive(PrimitiveType t, Void p) { 1878 return t.toString(); 1879 } 1880 1881 @Override 1882 public String visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 1883 // The knee jerk reaction is to do this but don't!, as we would like 1884 // it to be compatible with the old world, now if we decide to do so 1885 // care must be taken to avoid collisions. 1886 // return getFullyQualifiedName(t.asElement()); 1887 return t.toString(); 1888 } 1889 1890 @Override 1891 protected String defaultAction(TypeMirror e, Void p) { 1892 throw new UnsupportedOperationException("should not happen"); 1893 } 1894 }.visit(t); 1895 } 1896 1897 /** 1898 * A generic utility which returns the fully qualified names of an entity, 1899 * if the entity is not qualifiable then its enclosing entity, it is upto 1900 * the caller to add the elements name as required. 1901 * @param e the element to get FQN for. 1902 * @return the name 1903 */ 1904 public String getFullyQualifiedName(Element e) { 1905 return getFullyQualifiedName(e, true); 1906 } 1907 1908 public String getFullyQualifiedName(Element e, final boolean outer) { 1909 return new SimpleElementVisitor9<String, Void>() { 1910 @Override 1911 public String visitPackage(PackageElement e, Void p) { 1912 return e.getQualifiedName().toString(); 1913 } 1914 1915 @Override 1916 public String visitType(TypeElement e, Void p) { 1917 return e.getQualifiedName().toString(); 1918 } 1919 1920 @Override 1921 protected String defaultAction(Element e, Void p) { 1922 return outer ? visit(e.getEnclosingElement()) : e.getSimpleName().toString(); 1923 } 1924 }.visit(e); 1925 } 1926 1927 /** 1928 * Comparator for ClassUse presentations, and sorts as follows: 1929 * 1. member names 1930 * 2. then fully qualified member names 1931 * 3. then parameter types if applicable 1932 * 4. finally the element kinds ie. package, class, interface etc. 1933 * @return a comparator to sort classes and members for class use 1934 */ 1935 public Comparator<Element> makeClassUseComparator() { 1936 return new Utils.ElementComparator<Element>() { 1937 /** 1938 * Compare two Elements, first sort on simple name, and if 1939 * applicable on the fully qualified name, and finally if applicable 1940 * on the parameter types. 1941 * @param e1 - an element. 1942 * @param e2 - an element. 1943 * @return a negative integer, zero, or a positive integer as the first 1944 * argument is less than, equal to, or greater than the second. 1945 */ 1946 @Override 1947 public int compare(Element e1, Element e2) { 1948 int result = compareNames(e1, e2); 1949 if (result != 0) { 1950 return result; 1951 } 1952 result = compareFullyQualifiedNames(e1, e2); 1953 if (result != 0) { 1954 return result; 1955 } 1956 if (hasParameters(e1) && hasParameters(e2)) { 1957 @SuppressWarnings("unchecked") 1958 List<VariableElement> parameters1 = (List<VariableElement>) ((ExecutableElement)e1).getParameters(); 1959 @SuppressWarnings("unchecked") 1960 List<VariableElement> parameters2 = (List<VariableElement>) ((ExecutableElement)e2).getParameters(); 1961 result = compareParameters(false, parameters1, parameters2); 1962 if (result != 0) { 1963 return result; 1964 } 1965 result = compareParameters(true, parameters1, parameters2); 1966 } 1967 if (result != 0) { 1968 return result; 1969 } 1970 return compareElementTypeKinds(e1, e2); 1971 } 1972 }; 1973 } 1974 1975 /** 1976 * A general purpose comparator to sort Element entities, basically provides the building blocks 1977 * for creating specific comparators for an use-case. 1978 * @param <T> an Element 1979 */ 1980 private abstract class ElementComparator<T extends Element> implements Comparator<Element> { 1981 /** 1982 * compares two parameter arrays by first comparing the length of the arrays, and 1983 * then each Type of the parameter in the array. 1984 * @param params1 the first parameter array. 1985 * @param params2 the first parameter array. 1986 * @return a negative integer, zero, or a positive integer as the first 1987 * argument is less than, equal to, or greater than the second. 1988 */ 1989 final EnumMap<ElementKind, Integer> elementKindOrder; 1990 public ElementComparator() { 1991 elementKindOrder = new EnumMap<>(ElementKind.class); 1992 elementKindOrder.put(ElementKind.MODULE, 0); 1993 elementKindOrder.put(ElementKind.PACKAGE, 1); 1994 elementKindOrder.put(ElementKind.CLASS, 2); 1995 elementKindOrder.put(ElementKind.ENUM, 3); 1996 elementKindOrder.put(ElementKind.ENUM_CONSTANT, 4); 1997 elementKindOrder.put(ElementKind.INTERFACE, 5); 1998 elementKindOrder.put(ElementKind.ANNOTATION_TYPE, 6); 1999 elementKindOrder.put(ElementKind.FIELD, 7); 2000 elementKindOrder.put(ElementKind.CONSTRUCTOR, 8); 2001 elementKindOrder.put(ElementKind.METHOD, 9); 2002 } 2003 2004 protected int compareParameters(boolean caseSensitive, List<? extends VariableElement> params1, 2005 List<? extends VariableElement> params2) { 2006 2007 return compareStrings(caseSensitive, getParametersAsString(params1), 2008 getParametersAsString(params2)); 2009 } 2010 2011 String getParametersAsString(List<? extends VariableElement> params) { 2012 StringBuilder sb = new StringBuilder(); 2013 for (VariableElement param : params) { 2014 TypeMirror t = param.asType(); 2015 // prefix P for primitive and R for reference types, thus items will 2016 // be ordered lexically and correctly. 2017 sb.append(getTypeCode(t)).append("-").append(t).append("-"); 2018 } 2019 return sb.toString(); 2020 } 2021 2022 private String getTypeCode(TypeMirror t) { 2023 return new SimpleTypeVisitor9<String, Void>() { 2024 2025 @Override 2026 public String visitPrimitive(PrimitiveType t, Void p) { 2027 return "P"; 2028 } 2029 @Override 2030 public String visitArray(ArrayType t, Void p) { 2031 return visit(t.getComponentType()); 2032 } 2033 @Override 2034 protected String defaultAction(TypeMirror e, Void p) { 2035 return "R"; 2036 } 2037 2038 }.visit(t); 2039 } 2040 2041 /** 2042 * Compares two Elements, typically the name of a method, 2043 * field or constructor. 2044 * @param e1 the first Element. 2045 * @param e2 the second Element. 2046 * @return a negative integer, zero, or a positive integer as the first 2047 * argument is less than, equal to, or greater than the second. 2048 */ 2049 protected int compareNames(Element e1, Element e2) { 2050 return compareStrings(getSimpleName(e1), getSimpleName(e2)); 2051 } 2052 2053 /** 2054 * Compares the fully qualified names of the entities 2055 * @param e1 the first Element. 2056 * @param e2 the first Element. 2057 * @return a negative integer, zero, or a positive integer as the first 2058 * argument is less than, equal to, or greater than the second. 2059 */ 2060 protected int compareFullyQualifiedNames(Element e1, Element e2) { 2061 // add simplename to be compatible 2062 String thisElement = getFullyQualifiedName(e1); 2063 String thatElement = getFullyQualifiedName(e2); 2064 return compareStrings(thisElement, thatElement); 2065 } 2066 protected int compareElementTypeKinds(Element e1, Element e2) { 2067 return Integer.compare(elementKindOrder.get(e1.getKind()), 2068 elementKindOrder.get(e2.getKind())); 2069 } 2070 boolean hasParameters(Element e) { 2071 return new SimpleElementVisitor9<Boolean, Void>() { 2072 @Override 2073 public Boolean visitExecutable(ExecutableElement e, Void p) { 2074 return true; 2075 } 2076 2077 @Override 2078 protected Boolean defaultAction(Element e, Void p) { 2079 return false; 2080 } 2081 2082 }.visit(e); 2083 } 2084 2085 /** 2086 * The fully qualified names of the entities, used solely by the comparator. 2087 * 2088 * @param p1 the first Element. 2089 * @param p2 the first Element. 2090 * @return a negative integer, zero, or a positive integer as the first argument is less 2091 * than, equal to, or greater than the second. 2092 */ 2093 private String getFullyQualifiedName(Element e) { 2094 return new SimpleElementVisitor9<String, Void>() { 2095 @Override 2096 public String visitModule(ModuleElement e, Void p) { 2097 return e.getQualifiedName().toString(); 2098 } 2099 2100 @Override 2101 public String visitPackage(PackageElement e, Void p) { 2102 return e.getQualifiedName().toString(); 2103 } 2104 2105 @Override 2106 public String visitExecutable(ExecutableElement e, Void p) { 2107 // For backward compatibility 2108 return getFullyQualifiedName(e.getEnclosingElement()) 2109 + "." + e.getSimpleName().toString(); 2110 } 2111 2112 @Override 2113 public String visitType(TypeElement e, Void p) { 2114 return e.getQualifiedName().toString(); 2115 } 2116 2117 @Override 2118 protected String defaultAction(Element e, Void p) { 2119 return getEnclosingTypeElement(e).getQualifiedName().toString() 2120 + "." + e.getSimpleName().toString(); 2121 } 2122 }.visit(e); 2123 } 2124 } 2125 2126 public Iterable<TypeElement> getEnclosedTypeElements(PackageElement pkg) { 2127 List<TypeElement> out = getInterfaces(pkg); 2128 out.addAll(getClasses(pkg)); 2129 out.addAll(getEnums(pkg)); 2130 out.addAll(getAnnotationTypes(pkg)); 2131 return out; 2132 } 2133 2134 // Element related methods 2135 public List<Element> getAnnotationMembers(TypeElement aClass) { 2136 List<Element> members = getAnnotationFields(aClass); 2137 members.addAll(getAnnotationMethods(aClass)); 2138 return members; 2139 } 2140 2141 public List<Element> getAnnotationFields(TypeElement aClass) { 2142 return getItems0(aClass, true, FIELD); 2143 } 2144 2145 List<Element> getAnnotationFieldsUnfiltered(TypeElement aClass) { 2146 return getItems0(aClass, true, FIELD); 2147 } 2148 2149 public List<Element> getAnnotationMethods(TypeElement aClass) { 2150 return getItems0(aClass, true, METHOD); 2151 } 2152 2153 public List<TypeElement> getAnnotationTypes(Element e) { 2154 return convertToTypeElement(getItems(e, true, ANNOTATION_TYPE)); 2155 } 2156 2157 public List<TypeElement> getAnnotationTypesUnfiltered(Element e) { 2158 return convertToTypeElement(getItems(e, false, ANNOTATION_TYPE)); 2159 } 2160 2161 public List<VariableElement> getFields(Element e) { 2162 return convertToVariableElement(getItems(e, true, FIELD)); 2163 } 2164 2165 public List<VariableElement> getFieldsUnfiltered(Element e) { 2166 return convertToVariableElement(getItems(e, false, FIELD)); 2167 } 2168 2169 public List<TypeElement> getClasses(Element e) { 2170 return convertToTypeElement(getItems(e, true, CLASS)); 2171 } 2172 2173 public List<TypeElement> getClassesUnfiltered(Element e) { 2174 return convertToTypeElement(getItems(e, false, CLASS)); 2175 } 2176 2177 public List<ExecutableElement> getConstructors(Element e) { 2178 return convertToExecutableElement(getItems(e, true, CONSTRUCTOR)); 2179 } 2180 2181 public List<ExecutableElement> getMethods(Element e) { 2182 return convertToExecutableElement(getItems(e, true, METHOD)); 2183 } 2184 2185 List<ExecutableElement> getMethodsUnfiltered(Element e) { 2186 return convertToExecutableElement(getItems(e, false, METHOD)); 2187 } 2188 2189 public int getOrdinalValue(VariableElement member) { 2190 if (member == null || member.getKind() != ENUM_CONSTANT) { 2191 throw new IllegalArgumentException("must be an enum constant: " + member); 2192 } 2193 return member.getEnclosingElement().getEnclosedElements().indexOf(member); 2194 } 2195 2196 public long getLineNumber(Element e) { 2197 TreePath path = getTreePath(e); 2198 if (path == null) { // maybe null if synthesized 2199 TypeElement encl = getEnclosingTypeElement(e); 2200 path = getTreePath(encl); 2201 } 2202 CompilationUnitTree cu = path.getCompilationUnit(); 2203 LineMap lineMap = cu.getLineMap(); 2204 DocSourcePositions spos = docTrees.getSourcePositions(); 2205 long pos = spos.getStartPosition(cu, path.getLeaf()); 2206 return lineMap.getLineNumber(pos); 2207 } 2208 2209 public List<ExecutableElement> convertToExecutableElement(List<Element> list) { 2210 List<ExecutableElement> out = new ArrayList<>(list.size()); 2211 for (Element e : list) { 2212 out.add((ExecutableElement)e); 2213 } 2214 return out; 2215 } 2216 2217 public List<TypeElement> convertToTypeElement(List<Element> list) { 2218 List<TypeElement> out = new ArrayList<>(list.size()); 2219 for (Element e : list) { 2220 out.add((TypeElement)e); 2221 } 2222 return out; 2223 } 2224 2225 public List<VariableElement> convertToVariableElement(List<Element> list) { 2226 List<VariableElement> out = new ArrayList<>(list.size()); 2227 for (Element e : list) { 2228 out.add((VariableElement) e); 2229 } 2230 return out; 2231 } 2232 2233 public List<TypeElement> getInterfaces(Element e) { 2234 return convertToTypeElement(getItems(e, true, INTERFACE)); 2235 } 2236 2237 public List<TypeElement> getInterfacesUnfiltered(Element e) { 2238 return convertToTypeElement(getItems(e, false, INTERFACE)); 2239 } 2240 2241 List<Element> getNestedClasses(TypeElement e) { 2242 List<Element> result = new ArrayList<>(); 2243 recursiveGetItems(result, e, true, CLASS); 2244 return result; 2245 } 2246 2247 List<Element> getNestedClassesUnfiltered(TypeElement e) { 2248 List<Element> result = new ArrayList<>(); 2249 recursiveGetItems(result, e, false, CLASS); 2250 return result; 2251 } 2252 2253 public List<Element> getEnumConstants(Element e) { 2254 return getItems(e, true, ENUM_CONSTANT); 2255 } 2256 2257 public List<TypeElement> getEnums(Element e) { 2258 return convertToTypeElement(getItems(e, true, ENUM)); 2259 } 2260 2261 public List<TypeElement> getEnumsUnfiltered(Element e) { 2262 return convertToTypeElement(getItems(e, false, ENUM)); 2263 } 2264 2265 public SortedSet<TypeElement> getAllClassesUnfiltered(Element e) { 2266 List<TypeElement> clist = getClassesUnfiltered(e); 2267 clist.addAll(getInterfacesUnfiltered(e)); 2268 clist.addAll(getAnnotationTypesUnfiltered(e)); 2269 SortedSet<TypeElement> oset = new TreeSet<>(makeGeneralPurposeComparator()); 2270 oset.addAll(clist); 2271 return oset; 2272 } 2273 2274 private final HashMap<Element, SortedSet<TypeElement>> cachedClasses = new HashMap<>(); 2275 /** 2276 * Returns a list containing classes and interfaces, 2277 * including annotation types. 2278 * @param e Element 2279 * @return List 2280 */ 2281 public SortedSet<TypeElement> getAllClasses(Element e) { 2282 SortedSet<TypeElement> oset = cachedClasses.get(e); 2283 if (oset != null) 2284 return oset; 2285 List<TypeElement> clist = getClasses(e); 2286 clist.addAll(getInterfaces(e)); 2287 clist.addAll(getAnnotationTypes(e)); 2288 clist.addAll(getEnums(e)); 2289 oset = new TreeSet<>(makeGeneralPurposeComparator()); 2290 oset.addAll(clist); 2291 cachedClasses.put(e, oset); 2292 return oset; 2293 } 2294 2295 /* 2296 * Get all the elements unfiltered and filter them finally based 2297 * on its visibility, this works differently from the other getters. 2298 */ 2299 private List<TypeElement> getInnerClasses(Element e, boolean filter) { 2300 List<TypeElement> olist = new ArrayList<>(); 2301 for (TypeElement te : getClassesUnfiltered(e)) { 2302 if (!filter || configuration.docEnv.isSelected(te)) { 2303 olist.add(te); 2304 } 2305 } 2306 for (TypeElement te : getInterfacesUnfiltered(e)) { 2307 if (!filter || configuration.docEnv.isSelected(te)) { 2308 olist.add(te); 2309 } 2310 } 2311 for (TypeElement te : getAnnotationTypesUnfiltered(e)) { 2312 if (!filter || configuration.docEnv.isSelected(te)) { 2313 olist.add(te); 2314 } 2315 } 2316 for (TypeElement te : getEnumsUnfiltered(e)) { 2317 if (!filter || configuration.docEnv.isSelected(te)) { 2318 olist.add(te); 2319 } 2320 } 2321 return olist; 2322 } 2323 2324 public List<TypeElement> getInnerClasses(Element e) { 2325 return getInnerClasses(e, true); 2326 } 2327 2328 public List<TypeElement> getInnerClassesUnfiltered(Element e) { 2329 return getInnerClasses(e, false); 2330 } 2331 2332 /** 2333 * Returns a list of classes that are not errors or exceptions 2334 * @param e Element 2335 * @return List 2336 */ 2337 public List<TypeElement> getOrdinaryClasses(Element e) { 2338 return getClasses(e).stream() 2339 .filter(te -> (!isException(te) && !isError(te))) 2340 .collect(Collectors.toList()); 2341 } 2342 2343 public List<TypeElement> getErrors(Element e) { 2344 return getClasses(e) 2345 .stream() 2346 .filter(this::isError) 2347 .collect(Collectors.toList()); 2348 } 2349 2350 public List<TypeElement> getExceptions(Element e) { 2351 return getClasses(e) 2352 .stream() 2353 .filter(this::isException) 2354 .collect(Collectors.toList()); 2355 } 2356 2357 List<Element> getItems(Element e, boolean filter, ElementKind select) { 2358 List<Element> elements = new ArrayList<>(); 2359 // maintain backward compatibility by returning a null list, see AnnotationDocType.methods(). 2360 if (configuration.backwardCompatibility && e.getKind() == ANNOTATION_TYPE) 2361 return elements; 2362 return new SimpleElementVisitor9<List<Element>, Void>() { 2363 2364 @Override 2365 public List<Element> visitPackage(PackageElement e, Void p) { 2366 recursiveGetItems(elements, e, filter, select); 2367 return elements; 2368 } 2369 2370 @Override 2371 protected List<Element> defaultAction(Element e0, Void p) { 2372 return getItems0(e0, filter, select); 2373 } 2374 2375 }.visit(e); 2376 } 2377 2378 EnumSet<ElementKind> nestedKinds = EnumSet.of(ANNOTATION_TYPE, CLASS, ENUM, INTERFACE); 2379 2380 void recursiveGetItems(Collection<Element> list, Element e, boolean filter, ElementKind... select) { 2381 list.addAll(getItems0(e, filter, select)); 2382 List<Element> classes = getItems0(e, filter, nestedKinds); 2383 for (Element c : classes) { 2384 list.addAll(getItems0(c, filter, select)); 2385 if (isTypeElement(c)) { 2386 recursiveGetItems(list, c, filter, select); 2387 } 2388 } 2389 } 2390 2391 private List<Element> getItems0(Element te, boolean filter, ElementKind... select) { 2392 EnumSet<ElementKind> kinds = EnumSet.copyOf(Arrays.asList(select)); 2393 return getItems0(te, filter, kinds); 2394 } 2395 2396 private List<Element> getItems0(Element te, boolean filter, Set<ElementKind> kinds) { 2397 List<Element> elements = new ArrayList<>(); 2398 for (Element e : te.getEnclosedElements()) { 2399 if (kinds.contains(e.getKind())) { 2400 if (!filter || shouldDocument(e)) { 2401 elements.add(e); 2402 } 2403 } 2404 } 2405 return elements; 2406 } 2407 2408 private SimpleElementVisitor9<Boolean, Void> shouldDocumentVisitor = null; 2409 private boolean shouldDocument(Element e) { 2410 if (shouldDocumentVisitor == null) { 2411 shouldDocumentVisitor = new SimpleElementVisitor9<Boolean, Void>() { 2412 private boolean hasSource(TypeElement e) { 2413 return configuration.docEnv.getFileKind(e) == 2414 javax.tools.JavaFileObject.Kind.SOURCE; 2415 } 2416 2417 // handle types 2418 @Override 2419 public Boolean visitType(TypeElement e, Void p) { 2420 return configuration.docEnv.isSelected(e) && hasSource(e); 2421 } 2422 2423 // handle everything else 2424 @Override 2425 protected Boolean defaultAction(Element e, Void p) { 2426 return configuration.docEnv.isSelected(e); 2427 } 2428 2429 @Override 2430 public Boolean visitUnknown(Element e, Void p) { 2431 throw new AssertionError("unkown element: " + p); 2432 } 2433 }; 2434 } 2435 return shouldDocumentVisitor.visit(e); 2436 } 2437 2438 /* 2439 * nameCache is maintained for improving the comparator 2440 * performance, noting that the Collator used by the comparators 2441 * use Strings, as of this writing. 2442 * TODO: when those APIs handle charSequences, the use of 2443 * this nameCache must be re-investigated and removed. 2444 */ 2445 private final Map<Element, String> nameCache = new LinkedHashMap<>(); 2446 2447 /** 2448 * Returns the name of the element after the last dot of the package name. 2449 * This emulates the behavior of the old doclet. 2450 * @param e an element whose name is required 2451 * @return the name 2452 */ 2453 public String getSimpleName(Element e) { 2454 return nameCache.computeIfAbsent(e, this::getSimpleName0); 2455 } 2456 2457 private SimpleElementVisitor9<String, Void> snvisitor = null; 2458 2459 private String getSimpleName0(Element e) { 2460 if (snvisitor == null) { 2461 snvisitor = new SimpleElementVisitor9<String, Void>() { 2462 @Override 2463 public String visitModule(ModuleElement e, Void p) { 2464 return e.getSimpleName().toString(); 2465 } 2466 2467 @Override 2468 public String visitType(TypeElement e, Void p) { 2469 StringBuilder sb = new StringBuilder(e.getSimpleName()); 2470 Element enclosed = e.getEnclosingElement(); 2471 while (enclosed != null 2472 && (enclosed.getKind().isClass() || enclosed.getKind().isInterface())) { 2473 sb.insert(0, enclosed.getSimpleName() + "."); 2474 enclosed = enclosed.getEnclosingElement(); 2475 } 2476 return sb.toString(); 2477 } 2478 2479 @Override 2480 public String visitExecutable(ExecutableElement e, Void p) { 2481 if (e.getKind() == CONSTRUCTOR || e.getKind() == STATIC_INIT) { 2482 return e.getEnclosingElement().getSimpleName().toString(); 2483 } 2484 return e.getSimpleName().toString(); 2485 } 2486 2487 @Override 2488 protected String defaultAction(Element e, Void p) { 2489 return e.getSimpleName().toString(); 2490 } 2491 }; 2492 } 2493 return snvisitor.visit(e); 2494 } 2495 2496 public TypeElement getEnclosingTypeElement(Element e) { 2497 if (e.getKind() == ElementKind.PACKAGE) 2498 return null; 2499 Element encl = e.getEnclosingElement(); 2500 ElementKind kind = encl.getKind(); 2501 if (kind == ElementKind.PACKAGE) 2502 return null; 2503 while (!(kind.isClass() || kind.isInterface())) { 2504 encl = encl.getEnclosingElement(); 2505 } 2506 return (TypeElement)encl; 2507 } 2508 2509 private ConstantValueExpression cve = null; 2510 2511 public String constantValueExpresion(VariableElement ve) { 2512 if (cve == null) 2513 cve = new ConstantValueExpression(); 2514 return cve.constantValueExpression(configuration.workArounds, ve); 2515 } 2516 2517 private static class ConstantValueExpression { 2518 public String constantValueExpression(WorkArounds workArounds, VariableElement ve) { 2519 return new TypeKindVisitor9<String, Object>() { 2520 /* TODO: we need to fix this correctly. 2521 * we have a discrepancy here, note the use of getConstValue 2522 * vs. getConstantValue, at some point we need to use 2523 * getConstantValue. 2524 * In the legacy world byte and char primitives appear as Integer values, 2525 * thus a byte value of 127 will appear as 127, but in the new world, 2526 * a byte value appears as Byte thus 0x7f will be printed, similarly 2527 * chars will be translated to \n, \r etc. however, in the new world, 2528 * they will be printed as decimal values. The new world is correct, 2529 * and we should fix this by using getConstantValue and the visitor to 2530 * address this in the future. 2531 */ 2532 @Override 2533 public String visitPrimitiveAsBoolean(PrimitiveType t, Object val) { 2534 return (int)val == 0 ? "false" : "true"; 2535 } 2536 2537 @Override 2538 public String visitPrimitiveAsDouble(PrimitiveType t, Object val) { 2539 return sourceForm(((Double)val), 'd'); 2540 } 2541 2542 @Override 2543 public String visitPrimitiveAsFloat(PrimitiveType t, Object val) { 2544 return sourceForm(((Float)val).doubleValue(), 'f'); 2545 } 2546 2547 @Override 2548 public String visitPrimitiveAsLong(PrimitiveType t, Object val) { 2549 return val + "L"; 2550 } 2551 2552 @Override 2553 protected String defaultAction(TypeMirror e, Object val) { 2554 if (val == null) 2555 return null; 2556 else if (val instanceof Character) 2557 return sourceForm(((Character)val)); 2558 else if (val instanceof Byte) 2559 return sourceForm(((Byte)val)); 2560 else if (val instanceof String) 2561 return sourceForm((String)val); 2562 return val.toString(); // covers int, short 2563 } 2564 }.visit(ve.asType(), workArounds.getConstValue(ve)); 2565 } 2566 2567 // where 2568 private String sourceForm(double v, char suffix) { 2569 if (Double.isNaN(v)) 2570 return "0" + suffix + "/0" + suffix; 2571 if (v == Double.POSITIVE_INFINITY) 2572 return "1" + suffix + "/0" + suffix; 2573 if (v == Double.NEGATIVE_INFINITY) 2574 return "-1" + suffix + "/0" + suffix; 2575 return v + (suffix == 'f' || suffix == 'F' ? "" + suffix : ""); 2576 } 2577 2578 private String sourceForm(char c) { 2579 StringBuilder buf = new StringBuilder(8); 2580 buf.append('\''); 2581 sourceChar(c, buf); 2582 buf.append('\''); 2583 return buf.toString(); 2584 } 2585 2586 private String sourceForm(byte c) { 2587 return "0x" + Integer.toString(c & 0xff, 16); 2588 } 2589 2590 private String sourceForm(String s) { 2591 StringBuilder buf = new StringBuilder(s.length() + 5); 2592 buf.append('\"'); 2593 for (int i=0; i<s.length(); i++) { 2594 char c = s.charAt(i); 2595 sourceChar(c, buf); 2596 } 2597 buf.append('\"'); 2598 return buf.toString(); 2599 } 2600 2601 private void sourceChar(char c, StringBuilder buf) { 2602 switch (c) { 2603 case '\b': buf.append("\\b"); return; 2604 case '\t': buf.append("\\t"); return; 2605 case '\n': buf.append("\\n"); return; 2606 case '\f': buf.append("\\f"); return; 2607 case '\r': buf.append("\\r"); return; 2608 case '\"': buf.append("\\\""); return; 2609 case '\'': buf.append("\\\'"); return; 2610 case '\\': buf.append("\\\\"); return; 2611 default: 2612 if (isPrintableAscii(c)) { 2613 buf.append(c); return; 2614 } 2615 unicodeEscape(c, buf); 2616 return; 2617 } 2618 } 2619 2620 private void unicodeEscape(char c, StringBuilder buf) { 2621 final String chars = "0123456789abcdef"; 2622 buf.append("\\u"); 2623 buf.append(chars.charAt(15 & (c>>12))); 2624 buf.append(chars.charAt(15 & (c>>8))); 2625 buf.append(chars.charAt(15 & (c>>4))); 2626 buf.append(chars.charAt(15 & (c>>0))); 2627 } 2628 private boolean isPrintableAscii(char c) { 2629 return c >= ' ' && c <= '~'; 2630 } 2631 } 2632 2633 public boolean isEnclosingPackageIncluded(TypeElement te) { 2634 return isIncluded(containingPackage(te)); 2635 } 2636 2637 public boolean isIncluded(Element e) { 2638 return configuration.docEnv.isIncluded(e); 2639 } 2640 2641 private SimpleElementVisitor9<Boolean, Void> specifiedVisitor = null; 2642 public boolean isSpecified(Element e) { 2643 if (specifiedVisitor == null) { 2644 specifiedVisitor = new SimpleElementVisitor9<Boolean, Void>() { 2645 @Override 2646 public Boolean visitModule(ModuleElement e, Void p) { 2647 return configuration.getSpecifiedModuleElements().contains(e); 2648 } 2649 2650 @Override 2651 public Boolean visitPackage(PackageElement e, Void p) { 2652 return configuration.getSpecifiedPackageElements().contains(e); 2653 } 2654 2655 @Override 2656 public Boolean visitType(TypeElement e, Void p) { 2657 return configuration.getSpecifiedTypeElements().contains(e); 2658 } 2659 2660 @Override 2661 protected Boolean defaultAction(Element e, Void p) { 2662 return false; 2663 } 2664 }; 2665 } 2666 return specifiedVisitor.visit(e); 2667 } 2668 2669 /** 2670 * package name, an unnamed package is returned as <Unnamed> 2671 * @param pkg 2672 * @return 2673 */ 2674 public String getPackageName(PackageElement pkg) { 2675 if (pkg == null || pkg.isUnnamed()) { 2676 return DocletConstants.DEFAULT_PACKAGE_NAME; 2677 } 2678 return pkg.getQualifiedName().toString(); 2679 } 2680 2681 public boolean isAttribute(DocTree doctree) { 2682 return isKind(doctree, ATTRIBUTE); 2683 } 2684 2685 public boolean isAuthor(DocTree doctree) { 2686 return isKind(doctree, AUTHOR); 2687 } 2688 2689 public boolean isComment(DocTree doctree) { 2690 return isKind(doctree, COMMENT); 2691 } 2692 2693 public boolean isDeprecated(DocTree doctree) { 2694 return isKind(doctree, DEPRECATED); 2695 } 2696 2697 public boolean isDocComment(DocTree doctree) { 2698 return isKind(doctree, DOC_COMMENT); 2699 } 2700 2701 public boolean isDocRoot(DocTree doctree) { 2702 return isKind(doctree, DOC_ROOT); 2703 } 2704 2705 public boolean isEndElement(DocTree doctree) { 2706 return isKind(doctree, END_ELEMENT); 2707 } 2708 2709 public boolean isEntity(DocTree doctree) { 2710 return isKind(doctree, ENTITY); 2711 } 2712 2713 public boolean isErroneous(DocTree doctree) { 2714 return isKind(doctree, ERRONEOUS); 2715 } 2716 2717 public boolean isException(DocTree doctree) { 2718 return isKind(doctree, EXCEPTION); 2719 } 2720 2721 public boolean isIdentifier(DocTree doctree) { 2722 return isKind(doctree, IDENTIFIER); 2723 } 2724 2725 public boolean isInheritDoc(DocTree doctree) { 2726 return isKind(doctree, INHERIT_DOC); 2727 } 2728 2729 public boolean isLink(DocTree doctree) { 2730 return isKind(doctree, LINK); 2731 } 2732 2733 public boolean isLinkPlain(DocTree doctree) { 2734 return isKind(doctree, LINK_PLAIN); 2735 } 2736 2737 public boolean isLiteral(DocTree doctree) { 2738 return isKind(doctree, LITERAL); 2739 } 2740 2741 public boolean isOther(DocTree doctree) { 2742 return doctree.getKind() == DocTree.Kind.OTHER; 2743 } 2744 2745 public boolean isParam(DocTree doctree) { 2746 return isKind(doctree, PARAM); 2747 } 2748 2749 public boolean isReference(DocTree doctree) { 2750 return isKind(doctree, REFERENCE); 2751 } 2752 2753 public boolean isReturn(DocTree doctree) { 2754 return isKind(doctree, RETURN); 2755 } 2756 2757 public boolean isSee(DocTree doctree) { 2758 return isKind(doctree, SEE); 2759 } 2760 2761 public boolean isSerial(DocTree doctree) { 2762 return isKind(doctree, SERIAL); 2763 } 2764 2765 public boolean isSerialData(DocTree doctree) { 2766 return isKind(doctree, SERIAL_DATA); 2767 } 2768 2769 public boolean isSerialField(DocTree doctree) { 2770 return isKind(doctree, SERIAL_FIELD); 2771 } 2772 2773 public boolean isSince(DocTree doctree) { 2774 return isKind(doctree, SINCE); 2775 } 2776 2777 public boolean isStartElement(DocTree doctree) { 2778 return isKind(doctree, START_ELEMENT); 2779 } 2780 2781 public boolean isText(DocTree doctree) { 2782 return isKind(doctree, TEXT); 2783 } 2784 2785 public boolean isThrows(DocTree doctree) { 2786 return isKind(doctree, THROWS); 2787 } 2788 2789 public boolean isUnknownBlockTag(DocTree doctree) { 2790 return isKind(doctree, UNKNOWN_BLOCK_TAG); 2791 } 2792 2793 public boolean isUnknownInlineTag(DocTree doctree) { 2794 return isKind(doctree, UNKNOWN_INLINE_TAG); 2795 } 2796 2797 public boolean isValue(DocTree doctree) { 2798 return isKind(doctree, VALUE); 2799 } 2800 2801 public boolean isVersion(DocTree doctree) { 2802 return isKind(doctree, VERSION); 2803 } 2804 2805 private boolean isKind(DocTree doctree, DocTree.Kind match) { 2806 return doctree.getKind() == match; 2807 } 2808 2809 private final WeakSoftHashMap wksMap = new WeakSoftHashMap(this); 2810 2811 public CommentHelper getCommentHelper(Element element) { 2812 return wksMap.computeIfAbsent(element); 2813 } 2814 2815 public void removeCommentHelper(Element element) { 2816 wksMap.remove(element); 2817 } 2818 2819 public List<? extends DocTree> filteredList(List<? extends DocTree> dlist, DocTree.Kind... select) { 2820 List<DocTree> list = new ArrayList<>(dlist.size()); 2821 if (select == null) 2822 return dlist; 2823 for (DocTree dt : dlist) { 2824 if (dt.getKind() != ERRONEOUS) { 2825 for (DocTree.Kind kind : select) { 2826 if (dt.getKind() == kind) { 2827 list.add(dt); 2828 } 2829 } 2830 } 2831 } 2832 return list; 2833 } 2834 2835 private List<? extends DocTree> getBlockTags0(Element element, DocTree.Kind... kinds) { 2836 DocCommentTree dcTree = getDocCommentTree(element); 2837 if (dcTree == null) 2838 return Collections.emptyList(); 2839 2840 return filteredList(dcTree.getBlockTags(), kinds); 2841 } 2842 2843 public List<? extends DocTree> getBlockTags(Element element) { 2844 return getBlockTags0(element, (Kind[]) null); 2845 } 2846 2847 public List<? extends DocTree> getBlockTags(Element element, DocTree.Kind... kinds) { 2848 return getBlockTags0(element, kinds); 2849 } 2850 2851 public List<? extends DocTree> getBlockTags(Element element, String tagName) { 2852 DocTree.Kind kind = null; 2853 switch (tagName) { 2854 case "author": 2855 case "deprecated": 2856 case "hidden": 2857 case "param": 2858 case "return": 2859 case "see": 2860 case "serial": 2861 case "since": 2862 case "throws": 2863 case "exception": 2864 case "version": 2865 kind = DocTree.Kind.valueOf(tagName.toUpperCase()); 2866 return getBlockTags(element, kind); 2867 case "serialData": 2868 kind = SERIAL_DATA; 2869 return getBlockTags(element, kind); 2870 case "serialField": 2871 kind = SERIAL_FIELD; 2872 return getBlockTags(element, kind); 2873 default: 2874 kind = DocTree.Kind.UNKNOWN_BLOCK_TAG; 2875 break; 2876 } 2877 List<? extends DocTree> blockTags = getBlockTags(element, kind); 2878 List<DocTree> out = new ArrayList<>(); 2879 String tname = tagName.startsWith("@") ? tagName.substring(1) : tagName; 2880 CommentHelper ch = getCommentHelper(element); 2881 for (DocTree dt : blockTags) { 2882 if (ch.getTagName(dt).equals(tname)) { 2883 out.add(dt); 2884 } 2885 } 2886 return out; 2887 } 2888 2889 public boolean hasBlockTag(Element element, DocTree.Kind kind) { 2890 return hasBlockTag(element, kind, null); 2891 } 2892 2893 public boolean hasBlockTag(Element element, DocTree.Kind kind, final String tagName) { 2894 CommentHelper ch = getCommentHelper(element); 2895 String tname = tagName != null && tagName.startsWith("@") 2896 ? tagName.substring(1) 2897 : tagName; 2898 for (DocTree dt : getBlockTags(element, kind)) { 2899 if (dt.getKind() == kind) { 2900 if (tname == null || ch.getTagName(dt).equals(tname)) { 2901 return true; 2902 } 2903 } 2904 } 2905 return false; 2906 } 2907 2908 /** 2909 * Gets a TreePath for an Element. Note this method is called very 2910 * frequently, care must be taken to ensure this method is lithe 2911 * and efficient. 2912 * @param e an Element 2913 * @return TreePath 2914 */ 2915 public TreePath getTreePath(Element e) { 2916 DocCommentDuo duo = dcTreeCache.get(e); 2917 if (isValidDuo(duo) && duo.treePath != null) { 2918 return duo.treePath; 2919 } 2920 duo = configuration.cmtUtils.getSyntheticCommentDuo(e); 2921 if (isValidDuo(duo) && duo.treePath != null) { 2922 return duo.treePath; 2923 } 2924 Map<Element, TreePath> elementToTreePath = configuration.workArounds.getElementToTreePath(); 2925 TreePath path = elementToTreePath.get(e); 2926 if (path != null || elementToTreePath.containsKey(e)) { 2927 // expedite the path and one that is a null 2928 return path; 2929 } 2930 return elementToTreePath.computeIfAbsent(e, docTrees::getPath); 2931 } 2932 2933 private final Map<Element, DocCommentDuo> dcTreeCache = new LinkedHashMap<>(); 2934 2935 /** 2936 * Retrieves the doc comments for a given element. 2937 * @param element 2938 * @return DocCommentTree for the Element 2939 */ 2940 public DocCommentTree getDocCommentTree0(Element element) { 2941 2942 DocCommentDuo duo = null; 2943 2944 ElementKind kind = element.getKind(); 2945 if (kind == ElementKind.PACKAGE || kind == ElementKind.OTHER) { 2946 duo = dcTreeCache.get(element); // local cache 2947 if (!isValidDuo(duo) && kind == ElementKind.PACKAGE) { 2948 // package-info.java 2949 duo = getDocCommentTuple(element); 2950 } 2951 if (!isValidDuo(duo)) { 2952 // package.html or overview.html 2953 duo = configuration.cmtUtils.getHtmlCommentDuo(element); // html source 2954 } 2955 } else { 2956 duo = configuration.cmtUtils.getSyntheticCommentDuo(element); 2957 if (!isValidDuo(duo)) { 2958 duo = dcTreeCache.get(element); // local cache 2959 } 2960 if (!isValidDuo(duo)) { 2961 duo = getDocCommentTuple(element); // get the real mccoy 2962 } 2963 } 2964 2965 DocCommentTree docCommentTree = isValidDuo(duo) ? duo.dcTree : null; 2966 TreePath path = isValidDuo(duo) ? duo.treePath : null; 2967 if (!dcTreeCache.containsKey(element)) { 2968 if (docCommentTree != null && path != null) { 2969 configuration.workArounds.runDocLint(path); 2970 } 2971 dcTreeCache.put(element, duo); 2972 } 2973 return docCommentTree; 2974 } 2975 2976 private DocCommentDuo getDocCommentTuple(Element element) { 2977 // prevent nasty things downstream with overview element 2978 if (element.getKind() != ElementKind.OTHER) { 2979 TreePath path = getTreePath(element); 2980 if (path != null) { 2981 DocCommentTree docCommentTree = docTrees.getDocCommentTree(path); 2982 return new DocCommentDuo(path, docCommentTree); 2983 } 2984 } 2985 return null; 2986 } 2987 2988 boolean isValidDuo(DocCommentDuo duo) { 2989 return duo != null && duo.dcTree != null; 2990 } 2991 2992 public DocCommentTree getDocCommentTree(Element element) { 2993 CommentHelper ch = wksMap.get(element); 2994 if (ch != null) { 2995 return ch.dctree; 2996 } 2997 DocCommentTree dcTree = getDocCommentTree0(element); 2998 if (dcTree != null) { 2999 wksMap.put(element, new CommentHelper(configuration, element, getTreePath(element), dcTree)); 3000 } 3001 return dcTree; 3002 } 3003 3004 public List<? extends DocTree> getFullBody(Element element) { 3005 DocCommentTree docCommentTree = getDocCommentTree(element); 3006 return (docCommentTree == null) 3007 ? Collections.emptyList() 3008 : docCommentTree.getFullBody(); 3009 } 3010 3011 public List<? extends DocTree> getBody(Element element) { 3012 DocCommentTree docCommentTree = getDocCommentTree(element); 3013 return (docCommentTree == null) 3014 ? Collections.emptyList() 3015 : docCommentTree.getFullBody(); 3016 } 3017 3018 public List<? extends DocTree> getDeprecatedTrees(Element element) { 3019 return getBlockTags(element, DEPRECATED); 3020 } 3021 3022 public List<? extends DocTree> getSeeTrees(Element element) { 3023 return getBlockTags(element, SEE); 3024 } 3025 3026 public List<? extends DocTree> getSerialTrees(Element element) { 3027 return getBlockTags(element, SERIAL); 3028 } 3029 3030 public List<? extends DocTree> getSerialFieldTrees(VariableElement field) { 3031 return getBlockTags(field, DocTree.Kind.SERIAL_FIELD); 3032 } 3033 3034 public List<? extends DocTree> getThrowsTrees(Element element) { 3035 return getBlockTags(element, DocTree.Kind.EXCEPTION, DocTree.Kind.THROWS); 3036 } 3037 3038 public List<? extends DocTree> getTypeParamTrees(Element element) { 3039 return getParamTrees(element, true); 3040 } 3041 3042 public List<? extends DocTree> getParamTrees(Element element) { 3043 return getParamTrees(element, false); 3044 } 3045 3046 private List<? extends DocTree> getParamTrees(Element element, boolean isTypeParameters) { 3047 List<DocTree> out = new ArrayList<>(); 3048 for (DocTree dt : getBlockTags(element, PARAM)) { 3049 ParamTree pt = (ParamTree) dt; 3050 if (pt.isTypeParameter() == isTypeParameters) { 3051 out.add(dt); 3052 } 3053 } 3054 return out; 3055 } 3056 3057 public List<? extends DocTree> getReturnTrees(Element element) { 3058 List<DocTree> out = new ArrayList<>(); 3059 for (DocTree dt : getBlockTags(element, RETURN)) { 3060 out.add(dt); 3061 } 3062 return out; 3063 } 3064 3065 public List<? extends DocTree> getFirstSentenceTrees(Element element) { 3066 DocCommentTree dcTree = getDocCommentTree(element); 3067 if (dcTree == null) { 3068 return Collections.emptyList(); 3069 } 3070 List<DocTree> out = new ArrayList<>(); 3071 for (DocTree dt : dcTree.getFirstSentence()) { 3072 out.add(dt); 3073 } 3074 return out; 3075 } 3076 3077 public ModuleElement containingModule(Element e) { 3078 return elementUtils.getModuleOf(e); 3079 } 3080 3081 public PackageElement containingPackage(Element e) { 3082 return elementUtils.getPackageOf(e); 3083 } 3084 3085 public TypeElement getTopMostContainingTypeElement(Element e) { 3086 if (isPackage(e)) { 3087 return null; 3088 } 3089 TypeElement outer = getEnclosingTypeElement(e); 3090 if (outer == null) 3091 return (TypeElement)e; 3092 while (outer != null && outer.getNestingKind().isNested()) { 3093 outer = getEnclosingTypeElement(outer); 3094 } 3095 return outer; 3096 } 3097 3098 static class WeakSoftHashMap implements Map<Element, CommentHelper> { 3099 3100 private final WeakHashMap<Element, SoftReference<CommentHelper>> wkMap; 3101 private final Utils utils; 3102 public WeakSoftHashMap(Utils utils) { 3103 wkMap = new WeakHashMap<>(); 3104 this.utils = utils; 3105 } 3106 3107 @Override 3108 public boolean containsKey(Object key) { 3109 return wkMap.containsKey(key); 3110 } 3111 3112 @Override 3113 public Collection<CommentHelper> values() { 3114 Set<CommentHelper> out = new LinkedHashSet<>(); 3115 for (SoftReference<CommentHelper> v : wkMap.values()) { 3116 out.add(v.get()); 3117 } 3118 return out; 3119 } 3120 3121 @Override 3122 public boolean containsValue(Object value) { 3123 return wkMap.containsValue(new SoftReference<>((CommentHelper)value)); 3124 } 3125 3126 @Override 3127 public CommentHelper remove(Object key) { 3128 SoftReference<CommentHelper> value = wkMap.remove(key); 3129 return value == null ? null : value.get(); 3130 } 3131 3132 3133 @Override 3134 public CommentHelper put(Element key, CommentHelper value) { 3135 SoftReference<CommentHelper> nvalue = wkMap.put(key, new SoftReference<>(value)); 3136 return nvalue == null ? null : nvalue.get(); 3137 } 3138 3139 @Override 3140 public CommentHelper get(Object key) { 3141 SoftReference<CommentHelper> value = wkMap.get(key); 3142 return value == null ? null : value.get(); 3143 } 3144 3145 @Override 3146 public int size() { 3147 return wkMap.size(); 3148 } 3149 3150 @Override 3151 public boolean isEmpty() { 3152 return wkMap.isEmpty(); 3153 } 3154 3155 @Override 3156 public void clear() { 3157 wkMap.clear(); 3158 } 3159 3160 public CommentHelper computeIfAbsent(Element key) { 3161 if (wkMap.containsKey(key)) { 3162 SoftReference<CommentHelper> value = wkMap.get(key); 3163 if (value != null) { 3164 CommentHelper cvalue = value.get(); 3165 if (cvalue != null) { 3166 return cvalue; 3167 } 3168 } 3169 } 3170 CommentHelper newValue = new CommentHelper(utils.configuration, key, utils.getTreePath(key), 3171 utils.getDocCommentTree(key)); 3172 wkMap.put(key, new SoftReference<>(newValue)); 3173 return newValue; 3174 } 3175 3176 3177 @Override 3178 public void putAll(Map<? extends Element, ? extends CommentHelper> map) { 3179 for (Map.Entry<? extends Element, ? extends CommentHelper> entry : map.entrySet()) { 3180 put(entry.getKey(), entry.getValue()); 3181 } 3182 } 3183 3184 @Override 3185 public Set<Element> keySet() { 3186 return wkMap.keySet(); 3187 } 3188 3189 @Override 3190 public Set<Entry<Element, CommentHelper>> entrySet() { 3191 Set<Entry<Element, CommentHelper>> out = new LinkedHashSet<>(); 3192 for (Element e : wkMap.keySet()) { 3193 SimpleEntry<Element, CommentHelper> n = new SimpleEntry<>(e, get(e)); 3194 out.add(n); 3195 } 3196 return out; 3197 } 3198 } 3199} 3200