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