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