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