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