Utils.java revision 3896:8e4dbcb99277
1/* 2 * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.javadoc.internal.doclets.toolkit.util; 27 28import java.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 private 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 (elementUtils.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 compareNames(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 then simply compare the simple names, 1776 * comparing packages then simply compare the qualified names, otherwise 1777 * 1. if equal, then compare the ElementKind ex: Module, Package, Interface etc. 1778 * 2. sort on simple names of entities 1779 * 3a. if equal and if the type is of ExecutableElement(Constructor, Methods), 1780 * a case insensitive comparison of parameter the type signatures 1781 * 3b. if equal, case sensitive comparison of the type signatures 1782 * 4. finally, if equal, compare the FQNs of the entities 1783 * Iff comparing packages then simply sort on qualified names. 1784 * @return a comparator for index file use 1785 */ 1786 public Comparator<Element> makeIndexUseComparator() { 1787 return new Utils.ElementComparator<Element>() { 1788 /** 1789 * Compare two given elements, if comparing two modules, return the 1790 * comparison of SimpleName, if comparing two packages, return the 1791 * comparison of FullyQualifiedName, first sort on kinds, then on the 1792 * names, then on 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) && isModule(e2)) { 1804 return compareNames(e1, e2); 1805 } 1806 if (isPackage(e1) && isPackage(e2)) { 1807 return compareFullyQualifiedNames(e1, e2); 1808 } 1809 result = compareElementTypeKinds(e1, e2); 1810 if (result != 0) { 1811 return result; 1812 } 1813 result = compareNames(e1, e2); 1814 if (result != 0) { 1815 return result; 1816 } 1817 if (hasParameters(e1)) { 1818 List<? extends VariableElement> parameters1 = ((ExecutableElement)e1).getParameters(); 1819 List<? extends VariableElement> parameters2 = ((ExecutableElement)e2).getParameters(); 1820 result = compareParameters(false, parameters1, parameters2); 1821 if (result != 0) { 1822 return result; 1823 } 1824 result = compareParameters(true, parameters1, parameters2); 1825 if (result != 0) { 1826 return result; 1827 } 1828 } 1829 return compareFullyQualifiedNames(e1, e2); 1830 } 1831 }; 1832 } 1833 1834 /** 1835 * Compares the FullyQualifiedNames of two TypeMirrors 1836 * @return 1837 */ 1838 public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() { 1839 return (TypeMirror type1, TypeMirror type2) -> { 1840 String s1 = getQualifiedTypeName(type1); 1841 String s2 = getQualifiedTypeName(type2); 1842 return compareStrings(s1, s2); 1843 }; 1844 } 1845 1846 /** 1847 * Compares the SimpleNames of TypeMirrors if equal then the 1848 * FullyQualifiedNames of TypeMirrors. 1849 * 1850 * @return 1851 */ 1852 public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() { 1853 return (TypeMirror t1, TypeMirror t2) -> { 1854 int result = compareStrings(getTypeName(t1, false), getTypeName(t2, false)); 1855 if (result != 0) 1856 return result; 1857 return compareStrings(getQualifiedTypeName(t1), getQualifiedTypeName(t2)); 1858 }; 1859 } 1860 1861 /** 1862 * Get the qualified type name of a TypeMiror compatible with the Element's 1863 * getQualified name, returns the qualified name of the Reference type 1864 * otherwise the primitive name. 1865 * @param t the type whose name is to be obtained. 1866 * @return the fully qualified name of Reference type or the primitive name 1867 */ 1868 public String getQualifiedTypeName(TypeMirror t) { 1869 return new SimpleTypeVisitor9<String, Void>() { 1870 @Override 1871 public String visitDeclared(DeclaredType t, Void p) { 1872 return getFullyQualifiedName(t.asElement()); 1873 } 1874 1875 @Override 1876 public String visitArray(ArrayType t, Void p) { 1877 return visit(t.getComponentType()); 1878 } 1879 1880 @Override 1881 public String visitPrimitive(PrimitiveType t, Void p) { 1882 return t.toString(); 1883 } 1884 1885 @Override 1886 public String visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 1887 // The knee jerk reaction is to do this but don't!, as we would like 1888 // it to be compatible with the old world, now if we decide to do so 1889 // care must be taken to avoid collisions. 1890 // return getFullyQualifiedName(t.asElement()); 1891 return t.toString(); 1892 } 1893 1894 @Override 1895 protected String defaultAction(TypeMirror e, Void p) { 1896 throw new UnsupportedOperationException("should not happen"); 1897 } 1898 }.visit(t); 1899 } 1900 1901 /** 1902 * A generic utility which returns the fully qualified names of an entity, 1903 * if the entity is not qualifiable then its enclosing entity, it is upto 1904 * the caller to add the elements name as required. 1905 * @param e the element to get FQN for. 1906 * @return the name 1907 */ 1908 public String getFullyQualifiedName(Element e) { 1909 return getFullyQualifiedName(e, true); 1910 } 1911 1912 public String getFullyQualifiedName(Element e, final boolean outer) { 1913 return new SimpleElementVisitor9<String, Void>() { 1914 @Override 1915 public String visitPackage(PackageElement e, Void p) { 1916 return e.getQualifiedName().toString(); 1917 } 1918 1919 @Override 1920 public String visitType(TypeElement e, Void p) { 1921 return e.getQualifiedName().toString(); 1922 } 1923 1924 @Override 1925 protected String defaultAction(Element e, Void p) { 1926 return outer ? visit(e.getEnclosingElement()) : e.getSimpleName().toString(); 1927 } 1928 }.visit(e); 1929 } 1930 1931 /** 1932 * Comparator for ClassUse presentations, and sorts as follows: 1933 * 1. member names 1934 * 2. then fully qualified member names 1935 * 3. then parameter types if applicable 1936 * 4. finally the element kinds ie. package, class, interface etc. 1937 * @return a comparator to sort classes and members for class use 1938 */ 1939 public Comparator<Element> makeClassUseComparator() { 1940 return new Utils.ElementComparator<Element>() { 1941 /** 1942 * Compare two Elements, first sort on simple name, and if 1943 * applicable on the fully qualified name, and finally if applicable 1944 * on the parameter types. 1945 * @param e1 - an element. 1946 * @param e2 - an element. 1947 * @return a negative integer, zero, or a positive integer as the first 1948 * argument is less than, equal to, or greater than the second. 1949 */ 1950 @Override 1951 public int compare(Element e1, Element e2) { 1952 int result = compareNames(e1, e2); 1953 if (result != 0) { 1954 return result; 1955 } 1956 result = compareFullyQualifiedNames(e1, e2); 1957 if (result != 0) { 1958 return result; 1959 } 1960 if (hasParameters(e1) && hasParameters(e2)) { 1961 @SuppressWarnings("unchecked") 1962 List<VariableElement> parameters1 = (List<VariableElement>) ((ExecutableElement)e1).getParameters(); 1963 @SuppressWarnings("unchecked") 1964 List<VariableElement> parameters2 = (List<VariableElement>) ((ExecutableElement)e2).getParameters(); 1965 result = compareParameters(false, parameters1, parameters2); 1966 if (result != 0) { 1967 return result; 1968 } 1969 result = compareParameters(true, parameters1, parameters2); 1970 } 1971 if (result != 0) { 1972 return result; 1973 } 1974 return compareElementTypeKinds(e1, e2); 1975 } 1976 }; 1977 } 1978 1979 /** 1980 * A general purpose comparator to sort Element entities, basically provides the building blocks 1981 * for creating specific comparators for an use-case. 1982 * @param <T> an Element 1983 */ 1984 private abstract class ElementComparator<T extends Element> implements Comparator<Element> { 1985 /** 1986 * compares two parameter arrays by first comparing the length of the arrays, and 1987 * then each Type of the parameter in the array. 1988 * @param params1 the first parameter array. 1989 * @param params2 the first parameter array. 1990 * @return a negative integer, zero, or a positive integer as the first 1991 * argument is less than, equal to, or greater than the second. 1992 */ 1993 final EnumMap<ElementKind, Integer> elementKindOrder; 1994 public ElementComparator() { 1995 elementKindOrder = new EnumMap<>(ElementKind.class); 1996 elementKindOrder.put(ElementKind.MODULE, 0); 1997 elementKindOrder.put(ElementKind.PACKAGE, 1); 1998 elementKindOrder.put(ElementKind.CLASS, 2); 1999 elementKindOrder.put(ElementKind.ENUM, 3); 2000 elementKindOrder.put(ElementKind.ENUM_CONSTANT, 4); 2001 elementKindOrder.put(ElementKind.INTERFACE, 5); 2002 elementKindOrder.put(ElementKind.ANNOTATION_TYPE, 6); 2003 elementKindOrder.put(ElementKind.FIELD, 7); 2004 elementKindOrder.put(ElementKind.CONSTRUCTOR, 8); 2005 elementKindOrder.put(ElementKind.METHOD, 9); 2006 } 2007 2008 protected int compareParameters(boolean caseSensitive, List<? extends VariableElement> params1, 2009 List<? extends VariableElement> params2) { 2010 2011 return compareStrings(caseSensitive, getParametersAsString(params1), 2012 getParametersAsString(params2)); 2013 } 2014 2015 String getParametersAsString(List<? extends VariableElement> params) { 2016 StringBuilder sb = new StringBuilder(); 2017 for (VariableElement param : params) { 2018 TypeMirror t = param.asType(); 2019 // prefix P for primitive and R for reference types, thus items will 2020 // be ordered lexically and correctly. 2021 sb.append(getTypeCode(t)).append("-").append(t).append("-"); 2022 } 2023 return sb.toString(); 2024 } 2025 2026 private String getTypeCode(TypeMirror t) { 2027 return new SimpleTypeVisitor9<String, Void>() { 2028 2029 @Override 2030 public String visitPrimitive(PrimitiveType t, Void p) { 2031 return "P"; 2032 } 2033 @Override 2034 public String visitArray(ArrayType t, Void p) { 2035 return visit(t.getComponentType()); 2036 } 2037 @Override 2038 protected String defaultAction(TypeMirror e, Void p) { 2039 return "R"; 2040 } 2041 2042 }.visit(t); 2043 } 2044 2045 /** 2046 * Compares two Elements, typically the name of a method, 2047 * field or constructor. 2048 * @param e1 the first Element. 2049 * @param e2 the second Element. 2050 * @return a negative integer, zero, or a positive integer as the first 2051 * argument is less than, equal to, or greater than the second. 2052 */ 2053 protected int compareNames(Element e1, Element e2) { 2054 return compareStrings(getSimpleName(e1), getSimpleName(e2)); 2055 } 2056 2057 /** 2058 * Compares the fully qualified names of the entities 2059 * @param e1 the first Element. 2060 * @param e2 the first Element. 2061 * @return a negative integer, zero, or a positive integer as the first 2062 * argument is less than, equal to, or greater than the second. 2063 */ 2064 protected int compareFullyQualifiedNames(Element e1, Element e2) { 2065 // add simplename to be compatible 2066 String thisElement = getFullyQualifiedName(e1); 2067 String thatElement = getFullyQualifiedName(e2); 2068 return compareStrings(thisElement, thatElement); 2069 } 2070 protected int compareElementTypeKinds(Element e1, Element e2) { 2071 return Integer.compare(elementKindOrder.get(e1.getKind()), 2072 elementKindOrder.get(e2.getKind())); 2073 } 2074 boolean hasParameters(Element e) { 2075 return new SimpleElementVisitor9<Boolean, Void>() { 2076 @Override 2077 public Boolean visitExecutable(ExecutableElement e, Void p) { 2078 return true; 2079 } 2080 2081 @Override 2082 protected Boolean defaultAction(Element e, Void p) { 2083 return false; 2084 } 2085 2086 }.visit(e); 2087 } 2088 2089 /** 2090 * The fully qualified names of the entities, used solely by the comparator. 2091 * 2092 * @param p1 the first Element. 2093 * @param p2 the first Element. 2094 * @return a negative integer, zero, or a positive integer as the first argument is less 2095 * than, equal to, or greater than the second. 2096 */ 2097 private String getFullyQualifiedName(Element e) { 2098 return new SimpleElementVisitor9<String, Void>() { 2099 @Override 2100 public String visitModule(ModuleElement e, Void p) { 2101 return e.getQualifiedName().toString(); 2102 } 2103 2104 @Override 2105 public String visitPackage(PackageElement e, Void p) { 2106 return e.getQualifiedName().toString(); 2107 } 2108 2109 @Override 2110 public String visitExecutable(ExecutableElement e, Void p) { 2111 // For backward compatibility 2112 return getFullyQualifiedName(e.getEnclosingElement()) 2113 + "." + e.getSimpleName().toString(); 2114 } 2115 2116 @Override 2117 public String visitType(TypeElement e, Void p) { 2118 return e.getQualifiedName().toString(); 2119 } 2120 2121 @Override 2122 protected String defaultAction(Element e, Void p) { 2123 return getEnclosingTypeElement(e).getQualifiedName().toString() 2124 + "." + e.getSimpleName().toString(); 2125 } 2126 }.visit(e); 2127 } 2128 } 2129 2130 public Iterable<TypeElement> getEnclosedTypeElements(PackageElement pkg) { 2131 List<TypeElement> out = getInterfaces(pkg); 2132 out.addAll(getClasses(pkg)); 2133 out.addAll(getEnums(pkg)); 2134 out.addAll(getAnnotationTypes(pkg)); 2135 return out; 2136 } 2137 2138 // Element related methods 2139 public List<Element> getAnnotationMembers(TypeElement aClass) { 2140 List<Element> members = getAnnotationFields(aClass); 2141 members.addAll(getAnnotationMethods(aClass)); 2142 return members; 2143 } 2144 2145 public List<Element> getAnnotationFields(TypeElement aClass) { 2146 return getItems0(aClass, true, FIELD); 2147 } 2148 2149 List<Element> getAnnotationFieldsUnfiltered(TypeElement aClass) { 2150 return getItems0(aClass, true, FIELD); 2151 } 2152 2153 public List<Element> getAnnotationMethods(TypeElement aClass) { 2154 return getItems0(aClass, true, METHOD); 2155 } 2156 2157 public List<TypeElement> getAnnotationTypes(Element e) { 2158 return convertToTypeElement(getItems(e, true, ANNOTATION_TYPE)); 2159 } 2160 2161 public List<TypeElement> getAnnotationTypesUnfiltered(Element e) { 2162 return convertToTypeElement(getItems(e, false, ANNOTATION_TYPE)); 2163 } 2164 2165 public List<VariableElement> getFields(Element e) { 2166 return convertToVariableElement(getItems(e, true, FIELD)); 2167 } 2168 2169 public List<VariableElement> getFieldsUnfiltered(Element e) { 2170 return convertToVariableElement(getItems(e, false, FIELD)); 2171 } 2172 2173 public List<TypeElement> getClasses(Element e) { 2174 return convertToTypeElement(getItems(e, true, CLASS)); 2175 } 2176 2177 public List<TypeElement> getClassesUnfiltered(Element e) { 2178 return convertToTypeElement(getItems(e, false, CLASS)); 2179 } 2180 2181 public List<ExecutableElement> getConstructors(Element e) { 2182 return convertToExecutableElement(getItems(e, true, CONSTRUCTOR)); 2183 } 2184 2185 public List<ExecutableElement> getMethods(Element e) { 2186 return convertToExecutableElement(getItems(e, true, METHOD)); 2187 } 2188 2189 List<ExecutableElement> getMethodsUnfiltered(Element e) { 2190 return convertToExecutableElement(getItems(e, false, METHOD)); 2191 } 2192 2193 public int getOrdinalValue(VariableElement member) { 2194 if (member == null || member.getKind() != ENUM_CONSTANT) { 2195 throw new IllegalArgumentException("must be an enum constant: " + member); 2196 } 2197 return member.getEnclosingElement().getEnclosedElements().indexOf(member); 2198 } 2199 2200 private Map<ModuleElement, Set<PackageElement>> modulePackageMap = null; 2201 public Map<ModuleElement, Set<PackageElement>> getModulePackageMap() { 2202 if (modulePackageMap == null) { 2203 modulePackageMap = new HashMap<>(); 2204 Set<PackageElement> pkgs = configuration.getIncludedPackageElements(); 2205 pkgs.forEach((pkg) -> { 2206 ModuleElement mod = elementUtils.getModuleOf(pkg); 2207 modulePackageMap.computeIfAbsent(mod, m -> new HashSet<>()).add(pkg); 2208 }); 2209 } 2210 return modulePackageMap; 2211 } 2212 2213 public Map<ModuleElement, String> getDependentModules(ModuleElement mdle) { 2214 Map<ModuleElement, String> result = new TreeMap<>(makeModuleComparator()); 2215 Deque<ModuleElement> queue = new ArrayDeque<>(); 2216 // get all the requires for the element in question 2217 for (RequiresDirective rd : ElementFilter.requiresIn(mdle.getDirectives())) { 2218 ModuleElement dep = rd.getDependency(); 2219 // add the dependency to work queue 2220 if (!result.containsKey(dep)) { 2221 if (rd.isTransitive()) { 2222 queue.addLast(dep); 2223 } 2224 } 2225 // add all exports for the primary module 2226 result.put(rd.getDependency(), getModifiers(rd)); 2227 } 2228 2229 // add only requires public for subsequent module dependencies 2230 for (ModuleElement m = queue.poll(); m != null; m = queue.poll()) { 2231 for (RequiresDirective rd : ElementFilter.requiresIn(m.getDirectives())) { 2232 ModuleElement dep = rd.getDependency(); 2233 if (!result.containsKey(dep)) { 2234 if (rd.isTransitive()) { 2235 result.put(dep, getModifiers(rd)); 2236 queue.addLast(dep); 2237 } 2238 } 2239 } 2240 } 2241 return result; 2242 } 2243 2244 public String getModifiers(RequiresDirective rd) { 2245 StringBuilder modifiers = new StringBuilder(); 2246 String sep=""; 2247 if (rd.isTransitive()) { 2248 modifiers.append("transitive"); 2249 sep = " "; 2250 } 2251 if (rd.isStatic()) { 2252 modifiers.append(sep); 2253 modifiers.append("static"); 2254 } 2255 return (modifiers.length() == 0) ? " " : modifiers.toString(); 2256 } 2257 2258 public long getLineNumber(Element e) { 2259 TreePath path = getTreePath(e); 2260 if (path == null) { // maybe null if synthesized 2261 TypeElement encl = getEnclosingTypeElement(e); 2262 path = getTreePath(encl); 2263 } 2264 CompilationUnitTree cu = path.getCompilationUnit(); 2265 LineMap lineMap = cu.getLineMap(); 2266 DocSourcePositions spos = docTrees.getSourcePositions(); 2267 long pos = spos.getStartPosition(cu, path.getLeaf()); 2268 return lineMap.getLineNumber(pos); 2269 } 2270 2271 public List<ExecutableElement> convertToExecutableElement(List<Element> list) { 2272 List<ExecutableElement> out = new ArrayList<>(list.size()); 2273 for (Element e : list) { 2274 out.add((ExecutableElement)e); 2275 } 2276 return out; 2277 } 2278 2279 public List<TypeElement> convertToTypeElement(List<Element> list) { 2280 List<TypeElement> out = new ArrayList<>(list.size()); 2281 for (Element e : list) { 2282 out.add((TypeElement)e); 2283 } 2284 return out; 2285 } 2286 2287 public List<VariableElement> convertToVariableElement(List<Element> list) { 2288 List<VariableElement> out = new ArrayList<>(list.size()); 2289 for (Element e : list) { 2290 out.add((VariableElement) e); 2291 } 2292 return out; 2293 } 2294 2295 public List<TypeElement> getInterfaces(Element e) { 2296 return convertToTypeElement(getItems(e, true, INTERFACE)); 2297 } 2298 2299 public List<TypeElement> getInterfacesUnfiltered(Element e) { 2300 return convertToTypeElement(getItems(e, false, INTERFACE)); 2301 } 2302 2303 List<Element> getNestedClasses(TypeElement e) { 2304 List<Element> result = new ArrayList<>(); 2305 recursiveGetItems(result, e, true, CLASS); 2306 return result; 2307 } 2308 2309 List<Element> getNestedClassesUnfiltered(TypeElement e) { 2310 List<Element> result = new ArrayList<>(); 2311 recursiveGetItems(result, e, false, CLASS); 2312 return result; 2313 } 2314 2315 public List<Element> getEnumConstants(Element e) { 2316 return getItems(e, true, ENUM_CONSTANT); 2317 } 2318 2319 public List<TypeElement> getEnums(Element e) { 2320 return convertToTypeElement(getItems(e, true, ENUM)); 2321 } 2322 2323 public List<TypeElement> getEnumsUnfiltered(Element e) { 2324 return convertToTypeElement(getItems(e, false, ENUM)); 2325 } 2326 2327 public SortedSet<TypeElement> getAllClassesUnfiltered(Element e) { 2328 List<TypeElement> clist = getClassesUnfiltered(e); 2329 clist.addAll(getInterfacesUnfiltered(e)); 2330 clist.addAll(getAnnotationTypesUnfiltered(e)); 2331 SortedSet<TypeElement> oset = new TreeSet<>(makeGeneralPurposeComparator()); 2332 oset.addAll(clist); 2333 return oset; 2334 } 2335 2336 private final HashMap<Element, SortedSet<TypeElement>> cachedClasses = new HashMap<>(); 2337 /** 2338 * Returns a list containing classes and interfaces, 2339 * including annotation types. 2340 * @param e Element 2341 * @return List 2342 */ 2343 public SortedSet<TypeElement> getAllClasses(Element e) { 2344 SortedSet<TypeElement> oset = cachedClasses.get(e); 2345 if (oset != null) 2346 return oset; 2347 List<TypeElement> clist = getClasses(e); 2348 clist.addAll(getInterfaces(e)); 2349 clist.addAll(getAnnotationTypes(e)); 2350 clist.addAll(getEnums(e)); 2351 oset = new TreeSet<>(makeGeneralPurposeComparator()); 2352 oset.addAll(clist); 2353 cachedClasses.put(e, oset); 2354 return oset; 2355 } 2356 2357 /* 2358 * Get all the elements unfiltered and filter them finally based 2359 * on its visibility, this works differently from the other getters. 2360 */ 2361 private List<TypeElement> getInnerClasses(Element e, boolean filter) { 2362 List<TypeElement> olist = new ArrayList<>(); 2363 for (TypeElement te : getClassesUnfiltered(e)) { 2364 if (!filter || configuration.docEnv.isSelected(te)) { 2365 olist.add(te); 2366 } 2367 } 2368 for (TypeElement te : getInterfacesUnfiltered(e)) { 2369 if (!filter || configuration.docEnv.isSelected(te)) { 2370 olist.add(te); 2371 } 2372 } 2373 for (TypeElement te : getAnnotationTypesUnfiltered(e)) { 2374 if (!filter || configuration.docEnv.isSelected(te)) { 2375 olist.add(te); 2376 } 2377 } 2378 for (TypeElement te : getEnumsUnfiltered(e)) { 2379 if (!filter || configuration.docEnv.isSelected(te)) { 2380 olist.add(te); 2381 } 2382 } 2383 return olist; 2384 } 2385 2386 public List<TypeElement> getInnerClasses(Element e) { 2387 return getInnerClasses(e, true); 2388 } 2389 2390 public List<TypeElement> getInnerClassesUnfiltered(Element e) { 2391 return getInnerClasses(e, false); 2392 } 2393 2394 /** 2395 * Returns a list of classes that are not errors or exceptions 2396 * @param e Element 2397 * @return List 2398 */ 2399 public List<TypeElement> getOrdinaryClasses(Element e) { 2400 return getClasses(e).stream() 2401 .filter(te -> (!isException(te) && !isError(te))) 2402 .collect(Collectors.toList()); 2403 } 2404 2405 public List<TypeElement> getErrors(Element e) { 2406 return getClasses(e) 2407 .stream() 2408 .filter(this::isError) 2409 .collect(Collectors.toList()); 2410 } 2411 2412 public List<TypeElement> getExceptions(Element e) { 2413 return getClasses(e) 2414 .stream() 2415 .filter(this::isException) 2416 .collect(Collectors.toList()); 2417 } 2418 2419 List<Element> getItems(Element e, boolean filter, ElementKind select) { 2420 List<Element> elements = new ArrayList<>(); 2421 // maintain backward compatibility by returning a null list, see AnnotationDocType.methods(). 2422 if (configuration.backwardCompatibility && e.getKind() == ANNOTATION_TYPE) 2423 return elements; 2424 return new SimpleElementVisitor9<List<Element>, Void>() { 2425 2426 @Override 2427 public List<Element> visitPackage(PackageElement e, Void p) { 2428 recursiveGetItems(elements, e, filter, select); 2429 return elements; 2430 } 2431 2432 @Override 2433 protected List<Element> defaultAction(Element e0, Void p) { 2434 return getItems0(e0, filter, select); 2435 } 2436 2437 }.visit(e); 2438 } 2439 2440 EnumSet<ElementKind> nestedKinds = EnumSet.of(ANNOTATION_TYPE, CLASS, ENUM, INTERFACE); 2441 2442 void recursiveGetItems(Collection<Element> list, Element e, boolean filter, ElementKind... select) { 2443 list.addAll(getItems0(e, filter, select)); 2444 List<Element> classes = getItems0(e, filter, nestedKinds); 2445 for (Element c : classes) { 2446 list.addAll(getItems0(c, filter, select)); 2447 if (isTypeElement(c)) { 2448 recursiveGetItems(list, c, filter, select); 2449 } 2450 } 2451 } 2452 2453 private List<Element> getItems0(Element te, boolean filter, ElementKind... select) { 2454 EnumSet<ElementKind> kinds = EnumSet.copyOf(Arrays.asList(select)); 2455 return getItems0(te, filter, kinds); 2456 } 2457 2458 private List<Element> getItems0(Element te, boolean filter, Set<ElementKind> kinds) { 2459 List<Element> elements = new ArrayList<>(); 2460 for (Element e : te.getEnclosedElements()) { 2461 if (kinds.contains(e.getKind())) { 2462 if (!filter || shouldDocument(e)) { 2463 elements.add(e); 2464 } 2465 } 2466 } 2467 return elements; 2468 } 2469 2470 private SimpleElementVisitor9<Boolean, Void> shouldDocumentVisitor = null; 2471 private boolean shouldDocument(Element e) { 2472 if (shouldDocumentVisitor == null) { 2473 shouldDocumentVisitor = new SimpleElementVisitor9<Boolean, Void>() { 2474 private boolean hasSource(TypeElement e) { 2475 return configuration.docEnv.getFileKind(e) == 2476 javax.tools.JavaFileObject.Kind.SOURCE; 2477 } 2478 2479 // handle types 2480 @Override 2481 public Boolean visitType(TypeElement e, Void p) { 2482 return configuration.docEnv.isSelected(e) && hasSource(e); 2483 } 2484 2485 // handle everything else 2486 @Override 2487 protected Boolean defaultAction(Element e, Void p) { 2488 return configuration.docEnv.isSelected(e); 2489 } 2490 2491 @Override 2492 public Boolean visitUnknown(Element e, Void p) { 2493 throw new AssertionError("unkown element: " + p); 2494 } 2495 }; 2496 } 2497 return shouldDocumentVisitor.visit(e); 2498 } 2499 2500 /* 2501 * nameCache is maintained for improving the comparator 2502 * performance, noting that the Collator used by the comparators 2503 * use Strings, as of this writing. 2504 * TODO: when those APIs handle charSequences, the use of 2505 * this nameCache must be re-investigated and removed. 2506 */ 2507 private final Map<Element, String> nameCache = new LinkedHashMap<>(); 2508 2509 /** 2510 * Returns the name of the element after the last dot of the package name. 2511 * This emulates the behavior of the old doclet. 2512 * @param e an element whose name is required 2513 * @return the name 2514 */ 2515 public String getSimpleName(Element e) { 2516 return nameCache.computeIfAbsent(e, this::getSimpleName0); 2517 } 2518 2519 private SimpleElementVisitor9<String, Void> snvisitor = null; 2520 2521 private String getSimpleName0(Element e) { 2522 if (snvisitor == null) { 2523 snvisitor = new SimpleElementVisitor9<String, Void>() { 2524 @Override 2525 public String visitModule(ModuleElement e, Void p) { 2526 return e.getSimpleName().toString(); 2527 } 2528 2529 @Override 2530 public String visitType(TypeElement e, Void p) { 2531 StringBuilder sb = new StringBuilder(e.getSimpleName()); 2532 Element enclosed = e.getEnclosingElement(); 2533 while (enclosed != null 2534 && (enclosed.getKind().isClass() || enclosed.getKind().isInterface())) { 2535 sb.insert(0, enclosed.getSimpleName() + "."); 2536 enclosed = enclosed.getEnclosingElement(); 2537 } 2538 return sb.toString(); 2539 } 2540 2541 @Override 2542 public String visitExecutable(ExecutableElement e, Void p) { 2543 if (e.getKind() == CONSTRUCTOR || e.getKind() == STATIC_INIT) { 2544 return e.getEnclosingElement().getSimpleName().toString(); 2545 } 2546 return e.getSimpleName().toString(); 2547 } 2548 2549 @Override 2550 protected String defaultAction(Element e, Void p) { 2551 return e.getSimpleName().toString(); 2552 } 2553 }; 2554 } 2555 return snvisitor.visit(e); 2556 } 2557 2558 public TypeElement getEnclosingTypeElement(Element e) { 2559 if (e.getKind() == ElementKind.PACKAGE) 2560 return null; 2561 Element encl = e.getEnclosingElement(); 2562 ElementKind kind = encl.getKind(); 2563 if (kind == ElementKind.PACKAGE) 2564 return null; 2565 while (!(kind.isClass() || kind.isInterface())) { 2566 encl = encl.getEnclosingElement(); 2567 } 2568 return (TypeElement)encl; 2569 } 2570 2571 private ConstantValueExpression cve = null; 2572 2573 public String constantValueExpresion(VariableElement ve) { 2574 if (cve == null) 2575 cve = new ConstantValueExpression(); 2576 return cve.constantValueExpression(configuration.workArounds, ve); 2577 } 2578 2579 private static class ConstantValueExpression { 2580 public String constantValueExpression(WorkArounds workArounds, VariableElement ve) { 2581 return new TypeKindVisitor9<String, Object>() { 2582 /* TODO: we need to fix this correctly. 2583 * we have a discrepancy here, note the use of getConstValue 2584 * vs. getConstantValue, at some point we need to use 2585 * getConstantValue. 2586 * In the legacy world byte and char primitives appear as Integer values, 2587 * thus a byte value of 127 will appear as 127, but in the new world, 2588 * a byte value appears as Byte thus 0x7f will be printed, similarly 2589 * chars will be translated to \n, \r etc. however, in the new world, 2590 * they will be printed as decimal values. The new world is correct, 2591 * and we should fix this by using getConstantValue and the visitor to 2592 * address this in the future. 2593 */ 2594 @Override 2595 public String visitPrimitiveAsBoolean(PrimitiveType t, Object val) { 2596 return (int)val == 0 ? "false" : "true"; 2597 } 2598 2599 @Override 2600 public String visitPrimitiveAsDouble(PrimitiveType t, Object val) { 2601 return sourceForm(((Double)val), 'd'); 2602 } 2603 2604 @Override 2605 public String visitPrimitiveAsFloat(PrimitiveType t, Object val) { 2606 return sourceForm(((Float)val).doubleValue(), 'f'); 2607 } 2608 2609 @Override 2610 public String visitPrimitiveAsLong(PrimitiveType t, Object val) { 2611 return val + "L"; 2612 } 2613 2614 @Override 2615 protected String defaultAction(TypeMirror e, Object val) { 2616 if (val == null) 2617 return null; 2618 else if (val instanceof Character) 2619 return sourceForm(((Character)val)); 2620 else if (val instanceof Byte) 2621 return sourceForm(((Byte)val)); 2622 else if (val instanceof String) 2623 return sourceForm((String)val); 2624 return val.toString(); // covers int, short 2625 } 2626 }.visit(ve.asType(), workArounds.getConstValue(ve)); 2627 } 2628 2629 // where 2630 private String sourceForm(double v, char suffix) { 2631 if (Double.isNaN(v)) 2632 return "0" + suffix + "/0" + suffix; 2633 if (v == Double.POSITIVE_INFINITY) 2634 return "1" + suffix + "/0" + suffix; 2635 if (v == Double.NEGATIVE_INFINITY) 2636 return "-1" + suffix + "/0" + suffix; 2637 return v + (suffix == 'f' || suffix == 'F' ? "" + suffix : ""); 2638 } 2639 2640 private String sourceForm(char c) { 2641 StringBuilder buf = new StringBuilder(8); 2642 buf.append('\''); 2643 sourceChar(c, buf); 2644 buf.append('\''); 2645 return buf.toString(); 2646 } 2647 2648 private String sourceForm(byte c) { 2649 return "0x" + Integer.toString(c & 0xff, 16); 2650 } 2651 2652 private String sourceForm(String s) { 2653 StringBuilder buf = new StringBuilder(s.length() + 5); 2654 buf.append('\"'); 2655 for (int i=0; i<s.length(); i++) { 2656 char c = s.charAt(i); 2657 sourceChar(c, buf); 2658 } 2659 buf.append('\"'); 2660 return buf.toString(); 2661 } 2662 2663 private void sourceChar(char c, StringBuilder buf) { 2664 switch (c) { 2665 case '\b': buf.append("\\b"); return; 2666 case '\t': buf.append("\\t"); return; 2667 case '\n': buf.append("\\n"); return; 2668 case '\f': buf.append("\\f"); return; 2669 case '\r': buf.append("\\r"); return; 2670 case '\"': buf.append("\\\""); return; 2671 case '\'': buf.append("\\\'"); return; 2672 case '\\': buf.append("\\\\"); return; 2673 default: 2674 if (isPrintableAscii(c)) { 2675 buf.append(c); return; 2676 } 2677 unicodeEscape(c, buf); 2678 return; 2679 } 2680 } 2681 2682 private void unicodeEscape(char c, StringBuilder buf) { 2683 final String chars = "0123456789abcdef"; 2684 buf.append("\\u"); 2685 buf.append(chars.charAt(15 & (c>>12))); 2686 buf.append(chars.charAt(15 & (c>>8))); 2687 buf.append(chars.charAt(15 & (c>>4))); 2688 buf.append(chars.charAt(15 & (c>>0))); 2689 } 2690 private boolean isPrintableAscii(char c) { 2691 return c >= ' ' && c <= '~'; 2692 } 2693 } 2694 2695 public boolean isEnclosingPackageIncluded(TypeElement te) { 2696 return isIncluded(containingPackage(te)); 2697 } 2698 2699 public boolean isIncluded(Element e) { 2700 return configuration.docEnv.isIncluded(e); 2701 } 2702 2703 private SimpleElementVisitor9<Boolean, Void> specifiedVisitor = null; 2704 public boolean isSpecified(Element e) { 2705 if (specifiedVisitor == null) { 2706 specifiedVisitor = new SimpleElementVisitor9<Boolean, Void>() { 2707 @Override 2708 public Boolean visitModule(ModuleElement e, Void p) { 2709 return configuration.getSpecifiedModuleElements().contains(e); 2710 } 2711 2712 @Override 2713 public Boolean visitPackage(PackageElement e, Void p) { 2714 return configuration.getSpecifiedPackageElements().contains(e); 2715 } 2716 2717 @Override 2718 public Boolean visitType(TypeElement e, Void p) { 2719 return configuration.getSpecifiedTypeElements().contains(e); 2720 } 2721 2722 @Override 2723 protected Boolean defaultAction(Element e, Void p) { 2724 return false; 2725 } 2726 }; 2727 } 2728 return specifiedVisitor.visit(e); 2729 } 2730 2731 /** 2732 * package name, an unnamed package is returned as <Unnamed> 2733 * @param pkg 2734 * @return 2735 */ 2736 public String getPackageName(PackageElement pkg) { 2737 if (pkg == null || pkg.isUnnamed()) { 2738 return DocletConstants.DEFAULT_PACKAGE_NAME; 2739 } 2740 return pkg.getQualifiedName().toString(); 2741 } 2742 2743 public boolean isAttribute(DocTree doctree) { 2744 return isKind(doctree, ATTRIBUTE); 2745 } 2746 2747 public boolean isAuthor(DocTree doctree) { 2748 return isKind(doctree, AUTHOR); 2749 } 2750 2751 public boolean isComment(DocTree doctree) { 2752 return isKind(doctree, COMMENT); 2753 } 2754 2755 public boolean isDeprecated(DocTree doctree) { 2756 return isKind(doctree, DEPRECATED); 2757 } 2758 2759 public boolean isDocComment(DocTree doctree) { 2760 return isKind(doctree, DOC_COMMENT); 2761 } 2762 2763 public boolean isDocRoot(DocTree doctree) { 2764 return isKind(doctree, DOC_ROOT); 2765 } 2766 2767 public boolean isEndElement(DocTree doctree) { 2768 return isKind(doctree, END_ELEMENT); 2769 } 2770 2771 public boolean isEntity(DocTree doctree) { 2772 return isKind(doctree, ENTITY); 2773 } 2774 2775 public boolean isErroneous(DocTree doctree) { 2776 return isKind(doctree, ERRONEOUS); 2777 } 2778 2779 public boolean isException(DocTree doctree) { 2780 return isKind(doctree, EXCEPTION); 2781 } 2782 2783 public boolean isIdentifier(DocTree doctree) { 2784 return isKind(doctree, IDENTIFIER); 2785 } 2786 2787 public boolean isInheritDoc(DocTree doctree) { 2788 return isKind(doctree, INHERIT_DOC); 2789 } 2790 2791 public boolean isLink(DocTree doctree) { 2792 return isKind(doctree, LINK); 2793 } 2794 2795 public boolean isLinkPlain(DocTree doctree) { 2796 return isKind(doctree, LINK_PLAIN); 2797 } 2798 2799 public boolean isLiteral(DocTree doctree) { 2800 return isKind(doctree, LITERAL); 2801 } 2802 2803 public boolean isOther(DocTree doctree) { 2804 return doctree.getKind() == DocTree.Kind.OTHER; 2805 } 2806 2807 public boolean isParam(DocTree doctree) { 2808 return isKind(doctree, PARAM); 2809 } 2810 2811 public boolean isReference(DocTree doctree) { 2812 return isKind(doctree, REFERENCE); 2813 } 2814 2815 public boolean isReturn(DocTree doctree) { 2816 return isKind(doctree, RETURN); 2817 } 2818 2819 public boolean isSee(DocTree doctree) { 2820 return isKind(doctree, SEE); 2821 } 2822 2823 public boolean isSerial(DocTree doctree) { 2824 return isKind(doctree, SERIAL); 2825 } 2826 2827 public boolean isSerialData(DocTree doctree) { 2828 return isKind(doctree, SERIAL_DATA); 2829 } 2830 2831 public boolean isSerialField(DocTree doctree) { 2832 return isKind(doctree, SERIAL_FIELD); 2833 } 2834 2835 public boolean isSince(DocTree doctree) { 2836 return isKind(doctree, SINCE); 2837 } 2838 2839 public boolean isStartElement(DocTree doctree) { 2840 return isKind(doctree, START_ELEMENT); 2841 } 2842 2843 public boolean isText(DocTree doctree) { 2844 return isKind(doctree, TEXT); 2845 } 2846 2847 public boolean isThrows(DocTree doctree) { 2848 return isKind(doctree, THROWS); 2849 } 2850 2851 public boolean isUnknownBlockTag(DocTree doctree) { 2852 return isKind(doctree, UNKNOWN_BLOCK_TAG); 2853 } 2854 2855 public boolean isUnknownInlineTag(DocTree doctree) { 2856 return isKind(doctree, UNKNOWN_INLINE_TAG); 2857 } 2858 2859 public boolean isValue(DocTree doctree) { 2860 return isKind(doctree, VALUE); 2861 } 2862 2863 public boolean isVersion(DocTree doctree) { 2864 return isKind(doctree, VERSION); 2865 } 2866 2867 private boolean isKind(DocTree doctree, DocTree.Kind match) { 2868 return doctree.getKind() == match; 2869 } 2870 2871 private final WeakSoftHashMap wksMap = new WeakSoftHashMap(this); 2872 2873 public CommentHelper getCommentHelper(Element element) { 2874 return wksMap.computeIfAbsent(element); 2875 } 2876 2877 public void removeCommentHelper(Element element) { 2878 wksMap.remove(element); 2879 } 2880 2881 public List<? extends DocTree> filteredList(List<? extends DocTree> dlist, DocTree.Kind... select) { 2882 List<DocTree> list = new ArrayList<>(dlist.size()); 2883 if (select == null) 2884 return dlist; 2885 for (DocTree dt : dlist) { 2886 if (dt.getKind() != ERRONEOUS) { 2887 for (DocTree.Kind kind : select) { 2888 if (dt.getKind() == kind) { 2889 list.add(dt); 2890 } 2891 } 2892 } 2893 } 2894 return list; 2895 } 2896 2897 private List<? extends DocTree> getBlockTags0(Element element, DocTree.Kind... kinds) { 2898 DocCommentTree dcTree = getDocCommentTree(element); 2899 if (dcTree == null) 2900 return Collections.emptyList(); 2901 2902 return filteredList(dcTree.getBlockTags(), kinds); 2903 } 2904 2905 public List<? extends DocTree> getBlockTags(Element element) { 2906 return getBlockTags0(element, (Kind[]) null); 2907 } 2908 2909 public List<? extends DocTree> getBlockTags(Element element, DocTree.Kind... kinds) { 2910 return getBlockTags0(element, kinds); 2911 } 2912 2913 public List<? extends DocTree> getBlockTags(Element element, String tagName) { 2914 DocTree.Kind kind = null; 2915 switch (tagName) { 2916 case "author": 2917 case "deprecated": 2918 case "hidden": 2919 case "param": 2920 case "return": 2921 case "see": 2922 case "serial": 2923 case "since": 2924 case "throws": 2925 case "exception": 2926 case "version": 2927 kind = DocTree.Kind.valueOf(tagName.toUpperCase()); 2928 return getBlockTags(element, kind); 2929 case "serialData": 2930 kind = SERIAL_DATA; 2931 return getBlockTags(element, kind); 2932 case "serialField": 2933 kind = SERIAL_FIELD; 2934 return getBlockTags(element, kind); 2935 default: 2936 kind = DocTree.Kind.UNKNOWN_BLOCK_TAG; 2937 break; 2938 } 2939 List<? extends DocTree> blockTags = getBlockTags(element, kind); 2940 List<DocTree> out = new ArrayList<>(); 2941 String tname = tagName.startsWith("@") ? tagName.substring(1) : tagName; 2942 CommentHelper ch = getCommentHelper(element); 2943 for (DocTree dt : blockTags) { 2944 if (ch.getTagName(dt).equals(tname)) { 2945 out.add(dt); 2946 } 2947 } 2948 return out; 2949 } 2950 2951 public boolean hasBlockTag(Element element, DocTree.Kind kind) { 2952 return hasBlockTag(element, kind, null); 2953 } 2954 2955 public boolean hasBlockTag(Element element, DocTree.Kind kind, final String tagName) { 2956 CommentHelper ch = getCommentHelper(element); 2957 String tname = tagName != null && tagName.startsWith("@") 2958 ? tagName.substring(1) 2959 : tagName; 2960 for (DocTree dt : getBlockTags(element, kind)) { 2961 if (dt.getKind() == kind) { 2962 if (tname == null || ch.getTagName(dt).equals(tname)) { 2963 return true; 2964 } 2965 } 2966 } 2967 return false; 2968 } 2969 2970 /** 2971 * Gets a TreePath for an Element. Note this method is called very 2972 * frequently, care must be taken to ensure this method is lithe 2973 * and efficient. 2974 * @param e an Element 2975 * @return TreePath 2976 */ 2977 public TreePath getTreePath(Element e) { 2978 DocCommentDuo duo = dcTreeCache.get(e); 2979 if (isValidDuo(duo) && duo.treePath != null) { 2980 return duo.treePath; 2981 } 2982 duo = configuration.cmtUtils.getSyntheticCommentDuo(e); 2983 if (isValidDuo(duo) && duo.treePath != null) { 2984 return duo.treePath; 2985 } 2986 Map<Element, TreePath> elementToTreePath = configuration.workArounds.getElementToTreePath(); 2987 TreePath path = elementToTreePath.get(e); 2988 if (path != null || elementToTreePath.containsKey(e)) { 2989 // expedite the path and one that is a null 2990 return path; 2991 } 2992 return elementToTreePath.computeIfAbsent(e, docTrees::getPath); 2993 } 2994 2995 private final Map<Element, DocCommentDuo> dcTreeCache = new LinkedHashMap<>(); 2996 2997 /** 2998 * Retrieves the doc comments for a given element. 2999 * @param element 3000 * @return DocCommentTree for the Element 3001 */ 3002 public DocCommentTree getDocCommentTree0(Element element) { 3003 3004 DocCommentDuo duo = null; 3005 3006 ElementKind kind = element.getKind(); 3007 if (kind == ElementKind.PACKAGE || kind == ElementKind.OTHER) { 3008 duo = dcTreeCache.get(element); // local cache 3009 if (!isValidDuo(duo) && kind == ElementKind.PACKAGE) { 3010 // package-info.java 3011 duo = getDocCommentTuple(element); 3012 } 3013 if (!isValidDuo(duo)) { 3014 // package.html or overview.html 3015 duo = configuration.cmtUtils.getHtmlCommentDuo(element); // html source 3016 } 3017 } else { 3018 duo = configuration.cmtUtils.getSyntheticCommentDuo(element); 3019 if (!isValidDuo(duo)) { 3020 duo = dcTreeCache.get(element); // local cache 3021 } 3022 if (!isValidDuo(duo)) { 3023 duo = getDocCommentTuple(element); // get the real mccoy 3024 } 3025 } 3026 3027 DocCommentTree docCommentTree = isValidDuo(duo) ? duo.dcTree : null; 3028 TreePath path = isValidDuo(duo) ? duo.treePath : null; 3029 if (!dcTreeCache.containsKey(element)) { 3030 if (docCommentTree != null && path != null) { 3031 if (!configuration.isAllowScriptInComments()) { 3032 try { 3033 javaScriptScanner.scan(docCommentTree, path, p -> { 3034 throw new JavaScriptScanner.Fault(); 3035 }); 3036 } catch (JavaScriptScanner.Fault jsf) { 3037 String text = configuration.getText("doclet.JavaScript_in_comment"); 3038 throw new UncheckedDocletException(new SimpleDocletException(text, jsf)); 3039 } 3040 } 3041 configuration.workArounds.runDocLint(path); 3042 } 3043 dcTreeCache.put(element, duo); 3044 } 3045 return docCommentTree; 3046 } 3047 3048 private DocCommentDuo getDocCommentTuple(Element element) { 3049 // prevent nasty things downstream with overview element 3050 if (element.getKind() != ElementKind.OTHER) { 3051 TreePath path = getTreePath(element); 3052 if (path != null) { 3053 DocCommentTree docCommentTree = docTrees.getDocCommentTree(path); 3054 return new DocCommentDuo(path, docCommentTree); 3055 } 3056 } 3057 return null; 3058 } 3059 3060 public void checkJavaScriptInOption(String name, String value) { 3061 if (!configuration.isAllowScriptInComments()) { 3062 DocCommentTree dct = configuration.cmtUtils.parse( 3063 URI.create("option://" + name.replace("-", "")), "<body>" + value + "</body>"); 3064 try { 3065 javaScriptScanner.scan(dct, null, p -> { 3066 throw new JavaScriptScanner.Fault(); 3067 }); 3068 } catch (JavaScriptScanner.Fault jsf) { 3069 String text = configuration.getText("doclet.JavaScript_in_option", name); 3070 throw new UncheckedDocletException(new SimpleDocletException(text, jsf)); 3071 } 3072 } 3073 } 3074 3075 boolean isValidDuo(DocCommentDuo duo) { 3076 return duo != null && duo.dcTree != null; 3077 } 3078 3079 public DocCommentTree getDocCommentTree(Element element) { 3080 CommentHelper ch = wksMap.get(element); 3081 if (ch != null) { 3082 return ch.dctree; 3083 } 3084 DocCommentTree dcTree = getDocCommentTree0(element); 3085 if (dcTree != null) { 3086 wksMap.put(element, new CommentHelper(configuration, element, getTreePath(element), dcTree)); 3087 } 3088 return dcTree; 3089 } 3090 3091 public List<? extends DocTree> getFullBody(Element element) { 3092 DocCommentTree docCommentTree = getDocCommentTree(element); 3093 return (docCommentTree == null) 3094 ? Collections.emptyList() 3095 : docCommentTree.getFullBody(); 3096 } 3097 3098 public List<? extends DocTree> getBody(Element element) { 3099 DocCommentTree docCommentTree = getDocCommentTree(element); 3100 return (docCommentTree == null) 3101 ? Collections.emptyList() 3102 : docCommentTree.getFullBody(); 3103 } 3104 3105 public List<? extends DocTree> getDeprecatedTrees(Element element) { 3106 return getBlockTags(element, DEPRECATED); 3107 } 3108 3109 public List<? extends DocTree> getProvidesTrees(Element element) { 3110 return getBlockTags(element, PROVIDES); 3111 } 3112 3113 public List<? extends DocTree> getSeeTrees(Element element) { 3114 return getBlockTags(element, SEE); 3115 } 3116 3117 public List<? extends DocTree> getSerialTrees(Element element) { 3118 return getBlockTags(element, SERIAL); 3119 } 3120 3121 public List<? extends DocTree> getSerialFieldTrees(VariableElement field) { 3122 return getBlockTags(field, DocTree.Kind.SERIAL_FIELD); 3123 } 3124 3125 public List<? extends DocTree> getThrowsTrees(Element element) { 3126 return getBlockTags(element, DocTree.Kind.EXCEPTION, DocTree.Kind.THROWS); 3127 } 3128 3129 public List<? extends DocTree> getTypeParamTrees(Element element) { 3130 return getParamTrees(element, true); 3131 } 3132 3133 public List<? extends DocTree> getParamTrees(Element element) { 3134 return getParamTrees(element, false); 3135 } 3136 3137 private List<? extends DocTree> getParamTrees(Element element, boolean isTypeParameters) { 3138 List<DocTree> out = new ArrayList<>(); 3139 for (DocTree dt : getBlockTags(element, PARAM)) { 3140 ParamTree pt = (ParamTree) dt; 3141 if (pt.isTypeParameter() == isTypeParameters) { 3142 out.add(dt); 3143 } 3144 } 3145 return out; 3146 } 3147 3148 public List<? extends DocTree> getReturnTrees(Element element) { 3149 List<DocTree> out = new ArrayList<>(); 3150 for (DocTree dt : getBlockTags(element, RETURN)) { 3151 out.add(dt); 3152 } 3153 return out; 3154 } 3155 3156 public List<? extends DocTree> getUsesTrees(Element element) { 3157 return getBlockTags(element, USES); 3158 } 3159 3160 public List<? extends DocTree> getFirstSentenceTrees(Element element) { 3161 DocCommentTree dcTree = getDocCommentTree(element); 3162 if (dcTree == null) { 3163 return Collections.emptyList(); 3164 } 3165 List<DocTree> out = new ArrayList<>(); 3166 for (DocTree dt : dcTree.getFirstSentence()) { 3167 out.add(dt); 3168 } 3169 return out; 3170 } 3171 3172 public ModuleElement containingModule(Element e) { 3173 return elementUtils.getModuleOf(e); 3174 } 3175 3176 public PackageElement containingPackage(Element e) { 3177 return elementUtils.getPackageOf(e); 3178 } 3179 3180 public TypeElement getTopMostContainingTypeElement(Element e) { 3181 if (isPackage(e)) { 3182 return null; 3183 } 3184 TypeElement outer = getEnclosingTypeElement(e); 3185 if (outer == null) 3186 return (TypeElement)e; 3187 while (outer != null && outer.getNestingKind().isNested()) { 3188 outer = getEnclosingTypeElement(outer); 3189 } 3190 return outer; 3191 } 3192 3193 static class WeakSoftHashMap implements Map<Element, CommentHelper> { 3194 3195 private final WeakHashMap<Element, SoftReference<CommentHelper>> wkMap; 3196 private final Utils utils; 3197 public WeakSoftHashMap(Utils utils) { 3198 wkMap = new WeakHashMap<>(); 3199 this.utils = utils; 3200 } 3201 3202 @Override 3203 public boolean containsKey(Object key) { 3204 return wkMap.containsKey(key); 3205 } 3206 3207 @Override 3208 public Collection<CommentHelper> values() { 3209 Set<CommentHelper> out = new LinkedHashSet<>(); 3210 for (SoftReference<CommentHelper> v : wkMap.values()) { 3211 out.add(v.get()); 3212 } 3213 return out; 3214 } 3215 3216 @Override 3217 public boolean containsValue(Object value) { 3218 return wkMap.containsValue(new SoftReference<>((CommentHelper)value)); 3219 } 3220 3221 @Override 3222 public CommentHelper remove(Object key) { 3223 SoftReference<CommentHelper> value = wkMap.remove(key); 3224 return value == null ? null : value.get(); 3225 } 3226 3227 3228 @Override 3229 public CommentHelper put(Element key, CommentHelper value) { 3230 SoftReference<CommentHelper> nvalue = wkMap.put(key, new SoftReference<>(value)); 3231 return nvalue == null ? null : nvalue.get(); 3232 } 3233 3234 @Override 3235 public CommentHelper get(Object key) { 3236 SoftReference<CommentHelper> value = wkMap.get(key); 3237 return value == null ? null : value.get(); 3238 } 3239 3240 @Override 3241 public int size() { 3242 return wkMap.size(); 3243 } 3244 3245 @Override 3246 public boolean isEmpty() { 3247 return wkMap.isEmpty(); 3248 } 3249 3250 @Override 3251 public void clear() { 3252 wkMap.clear(); 3253 } 3254 3255 public CommentHelper computeIfAbsent(Element key) { 3256 if (wkMap.containsKey(key)) { 3257 SoftReference<CommentHelper> value = wkMap.get(key); 3258 if (value != null) { 3259 CommentHelper cvalue = value.get(); 3260 if (cvalue != null) { 3261 return cvalue; 3262 } 3263 } 3264 } 3265 CommentHelper newValue = new CommentHelper(utils.configuration, key, utils.getTreePath(key), 3266 utils.getDocCommentTree(key)); 3267 wkMap.put(key, new SoftReference<>(newValue)); 3268 return newValue; 3269 } 3270 3271 3272 @Override 3273 public void putAll(Map<? extends Element, ? extends CommentHelper> map) { 3274 for (Map.Entry<? extends Element, ? extends CommentHelper> entry : map.entrySet()) { 3275 put(entry.getKey(), entry.getValue()); 3276 } 3277 } 3278 3279 @Override 3280 public Set<Element> keySet() { 3281 return wkMap.keySet(); 3282 } 3283 3284 @Override 3285 public Set<Entry<Element, CommentHelper>> entrySet() { 3286 Set<Entry<Element, CommentHelper>> out = new LinkedHashSet<>(); 3287 for (Element e : wkMap.keySet()) { 3288 SimpleEntry<Element, CommentHelper> n = new SimpleEntry<>(e, get(e)); 3289 out.add(n); 3290 } 3291 return out; 3292 } 3293 } 3294} 3295