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