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