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