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