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