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