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