1/*
2 * Copyright (c) 1998, 2017, 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 jdk.javadoc.internal.doclets.toolkit.util;
27
28import java.util.*;
29
30import javax.lang.model.element.Element;
31import javax.lang.model.element.ModuleElement;
32import javax.lang.model.element.PackageElement;
33import javax.lang.model.element.TypeElement;
34
35import jdk.javadoc.doclet.DocletEnvironment;
36import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
37import jdk.javadoc.internal.doclets.toolkit.Messages;
38
39/**
40 * Build the mapping of each Unicode character with it's member lists
41 * containing members names starting with it. Also build a list for all the
42 * Unicode characters which start a member name. Member name is
43 * classkind or field or method or constructor name.
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 * @see java.lang.Character
51 * @author Atul M Dambalkar
52 */
53public class IndexBuilder {
54
55    /**
56     * Mapping of each Unicode Character with the member list containing
57     * members with names starting with it.
58     */
59    private final Map<Character, SortedSet<Element>> indexmap;
60
61    /**
62     * Don't generate deprecated information if true.
63     */
64    private boolean noDeprecated;
65
66    /**
67     * Build this Index only for classes?
68     */
69    private boolean classesOnly;
70
71    /**
72     * Indicates javafx mode.
73     */
74    private boolean javafx;
75
76    private final BaseConfiguration configuration;
77    private final Utils utils;
78    private final Comparator<Element> comparator;
79
80    /**
81     * Constructor. Build the index map.
82     *
83     * @param configuration the current configuration of the doclet.
84     * @param noDeprecated  true if -nodeprecated option is used,
85     *                      false otherwise.
86     */
87    public IndexBuilder(BaseConfiguration configuration, boolean noDeprecated) {
88        this(configuration, noDeprecated, false);
89    }
90
91    /**
92     * Constructor. Build the index map.
93     *
94     * @param configuration the current configuration of the doclet.
95     * @param noDeprecated  true if -nodeprecated option is used,
96     *                      false otherwise.
97     * @param classesOnly   Include only classes in index.
98     */
99    public IndexBuilder(BaseConfiguration configuration, boolean noDeprecated,
100                        boolean classesOnly) {
101        this.configuration  = configuration;
102        this.utils = configuration.utils;
103
104        Messages messages = configuration.getMessages();
105        if (classesOnly) {
106            messages.notice("doclet.Building_Index_For_All_Classes");
107        } else {
108            messages.notice("doclet.Building_Index");
109        }
110
111        this.noDeprecated = noDeprecated;
112        this.classesOnly = classesOnly;
113        this.javafx = configuration.javafx;
114        this.indexmap = new TreeMap<>();
115        comparator = classesOnly
116                ? utils.makeAllClassesComparator()
117                : utils.makeIndexUseComparator();
118        buildIndexMap(configuration.docEnv);
119    }
120
121    /**
122     * Get all the members in all the Packages and all the Classes
123     * given on the command line. Form separate list of those members depending
124     * upon their names.
125     *
126     * @param docEnv the doclet environment
127     */
128    protected void buildIndexMap(DocletEnvironment docEnv)  {
129        Set<PackageElement> packages = configuration.getSpecifiedPackageElements();
130        Set<TypeElement> classes = configuration.getIncludedTypeElements();
131        if (!classesOnly) {
132            if (packages.isEmpty()) {
133                Set<PackageElement> set = new HashSet<>();
134                for (TypeElement aClass : classes) {
135                    PackageElement pkg = utils.containingPackage(aClass);
136                    if (pkg != null && !pkg.isUnnamed()) {
137                        set.add(pkg);
138                    }
139                }
140                adjustIndexMap(set);
141            } else {
142                adjustIndexMap(packages);
143            }
144        }
145        adjustIndexMap(classes);
146        if (!classesOnly) {
147            for (TypeElement aClass : classes) {
148                if (shouldAddToIndexMap(aClass)) {
149                    putMembersInIndexMap(aClass);
150                }
151            }
152            if (configuration.showModules) {
153                addModulesToIndexMap();
154            }
155        }
156    }
157
158    /**
159     * Put all the members(fields, methods and constructors) in the te
160     * to the indexmap.
161     *
162     * @param te TypeElement whose members will be added to the indexmap.
163     */
164    protected void putMembersInIndexMap(TypeElement te) {
165        adjustIndexMap(utils.getAnnotationFields(te));
166        adjustIndexMap(utils.getFields(te));
167        adjustIndexMap(utils.getMethods(te));
168        adjustIndexMap(utils.getConstructors(te));
169        adjustIndexMap(utils.getEnumConstants(te));
170    }
171
172
173    /**
174     * Adjust list of members according to their names. Check the first
175     * character in a member name, and then add the member to a list of members
176     * for that particular unicode character.
177     *
178     * @param elements Array of members.
179     */
180    protected void adjustIndexMap(Iterable<? extends Element> elements) {
181        for (Element element : elements) {
182            if (shouldAddToIndexMap(element)) {
183                String name = utils.isPackage(element)
184                        ? utils.getPackageName((PackageElement)element)
185                        : utils.getSimpleName(element);
186                char ch = (name.length() == 0) ?
187                          '*' :
188                          Character.toUpperCase(name.charAt(0));
189                Character unicode = ch;
190                SortedSet<Element> list = indexmap.computeIfAbsent(unicode,
191                        c -> new TreeSet<>(comparator));
192                list.add(element);
193            }
194        }
195    }
196
197    /**
198     * Add all the modules to index map.
199     */
200    protected void addModulesToIndexMap() {
201        for (ModuleElement mdle : configuration.modules) {
202            String mdleName = mdle.getQualifiedName().toString();
203            char ch = (mdleName.length() == 0)
204                    ? '*'
205                    : Character.toUpperCase(mdleName.charAt(0));
206            Character unicode = ch;
207            SortedSet<Element> list = indexmap.computeIfAbsent(unicode,
208                    c -> new TreeSet<>(comparator));
209            list.add(mdle);
210        }
211    }
212
213    /**
214     * Should this element be added to the index map?
215     */
216    protected boolean shouldAddToIndexMap(Element element) {
217        if (utils.isHidden(element)) {
218            return false;
219        }
220
221        if (utils.isPackage(element))
222            // Do not add to index map if -nodeprecated option is set and the
223            // package is marked as deprecated.
224            return !(noDeprecated && configuration.utils.isDeprecated(element));
225        else
226            // Do not add to index map if -nodeprecated option is set and if the
227            // element is marked as deprecated or the containing package is marked as
228            // deprecated.
229            return !(noDeprecated &&
230                    (configuration.utils.isDeprecated(element) ||
231                    configuration.utils.isDeprecated(utils.containingPackage(element))));
232    }
233
234    /**
235     * Return a map of all the individual member lists with Unicode character.
236     *
237     * @return Map index map.
238     */
239    public Map<Character, SortedSet<Element>> getIndexMap() {
240        return indexmap;
241    }
242
243    /**
244     * Return the sorted list of members, for passed Unicode Character.
245     *
246     * @param index index Unicode character.
247     * @return List member list for specific Unicode character.
248     */
249    public List<? extends Element> getMemberList(Character index) {
250        SortedSet<Element> set = indexmap.get(index);
251        if (set == null)
252            return null;
253        List<Element> out = new ArrayList<>();
254        out.addAll(set);
255        return out;
256    }
257
258    /**
259     * Array of IndexMap keys, Unicode characters.
260     */
261    public List<Character> index() {
262        return new ArrayList<>(indexmap.keySet());
263    }
264}
265