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