1 /*
   2  * Copyright (c) 1999, 2014, 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 com.sun.tools.doclets.internal.toolkit.util;
  27 
  28 import java.io.*;
  29 import java.lang.annotation.Documented;
  30 import java.lang.annotation.ElementType;
  31 import java.lang.annotation.Target;
  32 import java.text.Collator;
  33 import java.util.*;
  34 
  35 import javax.tools.StandardLocation;
  36 
  37 import com.sun.javadoc.*;
  38 import com.sun.javadoc.AnnotationDesc.ElementValuePair;
  39 import com.sun.tools.doclets.internal.toolkit.*;
  40 import com.sun.tools.javac.util.StringUtils;
  41 
  42 /**
  43  * Utilities Class for Doclets.
  44  *
  45  *  <p><b>This is NOT part of any supported API.
  46  *  If you write code that depends on this, you do so at your own risk.
  47  *  This code and its internal interfaces are subject to change or
  48  *  deletion without notice.</b>
  49  *
  50  * @author Atul M Dambalkar
  51  * @author Jamie Ho
  52  */
  53 public class Utils {
  54     /**
  55      * Return array of class members whose documentation is to be generated.
  56      * If the member is deprecated do not include such a member in the
  57      * returned array.
  58      *
  59      * @param  members             Array of members to choose from.
  60      * @return ProgramElementDoc[] Array of eligible members for whom
  61      *                             documentation is getting generated.
  62      */
  63     public ProgramElementDoc[] excludeDeprecatedMembers(
  64         ProgramElementDoc[] members) {
  65         return
  66             toProgramElementDocArray(excludeDeprecatedMembersAsList(members));
  67     }
  68 
  69     /**
  70      * Return array of class members whose documentation is to be generated.
  71      * If the member is deprecated do not include such a member in the
  72      * returned array.
  73      *
  74      * @param  members    Array of members to choose from.
  75      * @return List       List of eligible members for whom
  76      *                    documentation is getting generated.
  77      */
  78     public List<ProgramElementDoc> excludeDeprecatedMembersAsList(
  79         ProgramElementDoc[] members) {
  80         List<ProgramElementDoc> list = new ArrayList<>();
  81         for (ProgramElementDoc member : members) {
  82             if (member.tags("deprecated").length == 0) {
  83                 list.add(member);
  84             }
  85         }
  86         Collections.sort(list);
  87         return list;
  88     }
  89 
  90     /**
  91      * Return the list of ProgramElementDoc objects as Array.
  92      */
  93     public ProgramElementDoc[] toProgramElementDocArray(List<ProgramElementDoc> list) {
  94         ProgramElementDoc[] pgmarr = new ProgramElementDoc[list.size()];
  95         for (int i = 0; i < list.size(); i++) {
  96             pgmarr[i] = list.get(i);
  97         }
  98         return pgmarr;
  99     }
 100 
 101     /**
 102      * Return true if a non-public member found in the given array.
 103      *
 104      * @param  members Array of members to look into.
 105      * @return boolean True if non-public member found, false otherwise.
 106      */
 107     public boolean nonPublicMemberFound(ProgramElementDoc[] members) {
 108         for (ProgramElementDoc member : members) {
 109             if (!member.isPublic()) {
 110                 return true;
 111             }
 112         }
 113         return false;
 114     }
 115 
 116     /**
 117      * Search for the given method in the given class.
 118      *
 119      * @param  cd        Class to search into.
 120      * @param  method    Method to be searched.
 121      * @return MethodDoc Method found, null otherwise.
 122      */
 123     public MethodDoc findMethod(ClassDoc cd, MethodDoc method) {
 124         MethodDoc[] methods = cd.methods();
 125         for (MethodDoc m : methods) {
 126             if (executableMembersEqual(method, m)) {
 127                 return m;
 128 
 129             }
 130         }
 131         return null;
 132     }
 133 
 134     /**
 135      * @param member1 the first method to compare.
 136      * @param member2 the second method to compare.
 137      * @return true if member1 overrides/hides or is overriden/hidden by member2.
 138      */
 139     public boolean executableMembersEqual(ExecutableMemberDoc member1,
 140             ExecutableMemberDoc member2) {
 141         if (! (member1 instanceof MethodDoc && member2 instanceof MethodDoc))
 142             return false;
 143 
 144         MethodDoc method1 = (MethodDoc) member1;
 145         MethodDoc method2 = (MethodDoc) member2;
 146         if (method1.isStatic() && method2.isStatic()) {
 147             Parameter[] targetParams = method1.parameters();
 148             Parameter[] currentParams;
 149             if (method1.name().equals(method2.name()) &&
 150                    (currentParams = method2.parameters()).length ==
 151                 targetParams.length) {
 152                 int j;
 153                 for (j = 0; j < targetParams.length; j++) {
 154                     if (! (targetParams[j].typeName().equals(
 155                               currentParams[j].typeName()) ||
 156                                    currentParams[j].type() instanceof TypeVariable ||
 157                                    targetParams[j].type() instanceof TypeVariable)) {
 158                         break;
 159                     }
 160                 }
 161                 if (j == targetParams.length) {
 162                     return true;
 163                 }
 164             }
 165             return false;
 166         } else {
 167                 return method1.overrides(method2) ||
 168                 method2.overrides(method1) ||
 169                                 member1 == member2;
 170         }
 171     }
 172 
 173     /**
 174      * According to
 175      * <cite>The Java&trade; Language Specification</cite>,
 176      * all the outer classes and static inner classes are core classes.
 177      */
 178     public boolean isCoreClass(ClassDoc cd) {
 179         return cd.containingClass() == null || cd.isStatic();
 180     }
 181 
 182     public boolean matches(ProgramElementDoc doc1,
 183             ProgramElementDoc doc2) {
 184         if (doc1 instanceof ExecutableMemberDoc &&
 185             doc2 instanceof ExecutableMemberDoc) {
 186             ExecutableMemberDoc ed1 = (ExecutableMemberDoc)doc1;
 187             ExecutableMemberDoc ed2 = (ExecutableMemberDoc)doc2;
 188             return executableMembersEqual(ed1, ed2);
 189         } else {
 190             return doc1.name().equals(doc2.name());
 191         }
 192     }
 193 
 194     /**
 195      * Copy the given directory contents from the source package directory
 196      * to the generated documentation directory. For example for a package
 197      * java.lang this method find out the source location of the package using
 198      * {@link SourcePath} and if given directory is found in the source
 199      * directory structure, copy the entire directory, to the generated
 200      * documentation hierarchy.
 201      *
 202      * @param configuration The configuration of the current doclet.
 203      * @param path The relative path to the directory to be copied.
 204      * @param dir The original directory name to copy from.
 205      * @param overwrite Overwrite files if true.
 206      */
 207     public void copyDocFiles(Configuration configuration, PackageDoc pd) {
 208         copyDocFiles(configuration, DocPath.forPackage(pd).resolve(DocPaths.DOC_FILES));
 209     }
 210 
 211     public void copyDocFiles(Configuration configuration, DocPath dir) {
 212         try {
 213             boolean first = true;
 214             for (DocFile f : DocFile.list(configuration, StandardLocation.SOURCE_PATH, dir)) {
 215                 if (!f.isDirectory()) {
 216                     continue;
 217                 }
 218                 DocFile srcdir = f;
 219                 DocFile destdir = DocFile.createFileForOutput(configuration, dir);
 220                 if (srcdir.isSameFile(destdir)) {
 221                     continue;
 222                 }
 223 
 224                 for (DocFile srcfile: srcdir.list()) {
 225                     DocFile destfile = destdir.resolve(srcfile.getName());
 226                     if (srcfile.isFile()) {
 227                         if (destfile.exists() && !first) {
 228                             configuration.message.warning((SourcePosition) null,
 229                                     "doclet.Copy_Overwrite_warning",
 230                                     srcfile.getPath(), destdir.getPath());
 231                         } else {
 232                             configuration.message.notice(
 233                                     "doclet.Copying_File_0_To_Dir_1",
 234                                     srcfile.getPath(), destdir.getPath());
 235                             destfile.copyFile(srcfile);
 236                         }
 237                     } else if (srcfile.isDirectory()) {
 238                         if (configuration.copydocfilesubdirs
 239                                 && !configuration.shouldExcludeDocFileDir(srcfile.getName())) {
 240                             copyDocFiles(configuration, dir.resolve(srcfile.getName()));
 241                         }
 242                     }
 243                 }
 244 
 245                 first = false;
 246             }
 247         } catch (SecurityException | IOException exc) {
 248             throw new DocletAbortException(exc);
 249         }
 250     }
 251     /**
 252      * Returns a TypeComparator object suitable for sorting Types.
 253      * @return a TypeComparator objectt
 254      */
 255     public Comparator<Type> makeTypeComparator() {
 256         return new TypeComparator();
 257     }
 258     /**
 259      * We want the list of types in alphabetical order.  However, types are not
 260      * comparable.  We need a comparator for now.
 261      */
 262     private static class TypeComparator implements Comparator<Type> {
 263         public int compare(Type type1, Type type2) {
 264             return compareStrings(type1.qualifiedTypeName(), type2.qualifiedTypeName());
 265         }
 266     }
 267 
 268     /**
 269      * For the class return all implemented interfaces including the
 270      * superinterfaces of the implementing interfaces, also iterate over for
 271      * all the superclasses. For interface return all the extended interfaces
 272      * as well as superinterfaces for those extended interfaces.
 273      *
 274      * @param  type       type whose implemented or
 275      *                    super interfaces are sought.
 276      * @param  configuration the current configuration of the doclet.
 277      * @param  sort if true, return list of interfaces sorted alphabetically.
 278      * @return List of all the required interfaces.
 279      */
 280     public List<Type> getAllInterfaces(Type type,
 281             Configuration configuration, boolean sort) {
 282         Map<ClassDoc,Type> results = sort ?
 283                 new TreeMap<ClassDoc,Type>() :
 284                 new LinkedHashMap<ClassDoc,Type>();
 285         Type[] interfaceTypes = null;
 286         Type superType = null;
 287         if (type instanceof ParameterizedType) {
 288             interfaceTypes = ((ParameterizedType) type).interfaceTypes();
 289             superType = ((ParameterizedType) type).superclassType();
 290         } else if (type instanceof ClassDoc) {
 291             interfaceTypes = ((ClassDoc) type).interfaceTypes();
 292             superType = ((ClassDoc) type).superclassType();
 293         } else {
 294             interfaceTypes = type.asClassDoc().interfaceTypes();
 295             superType = type.asClassDoc().superclassType();
 296         }
 297 
 298         for (Type interfaceType : interfaceTypes) {
 299             ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
 300             if (!(interfaceClassDoc.isPublic() ||
 301                   (configuration == null ||
 302                    isLinkable(interfaceClassDoc, configuration)))) {
 303                 continue;
 304             }
 305             results.put(interfaceClassDoc, interfaceType);
 306             for (Type t : getAllInterfaces(interfaceType, configuration, sort)) {
 307                 results.put(t.asClassDoc(), t);
 308             }
 309         }
 310         if (superType == null)
 311             return new ArrayList<>(results.values());
 312         //Try walking the tree.
 313         addAllInterfaceTypes(results,
 314             superType,
 315             interfaceTypesOf(superType),
 316             false, configuration);
 317         List<Type> resultsList = new ArrayList<>(results.values());
 318         if (sort) {
 319                 Collections.sort(resultsList, new TypeComparator());
 320         }
 321         return resultsList;
 322     }
 323 
 324     private Type[] interfaceTypesOf(Type type) {
 325         if (type instanceof AnnotatedType)
 326             type = ((AnnotatedType)type).underlyingType();
 327         return type instanceof ClassDoc ?
 328                 ((ClassDoc)type).interfaceTypes() :
 329                 ((ParameterizedType)type).interfaceTypes();
 330     }
 331 
 332     public List<Type> getAllInterfaces(Type type, Configuration configuration) {
 333         return getAllInterfaces(type, configuration, true);
 334     }
 335 
 336     private void findAllInterfaceTypes(Map<ClassDoc,Type> results, ClassDoc c, boolean raw,
 337             Configuration configuration) {
 338         Type superType = c.superclassType();
 339         if (superType == null)
 340             return;
 341         addAllInterfaceTypes(results, superType,
 342                 interfaceTypesOf(superType),
 343                 raw, configuration);
 344     }
 345 
 346     private void findAllInterfaceTypes(Map<ClassDoc,Type> results, ParameterizedType p,
 347             Configuration configuration) {
 348         Type superType = p.superclassType();
 349         if (superType == null)
 350             return;
 351         addAllInterfaceTypes(results, superType,
 352                 interfaceTypesOf(superType),
 353                 false, configuration);
 354     }
 355 
 356     private void addAllInterfaceTypes(Map<ClassDoc,Type> results, Type type,
 357             Type[] interfaceTypes, boolean raw,
 358             Configuration configuration) {
 359         for (Type interfaceType : interfaceTypes) {
 360             ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
 361             if (!(interfaceClassDoc.isPublic() ||
 362                   (configuration != null &&
 363                    isLinkable(interfaceClassDoc, configuration)))) {
 364                 continue;
 365             }
 366             if (raw)
 367                 interfaceType = interfaceType.asClassDoc();
 368             results.put(interfaceClassDoc, interfaceType);
 369             List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration);
 370             for (Type superInterface : superInterfaces) {
 371                 results.put(superInterface.asClassDoc(), superInterface);
 372             }
 373         }
 374         if (type instanceof AnnotatedType)
 375             type = ((AnnotatedType)type).underlyingType();
 376 
 377         if (type instanceof ParameterizedType)
 378             findAllInterfaceTypes(results, (ParameterizedType) type, configuration);
 379         else if (((ClassDoc) type).typeParameters().length == 0)
 380             findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration);
 381         else
 382             findAllInterfaceTypes(results, (ClassDoc) type, true, configuration);
 383     }
 384 
 385     /**
 386      * Enclose in quotes, used for paths and filenames that contains spaces
 387      */
 388     public String quote(String filepath) {
 389         return ("\"" + filepath + "\"");
 390     }
 391 
 392     /**
 393      * Given a package, return its name.
 394      * @param packageDoc the package to check.
 395      * @return the name of the given package.
 396      */
 397     public String getPackageName(PackageDoc packageDoc) {
 398         return packageDoc == null || packageDoc.name().length() == 0 ?
 399             DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name();
 400     }
 401 
 402     /**
 403      * Given a package, return its file name without the extension.
 404      * @param packageDoc the package to check.
 405      * @return the file name of the given package.
 406      */
 407     public String getPackageFileHeadName(PackageDoc packageDoc) {
 408         return packageDoc == null || packageDoc.name().length() == 0 ?
 409             DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name();
 410     }
 411 
 412     /**
 413      * Given a string, replace all occurrences of 'newStr' with 'oldStr'.
 414      * @param originalStr the string to modify.
 415      * @param oldStr the string to replace.
 416      * @param newStr the string to insert in place of the old string.
 417      */
 418     public String replaceText(String originalStr, String oldStr,
 419             String newStr) {
 420         if (oldStr == null || newStr == null || oldStr.equals(newStr)) {
 421             return originalStr;
 422         }
 423         return originalStr.replace(oldStr, newStr);
 424     }
 425 
 426     /**
 427      * Given an annotation, return true if it should be documented and false
 428      * otherwise.
 429      *
 430      * @param annotationDoc the annotation to check.
 431      *
 432      * @return true return true if it should be documented and false otherwise.
 433      */
 434     public boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) {
 435         for (AnnotationDesc anno : annotationDoc.annotations()) {
 436             if (anno.annotationType().qualifiedName().equals(
 437                     Documented.class.getName())) {
 438                 return true;
 439             }
 440         }
 441         return false;
 442     }
 443 
 444     private boolean isDeclarationTarget(AnnotationDesc targetAnno) {
 445         // The error recovery steps here are analogous to TypeAnnotations
 446         ElementValuePair[] elems = targetAnno.elementValues();
 447         if (elems == null
 448             || elems.length != 1
 449             || !"value".equals(elems[0].element().name())
 450             || !(elems[0].value().value() instanceof AnnotationValue[]))
 451             return true;    // error recovery
 452 
 453         for (AnnotationValue aValue : (AnnotationValue[])elems[0].value().value()) {
 454             Object value = aValue.value();
 455             if (!(value instanceof FieldDoc))
 456                 return true; // error recovery
 457 
 458             FieldDoc eValue = (FieldDoc) value;
 459             if (isJava5DeclarationElementType(eValue)) {
 460                 return true;
 461             }
 462         }
 463 
 464         return false;
 465     }
 466 
 467     /**
 468      * Returns true if the {@code annotationDoc} is to be treated
 469      * as a declaration annotation, when targeting the
 470      * {@code elemType} element type.
 471      *
 472      * @param annotationDoc the annotationDoc to check
 473      * @param elemType  the targeted elemType
 474      * @return true if annotationDoc is a declaration annotation
 475      */
 476     public boolean isDeclarationAnnotation(AnnotationTypeDoc annotationDoc,
 477             boolean isJava5DeclarationLocation) {
 478         if (!isJava5DeclarationLocation)
 479             return false;
 480         AnnotationDesc[] annotationDescList = annotationDoc.annotations();
 481         // Annotations with no target are treated as declaration as well
 482         if (annotationDescList.length==0)
 483             return true;
 484         for (AnnotationDesc anno : annotationDescList) {
 485             if (anno.annotationType().qualifiedName().equals(
 486                     Target.class.getName())) {
 487                 if (isDeclarationTarget(anno))
 488                     return true;
 489             }
 490         }
 491         return false;
 492     }
 493 
 494     /**
 495      * Return true if this class is linkable and false if we can't link to the
 496      * desired class.
 497      * <br>
 498      * <b>NOTE:</b>  You can only link to external classes if they are public or
 499      * protected.
 500      *
 501      * @param classDoc the class to check.
 502      * @param configuration the current configuration of the doclet.
 503      *
 504      * @return true if this class is linkable and false if we can't link to the
 505      * desired class.
 506      */
 507     public boolean isLinkable(ClassDoc classDoc,
 508             Configuration configuration) {
 509         return
 510             ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) ||
 511             (configuration.extern.isExternal(classDoc) &&
 512                 (classDoc.isPublic() || classDoc.isProtected()));
 513     }
 514 
 515     /**
 516      * Given a class, return the closest visible super class.
 517      *
 518      * @param classDoc the class we are searching the parent for.
 519      * @param configuration the current configuration of the doclet.
 520      * @return the closest visible super class.  Return null if it cannot
 521      *         be found (i.e. classDoc is java.lang.Object).
 522      */
 523     public Type getFirstVisibleSuperClass(ClassDoc classDoc,
 524             Configuration configuration) {
 525         if (classDoc == null) {
 526             return null;
 527         }
 528         Type sup = classDoc.superclassType();
 529         ClassDoc supClassDoc = classDoc.superclass();
 530         while (sup != null &&
 531                   (! (supClassDoc.isPublic() ||
 532                               isLinkable(supClassDoc, configuration))) ) {
 533             if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName()))
 534                 break;
 535             sup = supClassDoc.superclassType();
 536             supClassDoc = supClassDoc.superclass();
 537         }
 538         if (classDoc.equals(supClassDoc)) {
 539             return null;
 540         }
 541         return sup;
 542     }
 543 
 544     /**
 545      * Given a class, return the closest visible super class.
 546      *
 547      * @param classDoc the class we are searching the parent for.
 548      * @param configuration the current configuration of the doclet.
 549      * @return the closest visible super class.  Return null if it cannot
 550      *         be found (i.e. classDoc is java.lang.Object).
 551      */
 552     public ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc,
 553             Configuration configuration) {
 554         if (classDoc == null) {
 555             return null;
 556         }
 557         ClassDoc supClassDoc = classDoc.superclass();
 558         while (supClassDoc != null &&
 559                   (! (supClassDoc.isPublic() ||
 560                               isLinkable(supClassDoc, configuration))) ) {
 561             supClassDoc = supClassDoc.superclass();
 562         }
 563         if (classDoc.equals(supClassDoc)) {
 564             return null;
 565         }
 566         return supClassDoc;
 567     }
 568 
 569     /**
 570      * Given a ClassDoc, return the name of its type (Class, Interface, etc.).
 571      *
 572      * @param cd the ClassDoc to check.
 573      * @param lowerCaseOnly true if you want the name returned in lower case.
 574      *                      If false, the first letter of the name is capitalized.
 575      * @return
 576      */
 577     public String getTypeName(Configuration config,
 578         ClassDoc cd, boolean lowerCaseOnly) {
 579         String typeName = "";
 580         if (cd.isOrdinaryClass()) {
 581             typeName = "doclet.Class";
 582         } else if (cd.isInterface()) {
 583             typeName = "doclet.Interface";
 584         } else if (cd.isException()) {
 585             typeName = "doclet.Exception";
 586         } else if (cd.isError()) {
 587             typeName = "doclet.Error";
 588         } else if (cd.isAnnotationType()) {
 589             typeName = "doclet.AnnotationType";
 590         } else if (cd.isEnum()) {
 591             typeName = "doclet.Enum";
 592         }
 593         return config.getText(
 594             lowerCaseOnly ? StringUtils.toLowerCase(typeName) : typeName);
 595     }
 596 
 597     /**
 598      * Replace all tabs in a string with the appropriate number of spaces.
 599      * The string may be a multi-line string.
 600      * @param configuration the doclet configuration defining the setting for the
 601      *                      tab length.
 602      * @param text the text for which the tabs should be expanded
 603      * @return the text with all tabs expanded
 604      */
 605     public String replaceTabs(Configuration configuration, String text) {
 606         if (!text.contains("\t"))
 607             return text;
 608 
 609         final int tabLength = configuration.sourcetab;
 610         final String whitespace = configuration.tabSpaces;
 611         final int textLength = text.length();
 612         StringBuilder result = new StringBuilder(textLength);
 613         int pos = 0;
 614         int lineLength = 0;
 615         for (int i = 0; i < textLength; i++) {
 616             char ch = text.charAt(i);
 617             switch (ch) {
 618                 case '\n': case '\r':
 619                     lineLength = 0;
 620                     break;
 621                 case '\t':
 622                     result.append(text, pos, i);
 623                     int spaceCount = tabLength - lineLength % tabLength;
 624                     result.append(whitespace, 0, spaceCount);
 625                     lineLength += spaceCount;
 626                     pos = i + 1;
 627                     break;
 628                 default:
 629                     lineLength++;
 630             }
 631         }
 632         result.append(text, pos, textLength);
 633         return result.toString();
 634     }
 635 
 636     public String normalizeNewlines(String text) {
 637         StringBuilder sb = new StringBuilder();
 638         final int textLength = text.length();
 639         final String NL = DocletConstants.NL;
 640         int pos = 0;
 641         for (int i = 0; i < textLength; i++) {
 642             char ch = text.charAt(i);
 643             switch (ch) {
 644                 case '\n':
 645                     sb.append(text, pos, i);
 646                     sb.append(NL);
 647                     pos = i + 1;
 648                     break;
 649                 case '\r':
 650                     sb.append(text, pos, i);
 651                     sb.append(NL);
 652                     if (i + 1 < textLength && text.charAt(i + 1) == '\n')
 653                         i++;
 654                     pos = i + 1;
 655                     break;
 656             }
 657         }
 658         sb.append(text, pos, textLength);
 659         return sb.toString();
 660     }
 661 
 662     /**
 663      * The documentation for values() and valueOf() in Enums are set by the
 664      * doclet.
 665      */
 666     public void setEnumDocumentation(Configuration configuration,
 667             ClassDoc classDoc) {
 668         for (MethodDoc currentMethod : classDoc.methods()) {
 669             if (currentMethod.name().equals("values") &&
 670                 currentMethod.parameters().length == 0) {
 671                 StringBuilder sb = new StringBuilder();
 672                 sb.append(configuration.getText("doclet.enum_values_doc.main", classDoc.name()));
 673                 sb.append("\n@return ");
 674                 sb.append(configuration.getText("doclet.enum_values_doc.return"));
 675                 currentMethod.setRawCommentText(sb.toString());
 676             } else if (currentMethod.name().equals("valueOf") &&
 677                      currentMethod.parameters().length == 1) {
 678                 Type paramType = currentMethod.parameters()[0].type();
 679                 if (paramType != null &&
 680                     paramType.qualifiedTypeName().equals(String.class.getName())) {
 681                     StringBuilder sb = new StringBuilder();
 682                     sb.append(configuration.getText("doclet.enum_valueof_doc.main", classDoc.name()));
 683                     sb.append("\n@param name ");
 684                     sb.append(configuration.getText("doclet.enum_valueof_doc.param_name"));
 685                     sb.append("\n@return ");
 686                     sb.append(configuration.getText("doclet.enum_valueof_doc.return"));
 687                     sb.append("\n@throws IllegalArgumentException ");
 688                     sb.append(configuration.getText("doclet.enum_valueof_doc.throws_ila"));
 689                     sb.append("\n@throws NullPointerException ");
 690                     sb.append(configuration.getText("doclet.enum_valueof_doc.throws_npe"));
 691                     currentMethod.setRawCommentText(sb.toString());
 692                 }
 693             }
 694         }
 695     }
 696 
 697     /**
 698      *  Return true if the given Doc is deprecated.
 699      *
 700      * @param doc the Doc to check.
 701      * @return true if the given Doc is deprecated.
 702      */
 703     public boolean isDeprecated(Doc doc) {
 704         if (doc.tags("deprecated").length > 0) {
 705             return true;
 706         }
 707         AnnotationDesc[] annotationDescList;
 708         if (doc instanceof PackageDoc)
 709             annotationDescList = ((PackageDoc)doc).annotations();
 710         else
 711             annotationDescList = ((ProgramElementDoc)doc).annotations();
 712         for (AnnotationDesc anno : annotationDescList) {
 713             if (anno.annotationType().qualifiedName().equals(
 714                     Deprecated.class.getName())) {
 715                 return true;
 716             }
 717         }
 718         return false;
 719     }
 720 
 721     /**
 722      * A convenience method to get property name from the name of the
 723      * getter or setter method.
 724      * @param name name of the getter or setter method.
 725      * @return the name of the property of the given setter of getter.
 726      */
 727     public String propertyNameFromMethodName(Configuration configuration, String name) {
 728         String propertyName = null;
 729         if (name.startsWith("get") || name.startsWith("set")) {
 730             propertyName = name.substring(3);
 731         } else if (name.startsWith("is")) {
 732             propertyName = name.substring(2);
 733         }
 734         if ((propertyName == null) || propertyName.isEmpty()){
 735             return "";
 736         }
 737         return propertyName.substring(0, 1).toLowerCase(configuration.getLocale())
 738                 + propertyName.substring(1);
 739     }
 740 
 741     /**
 742      * In case of JavaFX mode on, filters out classes that are private,
 743      * package private or having the @treatAsPrivate annotation. Those are not
 744      * documented in JavaFX mode.
 745      *
 746      * @param classes array of classes to be filtered.
 747      * @param javafx set to true if in JavaFX mode.
 748      * @return list of filtered classes.
 749      */
 750     public ClassDoc[] filterOutPrivateClasses(final ClassDoc[] classes,
 751                                                      boolean javafx) {
 752         if (!javafx) {
 753             return classes;
 754         }
 755         final List<ClassDoc> filteredOutClasses = new ArrayList<>(classes.length);
 756         for (ClassDoc classDoc : classes) {
 757             if (classDoc.isPrivate() || classDoc.isPackagePrivate()) {
 758                 continue;
 759             }
 760             Tag[] aspTags = classDoc.tags("treatAsPrivate");
 761             if (aspTags != null && aspTags.length > 0) {
 762                 continue;
 763             }
 764             filteredOutClasses.add(classDoc);
 765         }
 766 
 767         return filteredOutClasses.toArray(new ClassDoc[0]);
 768     }
 769 
 770     /**
 771      * Test whether the given FieldDoc is one of the declaration annotation ElementTypes
 772      * defined in Java 5.
 773      * Instead of testing for one of the new enum constants added in Java 8, test for
 774      * the old constants. This prevents bootstrapping problems.
 775      *
 776      * @param elt The FieldDoc to test
 777      * @return true, iff the given ElementType is one of the constants defined in Java 5
 778      * @since 1.8
 779      */
 780     public boolean isJava5DeclarationElementType(FieldDoc elt) {
 781         return elt.name().contentEquals(ElementType.ANNOTATION_TYPE.name()) ||
 782                 elt.name().contentEquals(ElementType.CONSTRUCTOR.name()) ||
 783                 elt.name().contentEquals(ElementType.FIELD.name()) ||
 784                 elt.name().contentEquals(ElementType.LOCAL_VARIABLE.name()) ||
 785                 elt.name().contentEquals(ElementType.METHOD.name()) ||
 786                 elt.name().contentEquals(ElementType.PACKAGE.name()) ||
 787                 elt.name().contentEquals(ElementType.PARAMETER.name()) ||
 788                 elt.name().contentEquals(ElementType.TYPE.name());
 789     }
 790 
 791     /**
 792      * A general purpose case insensitive String comparator, which compares two Strings using a Collator
 793      * strength of "TERTIARY".
 794      *
 795      * @param s1 first String to compare.
 796      * @param s2 second String to compare.
 797      * @return a negative integer, zero, or a positive integer as the first
 798      *         argument is less than, equal to, or greater than the second.
 799      */
 800     public static int compareStrings(String s1, String s2) {
 801         return compareStrings(true, s1, s2);
 802     }
 803     /**
 804      * A general purpose case sensitive String comparator, which compares two Strings using a Collator
 805      * strength of "SECONDARY".
 806      *
 807      * @param s1 first String to compare.
 808      * @param s2 second String to compare.
 809      * @return a negative integer, zero, or a positive integer as the first
 810      *         argument is less than, equal to, or greater than the second.
 811      */
 812     public static int compareCaseCompare(String s1, String s2) {
 813         return compareStrings(false, s1, s2);
 814     }
 815     private static int compareStrings(boolean caseSensitive, String s1, String s2) {
 816         Collator collator = Collator.getInstance();
 817         collator.setStrength(caseSensitive ? Collator.TERTIARY : Collator.SECONDARY);
 818         return collator.compare(s1, s2);
 819     }
 820     /**
 821      * A simple comparator which compares simple names, then the fully qualified names
 822      * and finally the kinds, ClassUse comparator works well for this purpose.
 823      *
 824      * @return a simple general purpose doc comparator
 825      */
 826     public Comparator<Doc> makeGeneralPurposeComparator() {
 827         return makeComparatorForClassUse();
 828     }
 829     /**
 830      * A comparator for index file presentations, and are sorted as follows:
 831      *  1. sort on simple names of entities
 832      *  2. if equal, then compare the DocKind ex: Package, Interface etc.
 833      *  3a. if equal and if the type is of ExecutableMemberDoc(Constructor, Methods),
 834      *      a case insensitive comparison of parameter the type signatures
 835      *  3b. if equal, case sensitive comparison of the type signatures
 836      *  4. finally, if equal, compare the FQNs of the entities
 837      * @return a comparator for index file use
 838      */
 839     public Comparator<Doc> makeComparatorForIndexUse() {
 840         return new Utils.DocComparator<Doc>() {
 841             /**
 842              * Compare two given Doc entities, first sort on names, then on the kinds,
 843              * then on the parameters only if the type is an instance of ExecutableMemberDocs,
 844              * the parameters are compared and finally the fully qualified names.
 845              *
 846              * @param d1 - a Doc element.
 847              * @param d2 - a Doc element.
 848              * @return a negative integer, zero, or a positive integer as the first
 849              *         argument is less than, equal to, or greater than the second.
 850              */
 851             public int compare(Doc d1, Doc d2) {
 852                 int result = compareNames(d1, d2);
 853                 if (result != 0) {
 854                     return result;
 855                 }
 856                 result = compareDocKinds(d1, d2);
 857                 if (result != 0) {
 858                     return result;
 859                 }
 860                 if (hasParameters(d1)) {
 861                     Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters();
 862                     Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters();
 863                     result = compareParameters(false, param1, param2);
 864                     if (result != 0) {
 865                         return result;
 866                     }
 867                     result = compareParameters(true, param1, param2);
 868                     if (result != 0) {
 869                         return result;
 870                     }
 871                 }
 872                 return compareFullyQualifiedNames(d1, d2);
 873             }
 874         };
 875     }
 876     /**
 877      * Comparator for ClassUse presentations, and sorted as follows,
 878      * 1. compares simple names of entities
 879      * 2. if equal, the fully qualified names of the entities
 880      * 3. if equal and if applicable, the string representation of parameter types
 881      * 3a. first by using case insensitive comparison
 882      * 3b. second by using a case sensitive comparison
 883      * 4. finally the Doc kinds ie. package, class, interface etc.
 884      * @return a comparator to sort classes and members for class use
 885      */
 886     public Comparator<Doc> makeComparatorForClassUse() {
 887         return new Utils.DocComparator<Doc>() {
 888             /**
 889              * Compares two given Doc entities, first sort on name, and if
 890              * applicable on the fully qualified name, and if applicable
 891              * on the parameter types, and finally the DocKind.
 892              * @param d1 - a Doc element.
 893              * @param d2 - a Doc element.
 894              * @return a negative integer, zero, or a positive integer as the first
 895              *         argument is less than, equal to, or greater than the second.
 896              */
 897             public int compare(Doc d1, Doc d2) {
 898                 int result = compareNames(d1, d2);
 899                 if (result != 0) {
 900                     return result;
 901                 }
 902                 result = compareFullyQualifiedNames(d1, d2);
 903                 if (result != 0) {
 904                     return result;
 905                 }
 906                 if (hasParameters(d1) && hasParameters(d2)) {
 907                     Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters();
 908                     Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters();
 909                     result = compareParameters(false, param1, param2);
 910                     if (result != 0) {
 911                         return result;
 912                     }
 913                     return compareParameters(true, param1, param2);
 914                 }
 915                 return compareDocKinds(d1, d2);
 916             }
 917         };
 918     }
 919     /**
 920      * A general purpose comparator to sort Doc entities, basically provides the building blocks
 921      * for creating specific comparators for an use-case.
 922      * @param <T> a Doc entity
 923      */
 924     static abstract class DocComparator<T extends Doc> implements Comparator<Doc> {
 925         static enum DocKind {
 926            PACKAGE,
 927            CLASS,
 928            ENUM,
 929            INTERFACE,
 930            ANNOTATION,
 931            FIELD,
 932            CONSTRUCTOR,
 933            METHOD
 934         };
 935         boolean hasParameters(Doc d) {
 936             return d instanceof ExecutableMemberDoc;
 937         }
 938         DocKind getDocKind(Doc d) {
 939             if (d.isAnnotationType() || d.isAnnotationTypeElement()) {
 940                 return DocKind.ANNOTATION;
 941             } else if (d.isEnum() || d.isEnumConstant()) {
 942                 return DocKind.ENUM;
 943             } else if (d.isField()) {
 944                 return DocKind.FIELD;
 945             } else if (d.isInterface()) {
 946                 return DocKind.INTERFACE;
 947             } else if (d.isClass()) {
 948                 return DocKind.CLASS;
 949             } else if (d.isConstructor()) {
 950                 return DocKind.CONSTRUCTOR;
 951             } else if (d.isMethod()) {
 952                 return DocKind.METHOD;
 953             } else {
 954                 return DocKind.PACKAGE;
 955             }
 956         }
 957         /**
 958          * Compares two Doc entities' kinds, and these are ordered as defined in
 959          * the DocKind enumeration.
 960          * @param d1 the first Doc object
 961          * @param d2 the second Doc object
 962          * @return a negative integer, zero, or a positive integer as the first
 963          *         argument is less than, equal to, or greater than the second.
 964          */
 965         protected int compareDocKinds(Doc d1, Doc d2) {
 966             return getDocKind(d1).compareTo(getDocKind(d2));
 967         }
 968         /**
 969          * Compares arrays of parameters as a string representation of their types.
 970          *
 971          * @param ignoreCase specifies case sensitive or insensitive comparison.
 972          * @param params1 the first parameter array.
 973          * @param params2 the first parameter array.
 974          * @return a negative integer, zero, or a positive integer as the first argument is less
 975          * than, equal to, or greater than the second.
 976          */
 977         protected int compareParameters(boolean caseSensitive,
 978                                         Parameter[] params1,
 979                                         Parameter[] params2) {
 980             String s1 = getParametersAsString(params1);
 981             String s2 = getParametersAsString(params2);
 982             return compareStrings(caseSensitive, s1, s2);
 983         }
 984         /*
 985          * This method returns a string representation solely for comparison purposes.
 986          */
 987         protected String getParametersAsString(Parameter[] params) {
 988             StringBuilder sb = new StringBuilder();
 989             for (Parameter param : params) {
 990                 Type t = param.type();
 991                 // add parameter type to arrays, as TypeMirror does.
 992                 String tname = (t.asParameterizedType() != null && t.getElementType() != null)
 993                         ? t.getElementType() + t.dimension()
 994                         : t.toString();
 995                 // prefix P for primitive and R for reference types, thus items will
 996                 // be ordered naturally.
 997                 sb.append(t.isPrimitive() ? "P" : "R").append("-").append(tname).append("-");
 998             }
 999             return sb.toString();
1000         }
1001 
1002         /**
1003          * Compares two Doc entities typically the simple name of a method,
1004          * field, constructor etc.
1005          * @param d1 the first Doc.
1006          * @param d2 the second Doc.
1007          * @return a negative integer, zero, or a positive integer as the first
1008          *         argument is less than, equal to, or greater than the second.
1009          */
1010         protected int compareNames(Doc d1, Doc d2) {
1011             return compareStrings(d1.name(), d2.name());
1012         }
1013 
1014         /**
1015          * Compares the fully qualified names of the entities
1016          * @param d1 the first entity
1017          * @param d2 the second entity
1018          * @return a negative integer, zero, or a positive integer as the first
1019          *         argument is less than, equal to, or greater than the second.
1020          */
1021         protected int compareFullyQualifiedNames(Doc d1, Doc d2) {
1022             String name1 = (d1 instanceof ProgramElementDoc)
1023                     ? ((ProgramElementDoc)d1).qualifiedName()
1024                     : d1.name();
1025             String name2 = (d2 instanceof ProgramElementDoc)
1026                     ? ((ProgramElementDoc)d2).qualifiedName()
1027                     : d2.name();
1028             return compareStrings(name1, name2);
1029         }
1030     }
1031 }