langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/Util.java
changeset 25454 376a52c9540c
parent 25453 be80cf0463b3
child 25455 acabdf6da9db
equal deleted inserted replaced
25453:be80cf0463b3 25454:376a52c9540c
     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 Util {
       
    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 static 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 static 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 static 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 static 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 static 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 static 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 static boolean isCoreClass(ClassDoc cd) {
       
   179         return cd.containingClass() == null || cd.isStatic();
       
   180     }
       
   181 
       
   182     public static 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 static void copyDocFiles(Configuration configuration, PackageDoc pd) {
       
   208         copyDocFiles(configuration, DocPath.forPackage(pd).resolve(DocPaths.DOC_FILES));
       
   209     }
       
   210 
       
   211     public static 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     /**
       
   253      * We want the list of types in alphabetical order.  However, types are not
       
   254      * comparable.  We need a comparator for now.
       
   255      */
       
   256     private static class TypeComparator implements Comparator<Type> {
       
   257         public int compare(Type type1, Type type2) {
       
   258             return type1.qualifiedTypeName().compareToIgnoreCase(
       
   259                 type2.qualifiedTypeName());
       
   260         }
       
   261     }
       
   262 
       
   263     /**
       
   264      * For the class return all implemented interfaces including the
       
   265      * superinterfaces of the implementing interfaces, also iterate over for
       
   266      * all the superclasses. For interface return all the extended interfaces
       
   267      * as well as superinterfaces for those extended interfaces.
       
   268      *
       
   269      * @param  type       type whose implemented or
       
   270      *                    super interfaces are sought.
       
   271      * @param  configuration the current configuration of the doclet.
       
   272      * @param  sort if true, return list of interfaces sorted alphabetically.
       
   273      * @return List of all the required interfaces.
       
   274      */
       
   275     public static List<Type> getAllInterfaces(Type type,
       
   276             Configuration configuration, boolean sort) {
       
   277         Map<ClassDoc,Type> results = sort ?
       
   278                 new TreeMap<ClassDoc,Type>() :
       
   279                 new LinkedHashMap<ClassDoc,Type>();
       
   280         Type[] interfaceTypes = null;
       
   281         Type superType = null;
       
   282         if (type instanceof ParameterizedType) {
       
   283             interfaceTypes = ((ParameterizedType) type).interfaceTypes();
       
   284             superType = ((ParameterizedType) type).superclassType();
       
   285         } else if (type instanceof ClassDoc) {
       
   286             interfaceTypes = ((ClassDoc) type).interfaceTypes();
       
   287             superType = ((ClassDoc) type).superclassType();
       
   288         } else {
       
   289             interfaceTypes = type.asClassDoc().interfaceTypes();
       
   290             superType = type.asClassDoc().superclassType();
       
   291         }
       
   292 
       
   293         for (Type interfaceType : interfaceTypes) {
       
   294             ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
       
   295             if (!(interfaceClassDoc.isPublic() ||
       
   296                   (configuration == null ||
       
   297                    isLinkable(interfaceClassDoc, configuration)))) {
       
   298                 continue;
       
   299             }
       
   300             results.put(interfaceClassDoc, interfaceType);
       
   301             for (Type t : getAllInterfaces(interfaceType, configuration, sort)) {
       
   302                 results.put(t.asClassDoc(), t);
       
   303             }
       
   304         }
       
   305         if (superType == null)
       
   306             return new ArrayList<>(results.values());
       
   307         //Try walking the tree.
       
   308         addAllInterfaceTypes(results,
       
   309             superType,
       
   310             interfaceTypesOf(superType),
       
   311             false, configuration);
       
   312         List<Type> resultsList = new ArrayList<>(results.values());
       
   313         if (sort) {
       
   314                 Collections.sort(resultsList, new TypeComparator());
       
   315         }
       
   316         return resultsList;
       
   317     }
       
   318 
       
   319     private static Type[] interfaceTypesOf(Type type) {
       
   320         if (type instanceof AnnotatedType)
       
   321             type = ((AnnotatedType)type).underlyingType();
       
   322         return type instanceof ClassDoc ?
       
   323                 ((ClassDoc)type).interfaceTypes() :
       
   324                 ((ParameterizedType)type).interfaceTypes();
       
   325     }
       
   326 
       
   327     public static List<Type> getAllInterfaces(Type type, Configuration configuration) {
       
   328         return getAllInterfaces(type, configuration, true);
       
   329     }
       
   330 
       
   331     private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ClassDoc c, boolean raw,
       
   332             Configuration configuration) {
       
   333         Type superType = c.superclassType();
       
   334         if (superType == null)
       
   335             return;
       
   336         addAllInterfaceTypes(results, superType,
       
   337                 interfaceTypesOf(superType),
       
   338                 raw, configuration);
       
   339     }
       
   340 
       
   341     private static void findAllInterfaceTypes(Map<ClassDoc,Type> results, ParameterizedType p,
       
   342             Configuration configuration) {
       
   343         Type superType = p.superclassType();
       
   344         if (superType == null)
       
   345             return;
       
   346         addAllInterfaceTypes(results, superType,
       
   347                 interfaceTypesOf(superType),
       
   348                 false, configuration);
       
   349     }
       
   350 
       
   351     private static void addAllInterfaceTypes(Map<ClassDoc,Type> results, Type type,
       
   352             Type[] interfaceTypes, boolean raw,
       
   353             Configuration configuration) {
       
   354         for (Type interfaceType : interfaceTypes) {
       
   355             ClassDoc interfaceClassDoc = interfaceType.asClassDoc();
       
   356             if (!(interfaceClassDoc.isPublic() ||
       
   357                   (configuration != null &&
       
   358                    isLinkable(interfaceClassDoc, configuration)))) {
       
   359                 continue;
       
   360             }
       
   361             if (raw)
       
   362                 interfaceType = interfaceType.asClassDoc();
       
   363             results.put(interfaceClassDoc, interfaceType);
       
   364             List<Type> superInterfaces = getAllInterfaces(interfaceType, configuration);
       
   365             for (Type superInterface : superInterfaces) {
       
   366                 results.put(superInterface.asClassDoc(), superInterface);
       
   367             }
       
   368         }
       
   369         if (type instanceof AnnotatedType)
       
   370             type = ((AnnotatedType)type).underlyingType();
       
   371 
       
   372         if (type instanceof ParameterizedType)
       
   373             findAllInterfaceTypes(results, (ParameterizedType) type, configuration);
       
   374         else if (((ClassDoc) type).typeParameters().length == 0)
       
   375             findAllInterfaceTypes(results, (ClassDoc) type, raw, configuration);
       
   376         else
       
   377             findAllInterfaceTypes(results, (ClassDoc) type, true, configuration);
       
   378     }
       
   379 
       
   380     /**
       
   381      * Enclose in quotes, used for paths and filenames that contains spaces
       
   382      */
       
   383     public static String quote(String filepath) {
       
   384         return ("\"" + filepath + "\"");
       
   385     }
       
   386 
       
   387     /**
       
   388      * Given a package, return its name.
       
   389      * @param packageDoc the package to check.
       
   390      * @return the name of the given package.
       
   391      */
       
   392     public static String getPackageName(PackageDoc packageDoc) {
       
   393         return packageDoc == null || packageDoc.name().length() == 0 ?
       
   394             DocletConstants.DEFAULT_PACKAGE_NAME : packageDoc.name();
       
   395     }
       
   396 
       
   397     /**
       
   398      * Given a package, return its file name without the extension.
       
   399      * @param packageDoc the package to check.
       
   400      * @return the file name of the given package.
       
   401      */
       
   402     public static String getPackageFileHeadName(PackageDoc packageDoc) {
       
   403         return packageDoc == null || packageDoc.name().length() == 0 ?
       
   404             DocletConstants.DEFAULT_PACKAGE_FILE_NAME : packageDoc.name();
       
   405     }
       
   406 
       
   407     /**
       
   408      * Given a string, replace all occurrences of 'newStr' with 'oldStr'.
       
   409      * @param originalStr the string to modify.
       
   410      * @param oldStr the string to replace.
       
   411      * @param newStr the string to insert in place of the old string.
       
   412      */
       
   413     public static String replaceText(String originalStr, String oldStr,
       
   414             String newStr) {
       
   415         if (oldStr == null || newStr == null || oldStr.equals(newStr)) {
       
   416             return originalStr;
       
   417         }
       
   418         return originalStr.replace(oldStr, newStr);
       
   419     }
       
   420 
       
   421     /**
       
   422      * Given an annotation, return true if it should be documented and false
       
   423      * otherwise.
       
   424      *
       
   425      * @param annotationDoc the annotation to check.
       
   426      *
       
   427      * @return true return true if it should be documented and false otherwise.
       
   428      */
       
   429     public static boolean isDocumentedAnnotation(AnnotationTypeDoc annotationDoc) {
       
   430         for (AnnotationDesc anno : annotationDoc.annotations()) {
       
   431             if (anno.annotationType().qualifiedName().equals(
       
   432                     Documented.class.getName())) {
       
   433                 return true;
       
   434             }
       
   435         }
       
   436         return false;
       
   437     }
       
   438 
       
   439     private static boolean isDeclarationTarget(AnnotationDesc targetAnno) {
       
   440         // The error recovery steps here are analogous to TypeAnnotations
       
   441         ElementValuePair[] elems = targetAnno.elementValues();
       
   442         if (elems == null
       
   443             || elems.length != 1
       
   444             || !"value".equals(elems[0].element().name())
       
   445             || !(elems[0].value().value() instanceof AnnotationValue[]))
       
   446             return true;    // error recovery
       
   447 
       
   448         for (AnnotationValue aValue : (AnnotationValue[])elems[0].value().value()) {
       
   449             Object value = aValue.value();
       
   450             if (!(value instanceof FieldDoc))
       
   451                 return true; // error recovery
       
   452 
       
   453             FieldDoc eValue = (FieldDoc) value;
       
   454             if (Util.isJava5DeclarationElementType(eValue)) {
       
   455                 return true;
       
   456             }
       
   457         }
       
   458 
       
   459         return false;
       
   460     }
       
   461 
       
   462     /**
       
   463      * Returns true if the {@code annotationDoc} is to be treated
       
   464      * as a declaration annotation, when targeting the
       
   465      * {@code elemType} element type.
       
   466      *
       
   467      * @param annotationDoc the annotationDoc to check
       
   468      * @param elemType  the targeted elemType
       
   469      * @return true if annotationDoc is a declaration annotation
       
   470      */
       
   471     public static boolean isDeclarationAnnotation(AnnotationTypeDoc annotationDoc,
       
   472             boolean isJava5DeclarationLocation) {
       
   473         if (!isJava5DeclarationLocation)
       
   474             return false;
       
   475         AnnotationDesc[] annotationDescList = annotationDoc.annotations();
       
   476         // Annotations with no target are treated as declaration as well
       
   477         if (annotationDescList.length==0)
       
   478             return true;
       
   479         for (AnnotationDesc anno : annotationDescList) {
       
   480             if (anno.annotationType().qualifiedName().equals(
       
   481                     Target.class.getName())) {
       
   482                 if (isDeclarationTarget(anno))
       
   483                     return true;
       
   484             }
       
   485         }
       
   486         return false;
       
   487     }
       
   488 
       
   489     /**
       
   490      * Return true if this class is linkable and false if we can't link to the
       
   491      * desired class.
       
   492      * <br>
       
   493      * <b>NOTE:</b>  You can only link to external classes if they are public or
       
   494      * protected.
       
   495      *
       
   496      * @param classDoc the class to check.
       
   497      * @param configuration the current configuration of the doclet.
       
   498      *
       
   499      * @return true if this class is linkable and false if we can't link to the
       
   500      * desired class.
       
   501      */
       
   502     public static boolean isLinkable(ClassDoc classDoc,
       
   503             Configuration configuration) {
       
   504         return
       
   505             ((classDoc.isIncluded() && configuration.isGeneratedDoc(classDoc))) ||
       
   506             (configuration.extern.isExternal(classDoc) &&
       
   507                 (classDoc.isPublic() || classDoc.isProtected()));
       
   508     }
       
   509 
       
   510     /**
       
   511      * Given a class, return the closest visible super class.
       
   512      *
       
   513      * @param classDoc the class we are searching the parent for.
       
   514      * @param configuration the current configuration of the doclet.
       
   515      * @return the closest visible super class.  Return null if it cannot
       
   516      *         be found (i.e. classDoc is java.lang.Object).
       
   517      */
       
   518     public static Type getFirstVisibleSuperClass(ClassDoc classDoc,
       
   519             Configuration configuration) {
       
   520         if (classDoc == null) {
       
   521             return null;
       
   522         }
       
   523         Type sup = classDoc.superclassType();
       
   524         ClassDoc supClassDoc = classDoc.superclass();
       
   525         while (sup != null &&
       
   526                   (! (supClassDoc.isPublic() ||
       
   527                               isLinkable(supClassDoc, configuration))) ) {
       
   528             if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName()))
       
   529                 break;
       
   530             sup = supClassDoc.superclassType();
       
   531             supClassDoc = supClassDoc.superclass();
       
   532         }
       
   533         if (classDoc.equals(supClassDoc)) {
       
   534             return null;
       
   535         }
       
   536         return sup;
       
   537     }
       
   538 
       
   539     /**
       
   540      * Given a class, return the closest visible super class.
       
   541      *
       
   542      * @param classDoc the class we are searching the parent for.
       
   543      * @param configuration the current configuration of the doclet.
       
   544      * @return the closest visible super class.  Return null if it cannot
       
   545      *         be found (i.e. classDoc is java.lang.Object).
       
   546      */
       
   547     public static ClassDoc getFirstVisibleSuperClassCD(ClassDoc classDoc,
       
   548             Configuration configuration) {
       
   549         if (classDoc == null) {
       
   550             return null;
       
   551         }
       
   552         ClassDoc supClassDoc = classDoc.superclass();
       
   553         while (supClassDoc != null &&
       
   554                   (! (supClassDoc.isPublic() ||
       
   555                               isLinkable(supClassDoc, configuration))) ) {
       
   556             supClassDoc = supClassDoc.superclass();
       
   557         }
       
   558         if (classDoc.equals(supClassDoc)) {
       
   559             return null;
       
   560         }
       
   561         return supClassDoc;
       
   562     }
       
   563 
       
   564     /**
       
   565      * Given a ClassDoc, return the name of its type (Class, Interface, etc.).
       
   566      *
       
   567      * @param cd the ClassDoc to check.
       
   568      * @param lowerCaseOnly true if you want the name returned in lower case.
       
   569      *                      If false, the first letter of the name is capitalized.
       
   570      * @return
       
   571      */
       
   572     public static String getTypeName(Configuration config,
       
   573         ClassDoc cd, boolean lowerCaseOnly) {
       
   574         String typeName = "";
       
   575         if (cd.isOrdinaryClass()) {
       
   576             typeName = "doclet.Class";
       
   577         } else if (cd.isInterface()) {
       
   578             typeName = "doclet.Interface";
       
   579         } else if (cd.isException()) {
       
   580             typeName = "doclet.Exception";
       
   581         } else if (cd.isError()) {
       
   582             typeName = "doclet.Error";
       
   583         } else if (cd.isAnnotationType()) {
       
   584             typeName = "doclet.AnnotationType";
       
   585         } else if (cd.isEnum()) {
       
   586             typeName = "doclet.Enum";
       
   587         }
       
   588         return config.getText(
       
   589             lowerCaseOnly ? StringUtils.toLowerCase(typeName) : typeName);
       
   590     }
       
   591 
       
   592     /**
       
   593      * Replace all tabs in a string with the appropriate number of spaces.
       
   594      * The string may be a multi-line string.
       
   595      * @param configuration the doclet configuration defining the setting for the
       
   596      *                      tab length.
       
   597      * @param text the text for which the tabs should be expanded
       
   598      * @return the text with all tabs expanded
       
   599      */
       
   600     public static String replaceTabs(Configuration configuration, String text) {
       
   601         if (!text.contains("\t"))
       
   602             return text;
       
   603 
       
   604         final int tabLength = configuration.sourcetab;
       
   605         final String whitespace = configuration.tabSpaces;
       
   606         final int textLength = text.length();
       
   607         StringBuilder result = new StringBuilder(textLength);
       
   608         int pos = 0;
       
   609         int lineLength = 0;
       
   610         for (int i = 0; i < textLength; i++) {
       
   611             char ch = text.charAt(i);
       
   612             switch (ch) {
       
   613                 case '\n': case '\r':
       
   614                     lineLength = 0;
       
   615                     break;
       
   616                 case '\t':
       
   617                     result.append(text, pos, i);
       
   618                     int spaceCount = tabLength - lineLength % tabLength;
       
   619                     result.append(whitespace, 0, spaceCount);
       
   620                     lineLength += spaceCount;
       
   621                     pos = i + 1;
       
   622                     break;
       
   623                 default:
       
   624                     lineLength++;
       
   625             }
       
   626         }
       
   627         result.append(text, pos, textLength);
       
   628         return result.toString();
       
   629     }
       
   630 
       
   631     public static String normalizeNewlines(String text) {
       
   632         StringBuilder sb = new StringBuilder();
       
   633         final int textLength = text.length();
       
   634         final String NL = DocletConstants.NL;
       
   635         int pos = 0;
       
   636         for (int i = 0; i < textLength; i++) {
       
   637             char ch = text.charAt(i);
       
   638             switch (ch) {
       
   639                 case '\n':
       
   640                     sb.append(text, pos, i);
       
   641                     sb.append(NL);
       
   642                     pos = i + 1;
       
   643                     break;
       
   644                 case '\r':
       
   645                     sb.append(text, pos, i);
       
   646                     sb.append(NL);
       
   647                     if (i + 1 < textLength && text.charAt(i + 1) == '\n')
       
   648                         i++;
       
   649                     pos = i + 1;
       
   650                     break;
       
   651             }
       
   652         }
       
   653         sb.append(text, pos, textLength);
       
   654         return sb.toString();
       
   655     }
       
   656 
       
   657     /**
       
   658      * The documentation for values() and valueOf() in Enums are set by the
       
   659      * doclet.
       
   660      */
       
   661     public static void setEnumDocumentation(Configuration configuration,
       
   662             ClassDoc classDoc) {
       
   663         for (MethodDoc currentMethod : classDoc.methods()) {
       
   664             if (currentMethod.name().equals("values") &&
       
   665                 currentMethod.parameters().length == 0) {
       
   666                 StringBuilder sb = new StringBuilder();
       
   667                 sb.append(configuration.getText("doclet.enum_values_doc.main", classDoc.name()));
       
   668                 sb.append("\n@return ");
       
   669                 sb.append(configuration.getText("doclet.enum_values_doc.return"));
       
   670                 currentMethod.setRawCommentText(sb.toString());
       
   671             } else if (currentMethod.name().equals("valueOf") &&
       
   672                      currentMethod.parameters().length == 1) {
       
   673                 Type paramType = currentMethod.parameters()[0].type();
       
   674                 if (paramType != null &&
       
   675                     paramType.qualifiedTypeName().equals(String.class.getName())) {
       
   676                     StringBuilder sb = new StringBuilder();
       
   677                     sb.append(configuration.getText("doclet.enum_valueof_doc.main", classDoc.name()));
       
   678                     sb.append("\n@param name ");
       
   679                     sb.append(configuration.getText("doclet.enum_valueof_doc.param_name"));
       
   680                     sb.append("\n@return ");
       
   681                     sb.append(configuration.getText("doclet.enum_valueof_doc.return"));
       
   682                     sb.append("\n@throws IllegalArgumentException ");
       
   683                     sb.append(configuration.getText("doclet.enum_valueof_doc.throws_ila"));
       
   684                     sb.append("\n@throws NullPointerException ");
       
   685                     sb.append(configuration.getText("doclet.enum_valueof_doc.throws_npe"));
       
   686                     currentMethod.setRawCommentText(sb.toString());
       
   687                 }
       
   688             }
       
   689         }
       
   690     }
       
   691 
       
   692     /**
       
   693      *  Return true if the given Doc is deprecated.
       
   694      *
       
   695      * @param doc the Doc to check.
       
   696      * @return true if the given Doc is deprecated.
       
   697      */
       
   698     public static boolean isDeprecated(Doc doc) {
       
   699         if (doc.tags("deprecated").length > 0) {
       
   700             return true;
       
   701         }
       
   702         AnnotationDesc[] annotationDescList;
       
   703         if (doc instanceof PackageDoc)
       
   704             annotationDescList = ((PackageDoc)doc).annotations();
       
   705         else
       
   706             annotationDescList = ((ProgramElementDoc)doc).annotations();
       
   707         for (AnnotationDesc anno : annotationDescList) {
       
   708             if (anno.annotationType().qualifiedName().equals(
       
   709                     Deprecated.class.getName())) {
       
   710                 return true;
       
   711             }
       
   712         }
       
   713         return false;
       
   714     }
       
   715 
       
   716     /**
       
   717      * A convenience method to get property name from the name of the
       
   718      * getter or setter method.
       
   719      * @param name name of the getter or setter method.
       
   720      * @return the name of the property of the given setter of getter.
       
   721      */
       
   722     public static String propertyNameFromMethodName(Configuration configuration, String name) {
       
   723         String propertyName = null;
       
   724         if (name.startsWith("get") || name.startsWith("set")) {
       
   725             propertyName = name.substring(3);
       
   726         } else if (name.startsWith("is")) {
       
   727             propertyName = name.substring(2);
       
   728         }
       
   729         if ((propertyName == null) || propertyName.isEmpty()){
       
   730             return "";
       
   731         }
       
   732         return propertyName.substring(0, 1).toLowerCase(configuration.getLocale())
       
   733                 + propertyName.substring(1);
       
   734     }
       
   735 
       
   736     /**
       
   737      * In case of JavaFX mode on, filters out classes that are private,
       
   738      * package private or having the @treatAsPrivate annotation. Those are not
       
   739      * documented in JavaFX mode.
       
   740      *
       
   741      * @param classes array of classes to be filtered.
       
   742      * @param javafx set to true if in JavaFX mode.
       
   743      * @return list of filtered classes.
       
   744      */
       
   745     public static ClassDoc[] filterOutPrivateClasses(final ClassDoc[] classes,
       
   746                                                      boolean javafx) {
       
   747         if (!javafx) {
       
   748             return classes;
       
   749         }
       
   750         final List<ClassDoc> filteredOutClasses = new ArrayList<>(classes.length);
       
   751         for (ClassDoc classDoc : classes) {
       
   752             if (classDoc.isPrivate() || classDoc.isPackagePrivate()) {
       
   753                 continue;
       
   754             }
       
   755             Tag[] aspTags = classDoc.tags("treatAsPrivate");
       
   756             if (aspTags != null && aspTags.length > 0) {
       
   757                 continue;
       
   758             }
       
   759             filteredOutClasses.add(classDoc);
       
   760         }
       
   761 
       
   762         return filteredOutClasses.toArray(new ClassDoc[0]);
       
   763     }
       
   764 
       
   765     /**
       
   766      * Test whether the given FieldDoc is one of the declaration annotation ElementTypes
       
   767      * defined in Java 5.
       
   768      * Instead of testing for one of the new enum constants added in Java 8, test for
       
   769      * the old constants. This prevents bootstrapping problems.
       
   770      *
       
   771      * @param elt The FieldDoc to test
       
   772      * @return true, iff the given ElementType is one of the constants defined in Java 5
       
   773      * @since 1.8
       
   774      */
       
   775     public static boolean isJava5DeclarationElementType(FieldDoc elt) {
       
   776         return elt.name().contentEquals(ElementType.ANNOTATION_TYPE.name()) ||
       
   777                 elt.name().contentEquals(ElementType.CONSTRUCTOR.name()) ||
       
   778                 elt.name().contentEquals(ElementType.FIELD.name()) ||
       
   779                 elt.name().contentEquals(ElementType.LOCAL_VARIABLE.name()) ||
       
   780                 elt.name().contentEquals(ElementType.METHOD.name()) ||
       
   781                 elt.name().contentEquals(ElementType.PACKAGE.name()) ||
       
   782                 elt.name().contentEquals(ElementType.PARAMETER.name()) ||
       
   783                 elt.name().contentEquals(ElementType.TYPE.name());
       
   784     }
       
   785 
       
   786     /**
       
   787      * A general purpose case insensitive String comparator, which compares two Strings using a Collator
       
   788      * strength of "TERTIARY".
       
   789      *
       
   790      * @param s1 first String to compare.
       
   791      * @param s2 second String to compare.
       
   792      * @return a negative integer, zero, or a positive integer as the first
       
   793      *         argument is less than, equal to, or greater than the second.
       
   794      */
       
   795     public static int compareStrings(String s1, String s2) {
       
   796         return compareStrings(true, s1, s2);
       
   797     }
       
   798     /**
       
   799      * A general purpose case sensitive String comparator, which compares two Strings using a Collator
       
   800      * strength of "SECONDARY".
       
   801      *
       
   802      * @param s1 first String to compare.
       
   803      * @param s2 second String to compare.
       
   804      * @return a negative integer, zero, or a positive integer as the first
       
   805      *         argument is less than, equal to, or greater than the second.
       
   806      */
       
   807     public static int compareCaseCompare(String s1, String s2) {
       
   808         return compareStrings(false, s1, s2);
       
   809     }
       
   810     private static int compareStrings(boolean caseSensitive, String s1, String s2) {
       
   811         Collator collator = Collator.getInstance();
       
   812         collator.setStrength(caseSensitive ? Collator.TERTIARY : Collator.SECONDARY);
       
   813         return collator.compare(s1, s2);
       
   814     }
       
   815 
       
   816     /**
       
   817      * A comparator for index file presentations, and are sorted as follows:
       
   818      *  1. sort on simple names of entities
       
   819      *  2. if equal, then compare the DocKind ex: Package, Interface etc.
       
   820      *  3a. if equal and if the type is of ExecutableMemberDoc(Constructor, Methods),
       
   821      *      a case insensitive comparison of parameter the type signatures
       
   822      *  3b. if equal, case sensitive comparison of the type signatures
       
   823      *  4. finally, if equal, compare the FQNs of the entities
       
   824      * @return a comparator for index file use
       
   825      */
       
   826     public static Comparator<Doc> makeComparatorForIndexUse() {
       
   827         return new Util.DocComparator<Doc>() {
       
   828             /**
       
   829              * Compare two given Doc entities, first sort on names, then on the kinds,
       
   830              * then on the parameters only if the type is an instance of ExecutableMemberDocs,
       
   831              * the parameters are compared and finally the fully qualified names.
       
   832              *
       
   833              * @param d1 - a Doc element.
       
   834              * @param d2 - a Doc element.
       
   835              * @return a negative integer, zero, or a positive integer as the first
       
   836              *         argument is less than, equal to, or greater than the second.
       
   837              */
       
   838             public int compare(Doc d1, Doc d2) {
       
   839                 int result = compareNames(d1, d2);
       
   840                 if (result != 0) {
       
   841                     return result;
       
   842                 }
       
   843                 result = compareDocKinds(d1, d2);
       
   844                 if (result != 0) {
       
   845                     return result;
       
   846                 }
       
   847                 if (hasParameters(d1)) {
       
   848                     Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters();
       
   849                     Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters();
       
   850                     result = compareParameters(false, param1, param2);
       
   851                     if (result != 0) {
       
   852                         return result;
       
   853                     }
       
   854                     result = compareParameters(true, param1, param2);
       
   855                     if (result != 0) {
       
   856                         return result;
       
   857                     }
       
   858                 }
       
   859                 return compareFullyQualifiedNames(d1, d2);
       
   860             }
       
   861         };
       
   862     }
       
   863     /**
       
   864      * Comparator for ClassUse presentations, and sorted as follows,
       
   865      * 1. compares simple names of entities
       
   866      * 2. if equal, the fully qualified names of the entities
       
   867      * 3. if equal and if applicable, the string representation of parameter types
       
   868      * 3a. first by using case insensitive comparison
       
   869      * 3b. second by using a case sensitive comparison
       
   870      * 4. finally the Doc kinds ie. package, class, interface etc.
       
   871      * @return a comparator to sort classes and members for class use
       
   872      */
       
   873     public static Comparator<Doc> makeComparatorForClassUse() {
       
   874         return new Util.DocComparator<Doc>() {
       
   875             /**
       
   876              * Compares two given Doc entities, first sort on name, and if
       
   877              * applicable on the fully qualified name, and if applicable
       
   878              * on the parameter types, and finally the DocKind.
       
   879              * @param d1 - a Doc element.
       
   880              * @param d2 - a Doc element.
       
   881              * @return a negative integer, zero, or a positive integer as the first
       
   882              *         argument is less than, equal to, or greater than the second.
       
   883              */
       
   884             public int compare(Doc d1, Doc d2) {
       
   885                 int result = compareNames(d1, d2);
       
   886                 if (result != 0) {
       
   887                     return result;
       
   888                 }
       
   889                 result = compareFullyQualifiedNames(d1, d2);
       
   890                 if (result != 0) {
       
   891                     return result;
       
   892                 }
       
   893                 if (hasParameters(d1) && hasParameters(d2)) {
       
   894                     Parameter[] param1 = ((ExecutableMemberDoc) d1).parameters();
       
   895                     Parameter[] param2 = ((ExecutableMemberDoc) d2).parameters();
       
   896                     result = compareParameters(false, param1, param2);
       
   897                     if (result != 0) {
       
   898                         return result;
       
   899                     }
       
   900                     return compareParameters(true, param1, param2);
       
   901                 }
       
   902                 return compareDocKinds(d1, d2);
       
   903             }
       
   904         };
       
   905     }
       
   906     /**
       
   907      * A general purpose comparator to sort Doc entities, basically provides the building blocks
       
   908      * for creating specific comparators for an use-case.
       
   909      * @param <T> a Doc entity
       
   910      */
       
   911     static abstract class DocComparator<T extends Doc> implements Comparator<Doc> {
       
   912         static enum DocKind {
       
   913            PACKAGE,
       
   914            CLASS,
       
   915            ENUM,
       
   916            INTERFACE,
       
   917            ANNOTATION,
       
   918            FIELD,
       
   919            CONSTRUCTOR,
       
   920            METHOD
       
   921         };
       
   922         boolean hasParameters(Doc d) {
       
   923             return d instanceof ExecutableMemberDoc;
       
   924         }
       
   925         DocKind getDocKind(Doc d) {
       
   926             if (d.isAnnotationType() || d.isAnnotationTypeElement()) {
       
   927                 return DocKind.ANNOTATION;
       
   928             } else if (d.isEnum() || d.isEnumConstant()) {
       
   929                 return DocKind.ENUM;
       
   930             } else if (d.isField()) {
       
   931                 return DocKind.FIELD;
       
   932             } else if (d.isInterface()) {
       
   933                 return DocKind.INTERFACE;
       
   934             } else if (d.isClass()) {
       
   935                 return DocKind.CLASS;
       
   936             } else if (d.isConstructor()) {
       
   937                 return DocKind.CONSTRUCTOR;
       
   938             } else if (d.isMethod()) {
       
   939                 return DocKind.METHOD;
       
   940             } else {
       
   941                 return DocKind.PACKAGE;
       
   942             }
       
   943         }
       
   944         /**
       
   945          * Compares two Doc entities' kinds, and these are ordered as defined in
       
   946          * the DocKind enumeration.
       
   947          * @param d1 the first Doc object
       
   948          * @param d2 the second Doc object
       
   949          * @return a negative integer, zero, or a positive integer as the first
       
   950          *         argument is less than, equal to, or greater than the second.
       
   951          */
       
   952         protected int compareDocKinds(Doc d1, Doc d2) {
       
   953             return getDocKind(d1).compareTo(getDocKind(d2));
       
   954         }
       
   955         /**
       
   956          * Compares arrays of parameters as a string representation of their types.
       
   957          *
       
   958          * @param ignoreCase specifies case sensitive or insensitive comparison.
       
   959          * @param params1 the first parameter array.
       
   960          * @param params2 the first parameter array.
       
   961          * @return a negative integer, zero, or a positive integer as the first argument is less
       
   962          * than, equal to, or greater than the second.
       
   963          */
       
   964         protected int compareParameters(boolean caseSensitive,
       
   965                                         Parameter[] params1,
       
   966                                         Parameter[] params2) {
       
   967             String s1 = getParametersAsString(params1);
       
   968             String s2 = getParametersAsString(params2);
       
   969             return compareStrings(caseSensitive, s1, s2);
       
   970         }
       
   971         /*
       
   972          * This method returns a string representation solely for comparison purposes.
       
   973          */
       
   974         protected String getParametersAsString(Parameter[] params) {
       
   975             StringBuilder sb = new StringBuilder();
       
   976             for (Parameter param : params) {
       
   977                 Type t = param.type();
       
   978                 // add parameter type to arrays, as TypeMirror does.
       
   979                 String tname = (t.asParameterizedType() != null && t.getElementType() != null)
       
   980                         ? t.getElementType() + t.dimension()
       
   981                         : t.toString();
       
   982                 // prefix P for primitive and R for reference types, thus items will
       
   983                 // be ordered naturally.
       
   984                 sb.append(t.isPrimitive() ? "P" : "R").append("-").append(tname).append("-");
       
   985             }
       
   986             return sb.toString();
       
   987         }
       
   988 
       
   989         /**
       
   990          * Compares two Doc entities typically the simple name of a method,
       
   991          * field, constructor etc.
       
   992          * @param d1 the first Doc.
       
   993          * @param d2 the second Doc.
       
   994          * @return a negative integer, zero, or a positive integer as the first
       
   995          *         argument is less than, equal to, or greater than the second.
       
   996          */
       
   997         protected int compareNames(Doc d1, Doc d2) {
       
   998             return compareStrings(d1.name(), d2.name());
       
   999         }
       
  1000 
       
  1001         /**
       
  1002          * Compares the fully qualified names of the entities
       
  1003          * @param d1 the first entity
       
  1004          * @param d2 the second entity
       
  1005          * @return a negative integer, zero, or a positive integer as the first
       
  1006          *         argument is less than, equal to, or greater than the second.
       
  1007          */
       
  1008         protected int compareFullyQualifiedNames(Doc d1, Doc d2) {
       
  1009             String name1 = (d1 instanceof ProgramElementDoc)
       
  1010                     ? ((ProgramElementDoc)d1).qualifiedName()
       
  1011                     : d1.name();
       
  1012             String name2 = (d2 instanceof ProgramElementDoc)
       
  1013                     ? ((ProgramElementDoc)d2).qualifiedName()
       
  1014                     : d2.name();
       
  1015             return compareStrings(name1, name2);
       
  1016         }
       
  1017     }
       
  1018 }