1 /*
2  * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.javadoc.internal.doclets.toolkit.util;
27 
28 import java.util.*;
29 import java.util.stream.Collectors;
30 
31 import javax.lang.model.element.Element;
32 import javax.lang.model.element.ModuleElement;
33 import javax.lang.model.element.PackageElement;
34 import javax.lang.model.element.TypeElement;
35 
36 import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem;
37 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
38 import jdk.javadoc.internal.doclets.toolkit.Messages;
39 
40 import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*;
41 
42 /**
43  *  An alphabetical index of {@link Element elements}.
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 public class IndexBuilder {
51 
52     /**
53      * Sets of elements keyed by the first character of the names of the
54      * elements in those sets.
55      */
56     private final Map<Character, SortedSet<IndexItem>> indexMap;
57 
58     /**
59      * Don't generate deprecated information if true.
60      */
61     private final boolean noDeprecated;
62 
63     /**
64      * Build this index only for classes?
65      */
66     private final boolean classesOnly;
67 
68     private final BaseConfiguration configuration;
69     private final Utils utils;
70     private final Comparator<IndexItem> comparator;
71 
72     /**
73      * Creates a new {@code IndexBuilder}.
74      *
75      * @param configuration the current configuration of the doclet
76      * @param noDeprecated  true if -nodeprecated option is used,
77      *                      false otherwise
78      */
IndexBuilder(BaseConfiguration configuration, boolean noDeprecated)79     public IndexBuilder(BaseConfiguration configuration,
80                         boolean noDeprecated)
81     {
82         this(configuration, noDeprecated, false);
83     }
84 
85     /**
86      * Creates a new {@code IndexBuilder}.
87      *
88      * @param configuration the current configuration of the doclet
89      * @param noDeprecated  true if -nodeprecated option is used,
90      *                      false otherwise
91      * @param classesOnly   include only classes in index
92      */
IndexBuilder(BaseConfiguration configuration, boolean noDeprecated, boolean classesOnly)93     public IndexBuilder(BaseConfiguration configuration,
94                         boolean noDeprecated,
95                         boolean classesOnly)
96     {
97         this.configuration = configuration;
98         this.utils = configuration.utils;
99 
100         Messages messages = configuration.getMessages();
101         if (classesOnly) {
102             messages.notice("doclet.Building_Index_For_All_Classes");
103         } else {
104             messages.notice("doclet.Building_Index");
105         }
106 
107         this.noDeprecated = noDeprecated;
108         this.classesOnly = classesOnly;
109         this.indexMap = new TreeMap<>();
110         comparator = utils.comparators.makeIndexComparator(classesOnly);
111         buildIndex();
112     }
113 
114     /**
115      * Indexes all the members in all the packages and all the classes.
116      */
buildIndex()117     private void buildIndex()  {
118         Set<TypeElement> classes = configuration.getIncludedTypeElements();
119         indexTypeElements(classes);
120         if (classesOnly) {
121             return;
122         }
123         Set<PackageElement> packages = configuration.getSpecifiedPackageElements();
124         if (packages.isEmpty()) {
125             packages = classes
126                     .stream()
127                     .map(utils::containingPackage)
128                     .filter(_package -> _package != null && !_package.isUnnamed())
129                     .collect(Collectors.toSet());
130         }
131         packages.forEach(this::indexPackage);
132         classes.stream()
133                .filter(this::shouldIndex)
134                .forEach(this::indexMembers);
135 
136         if (configuration.showModules) {
137             indexModules();
138         }
139     }
140 
141     /**
142      * Indexes all the members (fields, methods, constructors, etc.) of the
143      * provided type element.
144      *
145      * @param te TypeElement whose members are to be indexed
146      */
indexMembers(TypeElement te)147     private void indexMembers(TypeElement te) {
148         VisibleMemberTable vmt = configuration.getVisibleMemberTable(te);
149         indexElements(vmt.getVisibleMembers(FIELDS), te);
150         indexElements(vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_OPTIONAL), te);
151         indexElements(vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_REQUIRED), te);
152         indexElements(vmt.getVisibleMembers(METHODS), te);
153         indexElements(vmt.getVisibleMembers(CONSTRUCTORS), te);
154         indexElements(vmt.getVisibleMembers(ENUM_CONSTANTS), te);
155     }
156 
157     /**
158      * Indexes the provided elements.
159      *
160      * @param elements a collection of elements
161      */
indexElements(Iterable<? extends Element> elements, TypeElement typeElement)162     private void indexElements(Iterable<? extends Element> elements, TypeElement typeElement) {
163         for (Element element : elements) {
164             if (shouldIndex(element)) {
165                 String name = utils.getSimpleName(element);
166                 Character ch = keyCharacter(name);
167                 SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
168                 set.add(new IndexItem(element, typeElement, configuration.utils));
169             }
170         }
171     }
172 
173     /**
174      * Index the given type elements.
175      *
176      * @param elements type elements
177      */
indexTypeElements(Iterable<TypeElement> elements)178     private void indexTypeElements(Iterable<TypeElement> elements) {
179         for (TypeElement typeElement : elements) {
180             if (shouldIndex(typeElement)) {
181                 String name = utils.getSimpleName(typeElement);
182                 Character ch = keyCharacter(name);
183                 SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
184                 set.add(new IndexItem(typeElement, configuration.utils));
185             }
186         }
187     }
188 
keyCharacter(String s)189     private static Character keyCharacter(String s) {
190         return s.isEmpty() ? '*' : Character.toUpperCase(s.charAt(0));
191     }
192 
193     /**
194      * Indexes all the modules.
195      */
indexModules()196     private void indexModules() {
197         for (ModuleElement m : configuration.modules) {
198             Character ch = keyCharacter(m.getQualifiedName().toString());
199             SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
200             set.add(new IndexItem(m, configuration.utils));
201         }
202     }
203 
204     /**
205      * Index the given package element.
206      *
207      * @param packageElement the package element
208      */
indexPackage(PackageElement packageElement)209     private void indexPackage(PackageElement packageElement) {
210         if (shouldIndex(packageElement)) {
211             Character ch = keyCharacter(utils.getPackageName(packageElement));
212             SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
213             set.add(new IndexItem(packageElement, configuration.utils));
214         }
215     }
216 
217     /**
218      * Should this element be added to the index?
219      */
shouldIndex(Element element)220     private boolean shouldIndex(Element element) {
221         if (utils.hasHiddenTag(element)) {
222             return false;
223         }
224 
225         if (utils.isPackage(element)) {
226             // Do not add to index map if -nodeprecated option is set and the
227             // package is marked as deprecated.
228             return !(noDeprecated && configuration.utils.isDeprecated(element));
229         } else {
230             // Do not add to index map if -nodeprecated option is set and if the
231             // element is marked as deprecated or the containing package is marked as
232             // deprecated.
233             return !(noDeprecated &&
234                     (configuration.utils.isDeprecated(element) ||
235                     configuration.utils.isDeprecated(utils.containingPackage(element))));
236         }
237     }
238 
239     /**
240      * Returns a map representation of this index.
241      *
242      * @return map
243      */
asMap()244     public Map<Character, SortedSet<IndexItem>> asMap() {
245         return indexMap;
246     }
247 
248     /**
249      * Returns a sorted list of elements whose names start with the
250      * provided character.
251      *
252      * @param key index key
253      * @return list of elements keyed by the provided character
254      */
getMemberList(Character key)255     public List<IndexItem> getMemberList(Character key) {
256         SortedSet<IndexItem> set = indexMap.get(key);
257         if (set == null) {
258             return null;
259         }
260         return new ArrayList<>(set);
261     }
262 
263     /**
264      * Returns a list of index keys.
265      */
keys()266     public List<Character> keys() {
267         return new ArrayList<>(indexMap.keySet());
268     }
269 
270     /**
271      * Add search tags for the key {@code key}.
272      *
273      * @param key the index key
274      * @param searchTags the search tags
275      */
addSearchTags(char key, List<SearchIndexItem> searchTags)276     public void addSearchTags(char key, List<SearchIndexItem> searchTags) {
277         searchTags.forEach(searchTag -> {
278             SortedSet<IndexItem> set = indexMap.computeIfAbsent(key, c -> new TreeSet<>(comparator));
279             set.add(new IndexItem(searchTag));
280         });
281     }
282 
283 }
284