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