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