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