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