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