8253735: Cleanup SearchIndexItem API

Reviewed-by: hannesw
This commit is contained in:
Jonathan Gibbons 2020-10-06 17:32:33 +00:00
parent 54b340b44f
commit bd50ccd037
22 changed files with 1089 additions and 913 deletions

View File

@ -292,7 +292,7 @@ public abstract class AbstractExecutableMemberWriter extends AbstractMemberWrite
* @return the 1.4.x style anchor for the executable element. * @return the 1.4.x style anchor for the executable element.
*/ */
protected String getErasureAnchor(ExecutableElement executableElement) { protected String getErasureAnchor(ExecutableElement executableElement) {
final StringBuilder buf = new StringBuilder(writer.anchorName(executableElement)); final StringBuilder buf = new StringBuilder(executableElement.getSimpleName());
buf.append("("); buf.append("(");
List<? extends VariableElement> parameters = executableElement.getParameters(); List<? extends VariableElement> parameters = executableElement.getParameters();
boolean foundTypeVariable = false; boolean foundTypeVariable = false;

View File

@ -25,24 +25,17 @@
package jdk.javadoc.internal.doclets.formats.html; package jdk.javadoc.internal.doclets.formats.html;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.SortedSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement; import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.PackageElement; import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import com.sun.source.doctree.DocTree; import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem.Category;
import jdk.javadoc.internal.doclets.formats.html.markup.Entity; import jdk.javadoc.internal.doclets.formats.html.markup.Entity;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
import jdk.javadoc.internal.doclets.formats.html.markup.TagName; import jdk.javadoc.internal.doclets.formats.html.markup.TagName;
@ -50,10 +43,7 @@ import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode;
import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
import jdk.javadoc.internal.doclets.toolkit.Content; import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
@ -68,50 +58,35 @@ import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
* This code and its internal interfaces are subject to change or * This code and its internal interfaces are subject to change or
* deletion without notice.</b> * deletion without notice.</b>
* *
* @see IndexBuilder * @see IndexBuilder
*/ */
public class AbstractIndexWriter extends HtmlDocletWriter { public class AbstractIndexWriter extends HtmlDocletWriter {
/** protected final IndexBuilder mainIndex;
* The index of all the members with unicode character.
*/
protected IndexBuilder indexBuilder;
protected Navigation navBar; protected final Navigation navBar;
protected final Map<Character, List<SearchIndexItem>> tagSearchIndexMap;
/** /**
* This constructor will be used by {@link SplitIndexWriter}. Initializes * Initializes the common data for writers that can generate index files
* path to this file and relative path from this file. * based on the information in {@code configuration.mainIndex}.
* *
* @param configuration The current configuration * @param configuration the current configuration
* @param path Path to the file which is getting generated. * @param path path to the file which is getting generated.
* @param indexBuilder Unicode based Index from {@link IndexBuilder}
*/ */
protected AbstractIndexWriter(HtmlConfiguration configuration, protected AbstractIndexWriter(HtmlConfiguration configuration,
DocPath path, DocPath path) {
IndexBuilder indexBuilder) {
super(configuration, path); super(configuration, path);
this.indexBuilder = indexBuilder; this.mainIndex = configuration.mainIndex;
this.navBar = new Navigation(null, configuration, PageMode.INDEX, path); this.navBar = new Navigation(null, configuration, PageMode.INDEX, path);
Stream<SearchIndexItem> items =
searchItems.itemsOfCategories(Category.INDEX, Category.SYSTEM_PROPERTY)
.sorted(comparators.makeGenericSearchIndexComparator());
this.tagSearchIndexMap = buildSearchTagIndex(items);
} }
protected void addContents(Character uc, List<IndexItem> memberlist, protected void addContents(Character uc, SortedSet<IndexItem> memberlist,
Content contentTree) { Content contentTree) {
addHeading(uc, contentTree); addHeading(uc, contentTree);
HtmlTree dl = HtmlTree.DL(HtmlStyle.index); HtmlTree dl = HtmlTree.DL(HtmlStyle.index);
Map<String,Integer> duplicateLabelCheck = new HashMap<>(); for (IndexItem item : memberlist) {
memberlist.forEach(e -> duplicateLabelCheck.compute(e.getFullyQualifiedLabel(utils), addDescription(item, dl);
(k, v) -> v == null ? 1 : v + 1));
for (IndexItem indexItem : memberlist) {
addDescription(indexItem, dl,
duplicateLabelCheck.get(indexItem.getFullyQualifiedLabel(utils)) > 1);
} }
contentTree.add(dl); contentTree.add(dl);
} }
@ -125,79 +100,62 @@ public class AbstractIndexWriter extends HtmlDocletWriter {
contentTree.add(heading); contentTree.add(heading);
} }
protected void addDescription(IndexItem indexItem, Content dl, boolean addModuleInfo) { protected void addDescription(IndexItem indexItem, Content dl) {
SearchIndexItem si = indexItem.getSearchTag(); if (indexItem.isTagItem()) {
if (si != null) { addTagDescription(indexItem, dl);
addDescription(si, dl); } else if (indexItem.isElementItem()) {
} else { addElementDescription(indexItem, dl);
si = new SearchIndexItem();
si.setLabel(indexItem.getLabel());
addElementDescription(indexItem, dl, si, addModuleInfo);
searchItems.add(si);
} }
} }
/** /**
* Add one line summary comment for the element. * Add one line summary comment for the element.
* *
* @param indexItem the element to be documented * @param item the element to be documented
* @param dlTree the content tree to which the description will be added * @param dlTree the content tree to which the description will be added
* @param si the search index item
* @param addModuleInfo whether to include module information
*/ */
protected void addElementDescription(IndexItem indexItem, Content dlTree, SearchIndexItem si, protected void addElementDescription(IndexItem item, Content dlTree) {
boolean addModuleInfo) {
Content dt; Content dt;
Element element = indexItem.getElement(); Element element = item.getElement();
String label = indexItem.getLabel(); String label = item.getLabel();
switch (element.getKind()) { switch (element.getKind()) {
case MODULE: case MODULE:
dt = HtmlTree.DT(getModuleLink((ModuleElement)element, new StringContent(label))); dt = HtmlTree.DT(getModuleLink((ModuleElement) element, new StringContent(label)));
si.setCategory(Category.MODULES);
dt.add(" - ").add(contents.module_).add(" " + label); dt.add(" - ").add(contents.module_).add(" " + label);
break; break;
case PACKAGE: case PACKAGE:
dt = HtmlTree.DT(getPackageLink((PackageElement)element, new StringContent(label))); dt = HtmlTree.DT(getPackageLink((PackageElement) element, new StringContent(label)));
if (configuration.showModules) { if (configuration.showModules) {
si.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(element))); item.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(element)));
} }
si.setCategory(Category.PACKAGES);
dt.add(" - ").add(contents.package_).add(" " + label); dt.add(" - ").add(contents.package_).add(" " + label);
break; break;
case CLASS: case CLASS:
case ENUM: case ENUM:
case RECORD: case RECORD:
case ANNOTATION_TYPE: case ANNOTATION_TYPE:
case INTERFACE: case INTERFACE:
dt = HtmlTree.DT(getLink(new LinkInfoImpl(configuration, dt = HtmlTree.DT(getLink(new LinkInfoImpl(configuration,
LinkInfoImpl.Kind.INDEX, (TypeElement)element).strong(true))); LinkInfoImpl.Kind.INDEX, (TypeElement) element).strong(true)));
si.setContainingPackage(utils.getPackageName(utils.containingPackage(element)));
if (configuration.showModules && addModuleInfo) {
si.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(element)));
}
si.setCategory(Category.TYPES);
dt.add(" - "); dt.add(" - ");
addClassInfo((TypeElement)element, dt); addClassInfo((TypeElement) element, dt);
break; break;
default:
TypeElement containingType = indexItem.getTypeElement(); case CONSTRUCTOR:
case METHOD:
case FIELD:
case ENUM_CONSTANT:
TypeElement containingType = item.getContainingTypeElement();
dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.memberNameLink, dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.memberNameLink,
getDocLink(LinkInfoImpl.Kind.INDEX, containingType, element, new StringContent(label)))); getDocLink(LinkInfoImpl.Kind.INDEX, containingType, element, new StringContent(label))));
si.setContainingPackage(utils.getPackageName(utils.containingPackage(element)));
si.setContainingClass(utils.getSimpleName(containingType));
if (configuration.showModules && addModuleInfo) {
si.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(element)));
}
if (utils.isExecutableElement(element)) {
String url = HtmlTree.encodeURL(links.getName(getAnchor((ExecutableElement)element)));
if (!label.equals(url)) {
si.setUrl(url);
}
}
si.setCategory(Category.MEMBERS);
dt.add(" - "); dt.add(" - ");
addMemberDesc(element, containingType, dt); addMemberDesc(element, containingType, dt);
break; break;
default:
throw new Error();
} }
dlTree.add(dt); dlTree.add(dt);
Content dd = new HtmlTree(TagName.DD); Content dd = new HtmlTree(TagName.DD);
@ -224,19 +182,19 @@ public class AbstractIndexWriter extends HtmlDocletWriter {
)); ));
} }
protected void addDescription(SearchIndexItem sii, Content dlTree) { protected void addTagDescription(IndexItem item, Content dlTree) {
String siiPath = pathToRoot.isEmpty() ? "" : pathToRoot.getPath() + "/"; String itemPath = pathToRoot.isEmpty() ? "" : pathToRoot.getPath() + "/";
siiPath += sii.getUrl(); itemPath += item.getUrl();
HtmlTree labelLink = HtmlTree.A(siiPath, new StringContent(sii.getLabel())); HtmlTree labelLink = HtmlTree.A(itemPath, new StringContent(item.getLabel()));
Content dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.searchTagLink, labelLink)); Content dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.searchTagLink, labelLink));
dt.add(" - "); dt.add(" - ");
dt.add(contents.getContent("doclet.Search_tag_in", sii.getHolder())); dt.add(contents.getContent("doclet.Search_tag_in", item.getHolder()));
dlTree.add(dt); dlTree.add(dt);
Content dd = new HtmlTree(TagName.DD); Content dd = new HtmlTree(TagName.DD);
if (sii.getDescription().isEmpty()) { if (item.getDescription().isEmpty()) {
dd.add(Entity.NO_BREAK_SPACE); dd.add(Entity.NO_BREAK_SPACE);
} else { } else {
dd.add(sii.getDescription()); dd.add(item.getDescription());
} }
dlTree.add(dd); dlTree.add(dd);
} }
@ -313,85 +271,4 @@ public class AbstractIndexWriter extends HtmlDocletWriter {
return "I:" + links.getName(unicode); return "I:" + links.getName(unicode);
} }
/**
* @throws DocFileIOException if there is a problem creating any of the search index files
*/
protected void createSearchIndexFiles() throws DocFileIOException {
createSearchIndexFile(DocPaths.MODULE_SEARCH_INDEX_JS,
searchItems.itemsOfCategories(Category.MODULES),
"moduleSearchIndex");
if (!configuration.packages.isEmpty()) {
SearchIndexItem si = new SearchIndexItem();
si.setCategory(Category.PACKAGES);
si.setLabel(resources.getText("doclet.All_Packages"));
si.setUrl(DocPaths.ALLPACKAGES_INDEX.getPath());
searchItems.add(si);
}
createSearchIndexFile(DocPaths.PACKAGE_SEARCH_INDEX_JS,
searchItems.itemsOfCategories(Category.PACKAGES),
"packageSearchIndex");
SearchIndexItem si = new SearchIndexItem();
si.setCategory(Category.TYPES);
si.setLabel(resources.getText("doclet.All_Classes"));
si.setUrl(DocPaths.ALLCLASSES_INDEX.getPath());
searchItems.add(si);
createSearchIndexFile(DocPaths.TYPE_SEARCH_INDEX_JS,
searchItems.itemsOfCategories(Category.TYPES),
"typeSearchIndex");
createSearchIndexFile(DocPaths.MEMBER_SEARCH_INDEX_JS,
searchItems.itemsOfCategories(Category.MEMBERS),
"memberSearchIndex");
createSearchIndexFile(DocPaths.TAG_SEARCH_INDEX_JS,
searchItems.itemsOfCategories(Category.INDEX, Category.SYSTEM_PROPERTY),
"tagSearchIndex");
}
/**
* Creates a search index file.
*
* @param searchIndexJS the file for the JavaScript to be generated
* @param searchIndex the search index items
* @param varName the variable name to write in the JavaScript file
* @throws DocFileIOException if there is a problem creating the search index file
*/
protected void createSearchIndexFile(DocPath searchIndexJS,
Stream<SearchIndexItem> searchIndex,
String varName)
throws DocFileIOException
{
// The file needs to be created even if there are no searchIndex items
// File could be written straight-through, without an intermediate StringBuilder
Iterator<SearchIndexItem> index = searchIndex.iterator();
StringBuilder searchVar = new StringBuilder("[");
boolean first = true;
while (index.hasNext()) {
SearchIndexItem item = index.next();
if (first) {
searchVar.append(item.toString());
first = false;
} else {
searchVar.append(",").append(item.toString());
}
}
searchVar.append("];");
DocFile jsFile = DocFile.createFileForOutput(configuration, searchIndexJS);
try (Writer wr = jsFile.openWriter()) {
wr.write(varName);
wr.write(" = ");
wr.write(searchVar.toString());
wr.write("updateSearchResults();");
} catch (IOException ie) {
throw new DocFileIOException(jsFile, DocFileIOException.Mode.WRITE, ie);
}
}
private static Map<Character, List<SearchIndexItem>> buildSearchTagIndex(
Stream<? extends SearchIndexItem> searchItems)
{
return searchItems.collect(Collectors.groupingBy(i -> keyCharacter(i.getLabel())));
}
protected static Character keyCharacter(String s) {
return s.isEmpty() ? '*' : Character.toUpperCase(s.charAt(0));
}
} }

View File

@ -133,13 +133,12 @@ public class AllClassesIndexWriter extends HtmlDocletWriter {
.addTab(resources.errorSummary, e -> utils.isError((TypeElement)e)) .addTab(resources.errorSummary, e -> utils.isError((TypeElement)e))
.addTab(resources.annotationTypeSummary, utils::isAnnotationType) .addTab(resources.annotationTypeSummary, utils::isAnnotationType)
.setTabScript(i -> "show(" + i + ");"); .setTabScript(i -> "show(" + i + ");");
for (Character unicode : indexBuilder.keys()) { for (Character unicode : indexBuilder.getFirstCharacters()) {
for (IndexItem indexItem : indexBuilder.getMemberList(unicode)) { for (IndexItem indexItem : indexBuilder.getItems(unicode)) {
TypeElement typeElement = (TypeElement) indexItem.getElement(); TypeElement typeElement = (TypeElement) indexItem.getElement();
if (typeElement == null || !utils.isCoreClass(typeElement)) { if (typeElement != null && utils.isCoreClass(typeElement)) {
continue; addTableRow(table, typeElement);
} }
addTableRow(table, typeElement);
} }
} }
Content titleContent = contents.allClassesLabel; Content titleContent = contents.allClassesLabel;

View File

@ -125,7 +125,7 @@ public class ConstructorWriterImpl extends AbstractExecutableMemberWriter
} }
constructorDocTree.add(heading); constructorDocTree.add(heading);
return HtmlTree.SECTION(HtmlStyle.detail, constructorDocTree) return HtmlTree.SECTION(HtmlStyle.detail, constructorDocTree)
.setId(links.getName(writer.getAnchor(constructor))); .setId(links.getAnchor(constructor));
} }
@Override @Override

View File

@ -54,6 +54,7 @@ import jdk.javadoc.internal.doclets.toolkit.WriterFactory;
import jdk.javadoc.internal.doclets.toolkit.util.DocFile; import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
/** /**
* Configure the output based on the command-line options. * Configure the output based on the command-line options.
@ -93,7 +94,16 @@ public class HtmlConfiguration extends BaseConfiguration {
*/ */
public TypeElement currentTypeElement = null; // Set this TypeElement in the ClassWriter. public TypeElement currentTypeElement = null; // Set this TypeElement in the ClassWriter.
protected SearchIndexItems searchItems; /**
* The collections of items for the main index.
* This field is only initialized if {@code options.createIndex()}
* is {@code true}.
* This index is populated somewhat lazily:
* 1. items found in doc comments are found while generating declaration pages
* 2. items for elements are added in bulk before generating the index files
* 3. additional items are added as needed
*/
protected HtmlIndexBuilder mainIndex;
public final Contents contents; public final Contents contents;
@ -201,6 +211,9 @@ public class HtmlConfiguration extends BaseConfiguration {
} }
} }
} }
if (options.createIndex()) {
mainIndex = new HtmlIndexBuilder(this);
}
docPaths = new DocPaths(utils); docPaths = new DocPaths(utils);
setCreateOverview(); setCreateOverview();
setTopFile(docEnv); setTopFile(docEnv);
@ -352,10 +365,4 @@ public class HtmlConfiguration extends BaseConfiguration {
} }
return super.finishOptionSettings0(); return super.finishOptionSettings0();
} }
@Override
protected void initConfiguration(DocletEnvironment docEnv) {
super.initConfiguration(docEnv);
searchItems = new SearchIndexItems(utils);
}
} }

View File

@ -169,18 +169,20 @@ public class HtmlDoclet extends AbstractDoclet {
} }
if (options.createIndex()) { if (options.createIndex()) {
IndexBuilder indexBuilder = new IndexBuilder(configuration, nodeprecated); SystemPropertiesWriter.generate(configuration);
configuration.mainIndex.addElements();
if (options.splitIndex()) { if (options.splitIndex()) {
SplitIndexWriter.generate(configuration, indexBuilder); SplitIndexWriter.generate(configuration);
} else { } else {
SingleIndexWriter.generate(configuration, indexBuilder); SingleIndexWriter.generate(configuration);
} }
AllClassesIndexWriter.generate(configuration, IndexBuilder allClassesIndex = new IndexBuilder(configuration, nodeprecated, true);
new IndexBuilder(configuration, nodeprecated, true)); allClassesIndex.addElements();
AllClassesIndexWriter.generate(configuration, allClassesIndex);
if (!configuration.packages.isEmpty()) { if (!configuration.packages.isEmpty()) {
AllPackagesIndexWriter.generate(configuration); AllPackagesIndexWriter.generate(configuration);
} }
SystemPropertiesWriter.generate(configuration); configuration.mainIndex.createSearchIndexFiles();
} }
if (options.createOverview()) { if (options.createOverview()) {

View File

@ -155,8 +155,6 @@ public class HtmlDocletWriter {
*/ */
public final HtmlConfiguration configuration; public final HtmlConfiguration configuration;
protected final SearchIndexItems searchItems;
protected final HtmlOptions options; protected final HtmlOptions options;
protected final Utils utils; protected final Utils utils;
@ -216,12 +214,11 @@ public class HtmlDocletWriter {
*/ */
public HtmlDocletWriter(HtmlConfiguration configuration, DocPath path) { public HtmlDocletWriter(HtmlConfiguration configuration, DocPath path) {
this.configuration = configuration; this.configuration = configuration;
this.searchItems = configuration.searchItems;
this.options = configuration.getOptions(); this.options = configuration.getOptions();
this.contents = configuration.contents; this.contents = configuration.contents;
this.messages = configuration.messages; this.messages = configuration.messages;
this.resources = configuration.docResources; this.resources = configuration.docResources;
this.links = new Links(path); this.links = new Links(path, configuration.utils);
this.utils = configuration.utils; this.utils = configuration.utils;
this.comparators = utils.comparators; this.comparators = utils.comparators;
this.path = path; this.path = path;
@ -955,7 +952,7 @@ public class HtmlDocletWriter {
ExecutableElement ee = (ExecutableElement)element; ExecutableElement ee = (ExecutableElement)element;
return getLink(new LinkInfoImpl(configuration, context, typeElement) return getLink(new LinkInfoImpl(configuration, context, typeElement)
.label(label) .label(label)
.where(links.getName(getAnchor(ee, isProperty))) .where(links.getAnchor(ee, isProperty))
.strong(strong)); .strong(strong));
} }
@ -988,7 +985,7 @@ public class HtmlDocletWriter {
ExecutableElement emd = (ExecutableElement) element; ExecutableElement emd = (ExecutableElement) element;
return getLink(new LinkInfoImpl(configuration, context, typeElement) return getLink(new LinkInfoImpl(configuration, context, typeElement)
.label(label) .label(label)
.where(links.getName(getAnchor(emd)))); .where(links.getAnchor(emd)));
} else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
return getLink(new LinkInfoImpl(configuration, context, typeElement) return getLink(new LinkInfoImpl(configuration, context, typeElement)
.label(label).where(links.getName(element.getSimpleName().toString()))); .label(label).where(links.getName(element.getSimpleName().toString())));
@ -997,27 +994,6 @@ public class HtmlDocletWriter {
} }
} }
public String getAnchor(ExecutableElement executableElement) {
return getAnchor(executableElement, false);
}
public String getAnchor(ExecutableElement executableElement, boolean isProperty) {
if (isProperty) {
return executableElement.getSimpleName().toString();
}
String member = anchorName(executableElement);
String erasedSignature = utils.makeSignature(executableElement, null, true, true);
return member + erasedSignature;
}
public String anchorName(Element member) {
if (member.getKind() == ElementKind.CONSTRUCTOR) {
return "<init>";
} else {
return utils.getSimpleName(member);
}
}
public Content seeTagToContent(Element element, DocTree see) { public Content seeTagToContent(Element element, DocTree see) {
Kind kind = see.getKind(); Kind kind = see.getKind();
if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) { if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) {

View File

@ -0,0 +1,241 @@
/*
* Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.javadoc.internal.doclets.formats.html;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
import jdk.javadoc.internal.doclets.formats.html.markup.Links;
import jdk.javadoc.internal.doclets.toolkit.Resources;
import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
/**
* Extensions to {@code IndexBuilder} to fill in remaining fields
* in index items: {@code containingModule}, {@code containingPackage},
* {@code containingClass}, and {@code url}, and to write out the
* JavaScript files.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class HtmlIndexBuilder extends IndexBuilder {
private final HtmlConfiguration configuration;
private final Links links;
private final Resources resources;
private final Utils utils;
/**
* Creates a new {@code HtmlIndexBuilder}.
*
* @param configuration the current configuration of the doclet
*/
HtmlIndexBuilder(HtmlConfiguration configuration) {
super(configuration, configuration.getOptions().noDeprecated());
this.configuration = configuration;
links = new Links(DocPath.empty, configuration.utils);
resources = configuration.docResources;
utils = configuration.utils;
}
/**
* {@inheritDoc}
*
* After the initial work to add the element items, the remaining fields in
* the items are also initialized.
*/
public void addElements() {
super.addElements();
if (classesOnly) {
return;
}
Map<String,Integer> duplicateLabelCheck = new HashMap<>();
for (Character ch : getFirstCharacters()) {
for (IndexItem item : getItems(ch)) {
duplicateLabelCheck.compute(item.getFullyQualifiedLabel(utils),
(k, v) -> v == null ? 1 : v + 1);
}
}
for (Character ch : getFirstCharacters()) {
for (IndexItem item : getItems(ch)) {
if (item.isElementItem()) {
boolean addModuleInfo =
duplicateLabelCheck.get(item.getFullyQualifiedLabel(utils)) > 1;
addContainingInfo(item, addModuleInfo);
}
}
}
}
private void addContainingInfo(IndexItem item, boolean addModuleInfo) {
Element element = item.getElement();
switch (element.getKind()) {
case MODULE:
break;
case PACKAGE:
if (configuration.showModules) {
item.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(element)));
}
break;
case CLASS:
case ENUM:
case RECORD:
case ANNOTATION_TYPE:
case INTERFACE:
item.setContainingPackage(utils.getPackageName(utils.containingPackage(element)));
if (configuration.showModules && addModuleInfo) {
item.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(element)));
}
break;
case CONSTRUCTOR:
case METHOD:
case FIELD:
case ENUM_CONSTANT:
TypeElement containingType = item.getContainingTypeElement();
item.setContainingPackage(utils.getPackageName(utils.containingPackage(element)));
item.setContainingClass(utils.getSimpleName(containingType));
if (configuration.showModules && addModuleInfo) {
item.setContainingModule(utils.getFullyQualifiedName(utils.containingModule(element)));
}
if (utils.isExecutableElement(element)) {
String url = HtmlTree.encodeURL(links.getAnchor((ExecutableElement) element));
if (!url.equals(item.getLabel())) {
item.setUrl(url);
}
}
break;
default:
throw new Error();
}
}
/**
* Generates the set of index files used by interactive search.
*
* @throws DocFileIOException if there is a problem creating any of the search index files
*/
public void createSearchIndexFiles() throws DocFileIOException {
// add last-minute items
if (!configuration.packages.isEmpty()) {
IndexItem item = IndexItem.of(IndexItem.Category.PACKAGES,
resources.getText("doclet.All_Packages"),
DocPaths.ALLPACKAGES_INDEX);
add(item);
}
IndexItem item = IndexItem.of(IndexItem.Category.TYPES,
resources.getText("doclet.All_Classes"),
DocPaths.ALLCLASSES_INDEX);
add(item);
for (IndexItem.Category category : IndexItem.Category.values()) {
DocPath file;
String varName;
switch (category) {
case MODULES -> {
file = DocPaths.MODULE_SEARCH_INDEX_JS;
varName = "moduleSearchIndex";
}
case PACKAGES -> {
file = DocPaths.PACKAGE_SEARCH_INDEX_JS;
varName = "packageSearchIndex";
}
case TYPES -> {
file = DocPaths.TYPE_SEARCH_INDEX_JS;
varName = "typeSearchIndex";
}
case MEMBERS -> {
file = DocPaths.MEMBER_SEARCH_INDEX_JS;
varName = "memberSearchIndex";
}
case TAGS -> {
file = DocPaths.TAG_SEARCH_INDEX_JS;
varName = "tagSearchIndex";
}
default -> throw new Error();
}
createSearchIndexFile(file, getItems(category), varName);
}
}
/**
* Creates a search index file.
*
* @param searchIndexJS the file for the JavaScript to be generated
* @param indexItems the search index items
* @param varName the variable name to write in the JavaScript file
*
* @throws DocFileIOException if there is a problem creating the search index file
*/
private void createSearchIndexFile(DocPath searchIndexJS,
SortedSet<IndexItem> indexItems,
String varName)
throws DocFileIOException
{
// The file needs to be created even if there are no searchIndex items
DocFile jsFile = DocFile.createFileForOutput(configuration, searchIndexJS);
try (Writer wr = jsFile.openWriter()) {
wr.write(varName);
wr.write(" = [");
boolean first = true;
for (IndexItem item : indexItems) {
if (first) {
first = false;
} else {
wr.write(",");
}
wr.write(item.toJSON());
}
wr.write("];");
wr.write("updateSearchResults();");
} catch (IOException ie) {
throw new DocFileIOException(jsFile, DocFileIOException.Mode.WRITE, ie);
}
}
}

View File

@ -111,7 +111,7 @@ public class MethodWriterImpl extends AbstractExecutableMemberWriter
} }
methodDocTree.add(heading); methodDocTree.add(heading);
return HtmlTree.SECTION(HtmlStyle.detail, methodDocTree) return HtmlTree.SECTION(HtmlStyle.detail, methodDocTree)
.setId(links.getName(writer.getAnchor(method))); .setId(links.getAnchor(method));
} }
/** /**
@ -284,7 +284,7 @@ public class MethodWriterImpl extends AbstractExecutableMemberWriter
Content codeOverriddenTypeLink = HtmlTree.CODE(overriddenTypeLink); Content codeOverriddenTypeLink = HtmlTree.CODE(overriddenTypeLink);
Content methlink = writer.getLink( Content methlink = writer.getLink(
new LinkInfoImpl(writer.configuration, LinkInfoImpl.Kind.MEMBER, holder) new LinkInfoImpl(writer.configuration, LinkInfoImpl.Kind.MEMBER, holder)
.where(writer.links.getName(writer.getAnchor(method))) .where(writer.links.getAnchor(method))
.label(method.getSimpleName())); .label(method.getSimpleName()));
Content codeMethLink = HtmlTree.CODE(methlink); Content codeMethLink = HtmlTree.CODE(methlink);
Content dd = HtmlTree.DD(codeMethLink); Content dd = HtmlTree.DD(codeMethLink);

View File

@ -142,7 +142,7 @@ public class Navigation {
this.documentedPage = page; this.documentedPage = page;
this.path = path; this.path = path;
this.pathToRoot = path.parent().invert(); this.pathToRoot = path.parent().invert();
this.links = new Links(path); this.links = new Links(path, configuration.utils);
this.rowListTitle = configuration.getDocResources().getText("doclet.Navigation"); this.rowListTitle = configuration.getDocResources().getText("doclet.Navigation");
this.searchLabel = contents.getContent("doclet.search"); this.searchLabel = contents.getContent("doclet.search");
} }

View File

@ -1,198 +0,0 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.javadoc.internal.doclets.formats.html;
import javax.lang.model.element.Element;
/**
* Index item for search.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SearchIndexItem {
enum Category {
MODULES,
PACKAGES,
TYPES,
MEMBERS,
/**
* The category of items corresponding to {@code {@index}} tags.
*/
INDEX,
/**
* The category of items corresponding to {@code {@systemProperty}} tags.
*/
SYSTEM_PROPERTY
}
private Category category;
private String label = "";
private String url = "";
private String containingModule = "";
private String containingPackage = "";
private String containingClass = "";
private String holder = "";
private String description = "";
private Element element;
public void setLabel(String l) {
label = l;
}
public String getLabel() {
return label;
}
public void setUrl(String u) {
url = u;
}
public String getUrl() {
return url;
}
public void setContainingModule(String m) {
containingModule = m;
}
public void setContainingPackage(String p) {
containingPackage = p;
}
public void setContainingClass(String c) {
containingClass = c;
}
public void setCategory(Category c) {
category = c;
}
public void setHolder(String h) {
holder = h;
}
public String getHolder() {
return holder;
}
public void setDescription(String d) {
description = d;
}
public String getDescription() {
return description;
}
protected Category getCategory() {
return category;
}
public void setElement(Element element) {
this.element = element;
}
public Element getElement() {
return element;
}
@Override
public String toString() {
// TODO: Additional processing is required, see JDK-8238495
StringBuilder item = new StringBuilder();
switch (category) {
case MODULES:
item.append("{")
.append("\"l\":\"").append(label).append("\"")
.append("}");
break;
case PACKAGES:
item.append("{");
if (!containingModule.isEmpty()) {
item.append("\"m\":\"").append(containingModule).append("\",");
}
item.append("\"l\":\"").append(label).append("\"");
if (!url.isEmpty()) {
item.append(",\"u\":\"").append(url).append("\"");
}
item.append("}");
break;
case TYPES:
item.append("{");
if (!containingPackage.isEmpty()) {
item.append("\"p\":\"").append(containingPackage).append("\",");
}
if (!containingModule.isEmpty()) {
item.append("\"m\":\"").append(containingModule).append("\",");
}
item.append("\"l\":\"").append(label).append("\"");
if (!url.isEmpty()) {
item.append(",\"u\":\"").append(url).append("\"");
}
item.append("}");
break;
case MEMBERS:
item.append("{");
if (!containingModule.isEmpty()) {
item.append("\"m\":\"").append(containingModule).append("\",");
}
item.append("\"p\":\"").append(containingPackage).append("\",")
.append("\"c\":\"").append(containingClass).append("\",")
.append("\"l\":\"").append(label).append("\"");
if (!url.isEmpty()) {
item.append(",\"u\":\"").append(url).append("\"");
}
item.append("}");
break;
case INDEX:
case SYSTEM_PROPERTY:
item.append("{")
.append("\"l\":\"").append(label).append("\",")
.append("\"h\":\"").append(holder).append("\",");
if (!description.isEmpty()) {
item.append("\"d\":\"").append(description).append("\",");
}
item.append("\"u\":\"").append(url).append("\"")
.append("}");
break;
default:
throw new AssertionError("Unexpected category: " + category);
}
return item.toString();
}
/**
* Get the part of the label after the last dot, or whole label if no dots.
*
* @return the simple name
*/
public String getSimpleName() {
return label.substring(label.lastIndexOf('.') + 1);
}
}

View File

@ -1,158 +0,0 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.javadoc.internal.doclets.formats.html;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem.Category;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Stream;
/**
* A container for organizing {@linkplain SearchIndexItem search items}
* by {@linkplain Category category}.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public final class SearchIndexItems {
private final Map<Category, Set<SearchIndexItem>> items = new HashMap<>();
private final Utils utils;
public SearchIndexItems(Utils utils) {
this.utils = Objects.requireNonNull(utils);
}
/**
* Adds the specified item to this container.
*
* @param item
* the item to add
*/
public void add(SearchIndexItem item) {
Objects.requireNonNull(item);
items.computeIfAbsent(item.getCategory(), this::newSetForCategory)
.add(item);
}
private Set<SearchIndexItem> newSetForCategory(Category category) {
final Comparator<SearchIndexItem> cmp;
if (category == Category.TYPES) {
cmp = utils.comparators.makeTypeSearchIndexComparator();
} else {
cmp = utils.comparators.makeGenericSearchIndexComparator();
}
return new TreeSet<>(cmp);
}
/**
* Checks if there are items of any of the specified categories
* in this container.
*
* <p> Iff there exists an item {@code i} for which there is a category
* {@code c} from the specified categories such that
* {@code i.getCategory().equals(c)}, then {@code true} is returned.
*
* @param firstCategory
* the first category
* @param otherCategories
* other categories (optional)
*
* @return {@code true} if there are items of any of the specified categories,
* {@code false} otherwise
*
* @throws NullPointerException
* if there are {@code null} categories
*/
public boolean containsAnyOfCategories(Category firstCategory,
Category... otherCategories)
{
return itemsOfCategories(firstCategory, otherCategories)
.findAny()
.isPresent();
}
/**
* Returns a stream of items of any of the specified categories
* from this container.
*
* <p> The returned stream consists of all items {@code i} for which there
* is a category {@code c} from the specified categories such that
* {@code i.getCategory().equals(c)}. The stream may be empty.
*
* @param firstCategory
* the first category
* @param otherCategories
* other categories (optional)
*
* @return a stream of items of the specified categories
*
* @throws NullPointerException
* if there are {@code null} categories
*/
public Stream<SearchIndexItem> itemsOfCategories(Category firstCategory,
Category... otherCategories)
{
return concatenatedStreamOf(firstCategory, otherCategories)
.distinct()
.flatMap(this::itemsOf);
}
private Stream<SearchIndexItem> itemsOf(Category cat) {
Objects.requireNonNull(cat);
return items.getOrDefault(cat, Set.of()).stream();
}
/**
* Returns a concatenated stream of elements.
*
* <p> The elements of the returned stream are encountered in the following order:
* {@code first, remaining[0], remaining[1], ..., remaining[remaining.length - 1]}.
*
* @param first
* the first element
* @param remaining
* the remaining elements, if any
* @param <T>
* the type of elements
*
* @return the stream of elements
*
* @throws NullPointerException
* if {@code remaining} is {@code null}
*/
private static <T> Stream<T> concatenatedStreamOf(T first, T[] remaining) {
return Stream.concat(Stream.of(first), Stream.of(remaining));
}
}

View File

@ -28,7 +28,7 @@ package jdk.javadoc.internal.doclets.formats.html;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem.Category; import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents;
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
import jdk.javadoc.internal.doclets.formats.html.markup.Entity; import jdk.javadoc.internal.doclets.formats.html.markup.Entity;
@ -38,9 +38,9 @@ import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
import jdk.javadoc.internal.doclets.toolkit.Content; import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem.Category;
/** /**
@ -57,34 +57,26 @@ import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
*/ */
public class SingleIndexWriter extends AbstractIndexWriter { public class SingleIndexWriter extends AbstractIndexWriter {
private Set<Character> elements; private Set<Character> firstCharacters;
/** /**
* Construct the SingleIndexWriter with filename "index-all.html" and the * Construct the SingleIndexWriter with filename "index-all.html" and the
* {@link IndexBuilder} * {@link IndexBuilder}
* *
* @param configuration the configuration for this doclet * @param configuration the configuration for this doclet
* @param filename Name of the index file to be generated.
* @param indexBuilder Unicode based Index from {@link IndexBuilder}
*/ */
public SingleIndexWriter(HtmlConfiguration configuration, public SingleIndexWriter(HtmlConfiguration configuration) {
DocPath filename, super(configuration, DocPaths.INDEX_ALL);
IndexBuilder indexBuilder) {
super(configuration, filename, indexBuilder);
} }
/** /**
* Generate single index file, for all Unicode characters. * Generate single index file, for all Unicode characters.
* *
* @param configuration the configuration for this doclet * @param configuration the configuration for this doclet
* @param indexBuilder IndexBuilder built by {@link IndexBuilder}
* @throws DocFileIOException if there is a problem generating the index * @throws DocFileIOException if there is a problem generating the index
*/ */
public static void generate(HtmlConfiguration configuration, public static void generate(HtmlConfiguration configuration) throws DocFileIOException {
IndexBuilder indexBuilder) throws DocFileIOException { SingleIndexWriter indexgen = new SingleIndexWriter(configuration);
DocPath filename = DocPaths.INDEX_ALL;
SingleIndexWriter indexgen = new SingleIndexWriter(configuration,
filename, indexBuilder);
indexgen.generateIndexFile(); indexgen.generateIndexFile();
} }
@ -101,14 +93,10 @@ public class SingleIndexWriter extends AbstractIndexWriter {
navBar.setUserHeader(getUserHeaderFooter(true)); navBar.setUserHeader(getUserHeaderFooter(true));
headerContent.add(navBar.getContent(Navigation.Position.TOP)); headerContent.add(navBar.getContent(Navigation.Position.TOP));
Content mainContent = new ContentBuilder(); Content mainContent = new ContentBuilder();
elements = new TreeSet<>(indexBuilder.asMap().keySet()); firstCharacters = new TreeSet<>(mainIndex.getFirstCharacters());
elements.addAll(tagSearchIndexMap.keySet());
addLinksForIndexes(mainContent); addLinksForIndexes(mainContent);
for (Character unicode : elements) { for (Character ch : firstCharacters) {
if (tagSearchIndexMap.get(unicode) != null) { addContents(ch, mainIndex.getItems(ch), mainContent);
indexBuilder.addSearchTags(unicode, tagSearchIndexMap.get(unicode));
}
addContents(unicode, indexBuilder.getMemberList(unicode), mainContent);
} }
addLinksForIndexes(mainContent); addLinksForIndexes(mainContent);
HtmlTree footer = HtmlTree.FOOTER(); HtmlTree footer = HtmlTree.FOOTER();
@ -122,7 +110,6 @@ public class SingleIndexWriter extends AbstractIndexWriter {
contents.getContent("doclet.Index")))) contents.getContent("doclet.Index"))))
.addMainContent(mainContent) .addMainContent(mainContent)
.setFooter(footer)); .setFooter(footer));
createSearchIndexFiles();
printHtmlDocument(null, "index", body); printHtmlDocument(null, "index", body);
} }
@ -132,7 +119,7 @@ public class SingleIndexWriter extends AbstractIndexWriter {
* @param contentTree the content tree to which the links for indexes will be added * @param contentTree the content tree to which the links for indexes will be added
*/ */
protected void addLinksForIndexes(Content contentTree) { protected void addLinksForIndexes(Content contentTree) {
for (Character ch : elements) { for (Character ch : firstCharacters) {
String unicode = ch.toString(); String unicode = ch.toString();
contentTree.add( contentTree.add(
links.createLink(getNameForIndex(unicode), links.createLink(getNameForIndex(unicode),
@ -147,7 +134,8 @@ public class SingleIndexWriter extends AbstractIndexWriter {
contentTree.add(links.createLink(DocPaths.ALLPACKAGES_INDEX, contentTree.add(links.createLink(DocPaths.ALLPACKAGES_INDEX,
contents.allPackagesLabel)); contents.allPackagesLabel));
} }
if (searchItems.containsAnyOfCategories(Category.SYSTEM_PROPERTY)) { boolean anySystemProperties = !mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).isEmpty();
if (anySystemProperties) {
contentTree.add(getVerticalSeparator()); contentTree.add(getVerticalSeparator());
contentTree.add(links.createLink(DocPaths.SYSTEM_PROPERTIES, contents.systemPropertiesLabel)); contentTree.add(links.createLink(DocPaths.SYSTEM_PROPERTIES, contents.systemPropertiesLabel));
} }

View File

@ -29,12 +29,10 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.stream.Collectors;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem.Category; import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents;
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
import jdk.javadoc.internal.doclets.formats.html.markup.Entity; import jdk.javadoc.internal.doclets.formats.html.markup.Entity;
@ -47,6 +45,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem.Category;
/** /**
* Generate Separate Index Files for all the member names with Indexing in * Generate Separate Index Files for all the member names with Indexing in
@ -70,14 +69,12 @@ public class SplitIndexWriter extends AbstractIndexWriter {
* *
* @param configuration the configuration for this doclet * @param configuration the configuration for this doclet
* @param path Path to the file which is getting generated. * @param path Path to the file which is getting generated.
* @param indexBuilder Unicode based Index from {@link IndexBuilder}
* @param elements the collection of characters for which to generate index files * @param elements the collection of characters for which to generate index files
*/ */
public SplitIndexWriter(HtmlConfiguration configuration, public SplitIndexWriter(HtmlConfiguration configuration,
DocPath path, DocPath path,
IndexBuilder indexBuilder,
Collection<Character> elements) { Collection<Character> elements) {
super(configuration, path, indexBuilder); super(configuration, path);
this.indexElements = new ArrayList<>(elements); this.indexElements = new ArrayList<>(elements);
} }
@ -86,30 +83,19 @@ public class SplitIndexWriter extends AbstractIndexWriter {
* the members starting with the particular unicode character. * the members starting with the particular unicode character.
* *
* @param configuration the configuration for this doclet * @param configuration the configuration for this doclet
* @param indexBuilder IndexBuilder built by {@link IndexBuilder}
* @throws DocFileIOException if there is a problem generating the index files * @throws DocFileIOException if there is a problem generating the index files
*/ */
public static void generate(HtmlConfiguration configuration, public static void generate(HtmlConfiguration configuration) throws DocFileIOException {
IndexBuilder indexBuilder) throws DocFileIOException
{
DocPath path = DocPaths.INDEX_FILES; DocPath path = DocPaths.INDEX_FILES;
SortedSet<Character> keys = new TreeSet<>(indexBuilder.asMap().keySet()); IndexBuilder mainIndex = configuration.mainIndex;
Set<Character> searchItemsKeys = configuration.searchItems SortedSet<Character> keys = new TreeSet<>(mainIndex.getFirstCharacters());
.itemsOfCategories(Category.INDEX, Category.SYSTEM_PROPERTY)
.map(i -> keyCharacter(i.getLabel()))
.collect(Collectors.toSet());
keys.addAll(searchItemsKeys);
ListIterator<Character> li = new ArrayList<>(keys).listIterator(); ListIterator<Character> li = new ArrayList<>(keys).listIterator();
while (li.hasNext()) { while (li.hasNext()) {
Character ch = li.next(); Character ch = li.next();
DocPath filename = DocPaths.indexN(li.nextIndex()); DocPath filename = DocPaths.indexN(li.nextIndex());
SplitIndexWriter indexgen = new SplitIndexWriter(configuration, SplitIndexWriter indexgen = new SplitIndexWriter(configuration,
path.resolve(filename), path.resolve(filename), keys);
indexBuilder, keys);
indexgen.generateIndexFile(ch); indexgen.generateIndexFile(ch);
if (!li.hasNext()) {
indexgen.createSearchIndexFiles();
}
} }
} }
@ -135,10 +121,7 @@ public class SplitIndexWriter extends AbstractIndexWriter {
contents.getContent("doclet.Index")))); contents.getContent("doclet.Index"))));
Content mainContent = new ContentBuilder(); Content mainContent = new ContentBuilder();
addLinksForIndexes(mainContent); addLinksForIndexes(mainContent);
if (tagSearchIndexMap.get(unicode) != null) { addContents(unicode, mainIndex.getItems(unicode), mainContent);
indexBuilder.addSearchTags(unicode, tagSearchIndexMap.get(unicode));
}
addContents(unicode, indexBuilder.getMemberList(unicode), mainContent);
addLinksForIndexes(mainContent); addLinksForIndexes(mainContent);
main.add(mainContent); main.add(mainContent);
HtmlTree footer = HtmlTree.FOOTER(); HtmlTree footer = HtmlTree.FOOTER();
@ -173,7 +156,8 @@ public class SplitIndexWriter extends AbstractIndexWriter {
contentTree.add(links.createLink(pathToRoot.resolve(DocPaths.ALLPACKAGES_INDEX), contentTree.add(links.createLink(pathToRoot.resolve(DocPaths.ALLPACKAGES_INDEX),
contents.allPackagesLabel)); contents.allPackagesLabel));
} }
if (searchItems.containsAnyOfCategories(Category.SYSTEM_PROPERTY)) { boolean anySystemProperties = !mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).isEmpty();
if (anySystemProperties) {
contentTree.add(getVerticalSeparator()); contentTree.add(getVerticalSeparator());
contentTree.add(links.createLink(pathToRoot.resolve(DocPaths.SYSTEM_PROPERTIES), contentTree.add(links.createLink(pathToRoot.resolve(DocPaths.SYSTEM_PROPERTIES),
contents.systemPropertiesLabel)); contents.systemPropertiesLabel));

View File

@ -24,7 +24,7 @@
*/ */
package jdk.javadoc.internal.doclets.formats.html; package jdk.javadoc.internal.doclets.formats.html;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem.Category; import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents;
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
import jdk.javadoc.internal.doclets.formats.html.markup.FixedStringContent; import jdk.javadoc.internal.doclets.formats.html.markup.FixedStringContent;
@ -40,6 +40,8 @@ import jdk.javadoc.internal.doclets.toolkit.OverviewElement;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem.Category;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import java.nio.file.Path; import java.nio.file.Path;
@ -85,8 +87,8 @@ public class SystemPropertiesWriter extends HtmlDocletWriter {
} }
private static void generate(HtmlConfiguration configuration, DocPath fileName) throws DocFileIOException { private static void generate(HtmlConfiguration configuration, DocPath fileName) throws DocFileIOException {
boolean hasSystemProperties = configuration.searchItems boolean hasSystemProperties = configuration.mainIndex != null
.containsAnyOfCategories(Category.SYSTEM_PROPERTY); && !configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).isEmpty();
if (!hasSystemProperties) { if (!hasSystemProperties) {
// Cannot defer this check any further, because of the super() call // Cannot defer this check any further, because of the super() call
// that prints out notices on creating files, etc. // that prints out notices on creating files, etc.
@ -132,15 +134,15 @@ public class SystemPropertiesWriter extends HtmlDocletWriter {
* @param content HtmlTree content to which the links will be added * @param content HtmlTree content to which the links will be added
*/ */
protected void addSystemProperties(Content content) { protected void addSystemProperties(Content content) {
Map<String, List<SearchIndexItem>> searchIndexMap = groupSystemProperties(); Map<String, List<IndexItem>> searchIndexMap = groupSystemProperties();
Content separator = new StringContent(", "); Content separator = new StringContent(", ");
Table table = new Table(HtmlStyle.systemPropertiesSummary, HtmlStyle.summaryTable) Table table = new Table(HtmlStyle.systemPropertiesSummary, HtmlStyle.summaryTable)
.setCaption(contents.systemPropertiesSummaryLabel) .setCaption(contents.systemPropertiesSummaryLabel)
.setHeader(new TableHeader(contents.propertyLabel, contents.referencedIn)) .setHeader(new TableHeader(contents.propertyLabel, contents.referencedIn))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
for (Entry<String, List<SearchIndexItem>> entry : searchIndexMap.entrySet()) { for (Entry<String, List<IndexItem>> entry : searchIndexMap.entrySet()) {
Content propertyName = new StringContent(entry.getKey()); Content propertyName = new StringContent(entry.getKey());
List<SearchIndexItem> searchIndexItems = entry.getValue(); List<IndexItem> searchIndexItems = entry.getValue();
Content separatedReferenceLinks = new ContentBuilder(); Content separatedReferenceLinks = new ContentBuilder();
separatedReferenceLinks.add(createLink(searchIndexItems.get(0))); separatedReferenceLinks.add(createLink(searchIndexItems.get(0)));
for (int i = 1; i < searchIndexItems.size(); i++) { for (int i = 1; i < searchIndexItems.size(); i++) {
@ -152,24 +154,23 @@ public class SystemPropertiesWriter extends HtmlDocletWriter {
content.add(table); content.add(table);
} }
private Map<String, List<SearchIndexItem>> groupSystemProperties() { private Map<String, List<IndexItem>> groupSystemProperties() {
return searchItems return configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).stream()
.itemsOfCategories(Category.SYSTEM_PROPERTY) .collect(groupingBy(IndexItem::getLabel, TreeMap::new, toList()));
.collect(groupingBy(SearchIndexItem::getLabel, TreeMap::new, toList()));
} }
private Content createLink(SearchIndexItem i) { private Content createLink(IndexItem i) {
assert i.getCategory() == Category.SYSTEM_PROPERTY : i; assert i.getDocTree().getKind() == DocTree.Kind.SYSTEM_PROPERTY : i;
if (i.getElement() != null) { Element element = i.getElement();
if (i.getElement() instanceof OverviewElement) { if (element instanceof OverviewElement) {
return links.createLink(pathToRoot.resolve(i.getUrl()), return links.createLink(pathToRoot.resolve(i.getUrl()),
resources.getText("doclet.Overview")); resources.getText("doclet.Overview"));
} } else if (element instanceof DocletElement) {
DocletElement e = ((DocletElement) i.getElement()); DocletElement e = (DocletElement) element;
// Implementations of DocletElement do not override equals and // Implementations of DocletElement do not override equals and
// hashCode; putting instances of DocletElement in a map is not // hashCode; putting instances of DocletElement in a map is not
// incorrect, but might well be inefficient // incorrect, but might well be inefficient
String t = titles.computeIfAbsent(i.getElement(), utils::getHTMLTitle); String t = titles.computeIfAbsent(element, utils::getHTMLTitle);
if (t.isBlank()) { if (t.isBlank()) {
// The user should probably be notified (a warning?) that this // The user should probably be notified (a warning?) that this
// file does not have a title // file does not have a title

View File

@ -41,7 +41,6 @@ import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.IndexTree; import com.sun.source.doctree.IndexTree;
import com.sun.source.doctree.ParamTree; import com.sun.source.doctree.ParamTree;
import com.sun.source.doctree.SystemPropertyTree; import com.sun.source.doctree.SystemPropertyTree;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem.Category;
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
@ -59,6 +58,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
import jdk.javadoc.internal.doclets.toolkit.util.Utils; import jdk.javadoc.internal.doclets.toolkit.util.Utils;
/** /**
@ -111,7 +111,7 @@ public class TagletWriterImpl extends TagletWriter {
@Override @Override
protected Content indexTagOutput(Element element, DocTree tag) { protected Content indexTagOutput(Element element, DocTree tag) {
CommentHelper ch = utils.getCommentHelper(element); CommentHelper ch = utils.getCommentHelper(element);
IndexTree itt = (IndexTree)tag; IndexTree itt = (IndexTree) tag;
String tagText = ch.getText(itt.getSearchTerm()); String tagText = ch.getText(itt.getSearchTerm());
if (tagText.charAt(0) == '"' && tagText.charAt(tagText.length() - 1) == '"') { if (tagText.charAt(0) == '"' && tagText.charAt(tagText.length() - 1) == '"') {
@ -120,7 +120,7 @@ public class TagletWriterImpl extends TagletWriter {
} }
String desc = ch.getText(itt.getDescription()); String desc = ch.getText(itt.getDescription());
return createAnchorAndSearchIndex(element, tagText, desc, false); return createAnchorAndSearchIndex(element, tagText, desc, tag);
} }
@Override @Override
@ -283,7 +283,7 @@ public class TagletWriterImpl extends TagletWriter {
SystemPropertyTree itt = (SystemPropertyTree) tag; SystemPropertyTree itt = (SystemPropertyTree) tag;
String tagText = itt.getPropertyName().toString(); String tagText = itt.getPropertyName().toString();
return HtmlTree.CODE(createAnchorAndSearchIndex(element, tagText, return HtmlTree.CODE(createAnchorAndSearchIndex(element, tagText,
resources.getText("doclet.System_Property"), true)); resources.getText("doclet.System_Property"), tag));
} }
@Override @Override
@ -366,7 +366,7 @@ public class TagletWriterImpl extends TagletWriter {
} }
@SuppressWarnings("preview") @SuppressWarnings("preview")
private Content createAnchorAndSearchIndex(Element element, String tagText, String desc, boolean isSystemProperty) { private Content createAnchorAndSearchIndex(Element element, String tagText, String desc, DocTree tree) {
Content result = null; Content result = null;
if (isFirstSentence && inSummary) { if (isFirstSentence && inSummary) {
result = new StringContent(tagText); result = new StringContent(tagText);
@ -379,77 +379,60 @@ public class TagletWriterImpl extends TagletWriter {
} }
result = HtmlTree.SPAN(anchorName, HtmlStyle.searchTagResult, new StringContent(tagText)); result = HtmlTree.SPAN(anchorName, HtmlStyle.searchTagResult, new StringContent(tagText));
if (options.createIndex() && !tagText.isEmpty()) { if (options.createIndex() && !tagText.isEmpty()) {
SearchIndexItem si = new SearchIndexItem(); String holder = new SimpleElementVisitor14<String, Void>() {
si.setLabel(tagText);
si.setDescription(desc);
si.setUrl(htmlWriter.path.getPath() + "#" + anchorName);
new SimpleElementVisitor14<Void, Void>() {
@Override @Override
public Void visitModule(ModuleElement e, Void p) { public String visitModule(ModuleElement e, Void p) {
si.setHolder(resources.getText("doclet.module") return resources.getText("doclet.module")
+ " " + utils.getFullyQualifiedName(e)); + " " + utils.getFullyQualifiedName(e);
return null;
} }
@Override @Override
public Void visitPackage(PackageElement e, Void p) { public String visitPackage(PackageElement e, Void p) {
si.setHolder(resources.getText("doclet.package") return resources.getText("doclet.package")
+ " " + utils.getFullyQualifiedName(e)); + " " + utils.getFullyQualifiedName(e);
return null;
} }
@Override @Override
public Void visitType(TypeElement e, Void p) { public String visitType(TypeElement e, Void p) {
si.setHolder(utils.getTypeElementName(e, true) return utils.getTypeElementName(e, true)
+ " " + utils.getFullyQualifiedName(e)); + " " + utils.getFullyQualifiedName(e);
return null;
} }
@Override @Override
public Void visitExecutable(ExecutableElement e, Void p) { public String visitExecutable(ExecutableElement e, Void p) {
si.setHolder(utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e)) return utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e))
+ "." + utils.getSimpleName(e) + "." + utils.getSimpleName(e)
+ utils.flatSignature(e, htmlWriter.getCurrentPageElement())); + utils.flatSignature(e, htmlWriter.getCurrentPageElement());
return null;
} }
@Override @Override
public Void visitVariable(VariableElement e, Void p) { public String visitVariable(VariableElement e, Void p) {
TypeElement te = utils.getEnclosingTypeElement(e); return utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e))
si.setHolder(utils.getFullyQualifiedName(te) + "." + utils.getSimpleName(e)); + "." + utils.getSimpleName(e);
return null;
} }
@Override @Override
public Void visitUnknown(Element e, Void p) { public String visitUnknown(Element e, Void p) {
if (e instanceof DocletElement) { if (e instanceof DocletElement) {
DocletElement de = (DocletElement) e; DocletElement de = (DocletElement) e;
si.setElement(de); return switch (de.getSubKind()) {
switch (de.getSubKind()) { case OVERVIEW -> resources.getText("doclet.Overview");
case OVERVIEW: case DOCFILE -> getHolderName(de);
si.setHolder(resources.getText("doclet.Overview")); };
break;
case DOCFILE:
si.setHolder(getHolderName(de));
break;
default:
throw new IllegalStateException();
}
return null;
} else { } else {
return super.visitUnknown(e, p); return super.visitUnknown(e, p);
} }
} }
@Override @Override
protected Void defaultAction(Element e, Void p) { protected String defaultAction(Element e, Void p) {
si.setHolder(utils.getFullyQualifiedName(e)); return utils.getFullyQualifiedName(e);
return null;
} }
}.visit(element); }.visit(element);
si.setCategory(isSystemProperty ? Category.SYSTEM_PROPERTY : Category.INDEX); IndexItem item = IndexItem.of(element, tree, tagText, holder, desc,
configuration.searchItems.add(si); new DocLink(htmlWriter.path, anchorName));
configuration.mainIndex.add(item);
} }
} }
return result; return result;

View File

@ -25,10 +25,13 @@
package jdk.javadoc.internal.doclets.formats.html.markup; package jdk.javadoc.internal.doclets.formats.html.markup;
import javax.lang.model.element.ExecutableElement;
import jdk.javadoc.internal.doclets.formats.html.SectionName; import jdk.javadoc.internal.doclets.formats.html.SectionName;
import jdk.javadoc.internal.doclets.toolkit.Content; import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.util.DocLink; import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
/** /**
* Factory for HTML A elements, both links (with a {@code href} attribute) * Factory for HTML A elements, both links (with a {@code href} attribute)
@ -47,6 +50,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
public class Links { public class Links {
private final DocPath file; private final DocPath file;
private final Utils utils;
/** /**
* Creates a {@code Links} object for a specific file, to be written in a specific HTML version. * Creates a {@code Links} object for a specific file, to be written in a specific HTML version.
@ -56,8 +60,9 @@ public class Links {
* *
* @param file the file * @param file the file
*/ */
public Links(DocPath file) { public Links(DocPath file, Utils utils) {
this.file = file; this.file = file;
this.utils = utils;
} }
/** /**
@ -260,10 +265,36 @@ public class Links {
} }
/** /**
* Converts a name to a valid HTML name (id). * Returns the HTML id to use for an executable element.
* This depends on the HTML version specified when the {@code Links} object was created.
* *
* @param name the string that needs to be converted to a valid HTML name * @param executableElement the element
*
* @return the id
*/
public String getAnchor(ExecutableElement executableElement) {
return getAnchor(executableElement, false);
}
/**
* Returns the HTML id to use for an executable element.
*
* @param executableElement the element
* @param isProperty whether or not the element represents a property
*
* @return the id
*/
public String getAnchor(ExecutableElement executableElement, boolean isProperty) {
String a = isProperty
? executableElement.getSimpleName().toString()
: executableElement.getSimpleName()
+ utils.makeSignature(executableElement, null, true, true);
return getName(a);
}
/**
* Converts a name to a valid HTML id.
*
* @param name the string that needs to be converted to a valid HTML id
* @return a valid HTML name * @return a valid HTML name
*/ */
public String getName(String name) { public String getName(String name) {

View File

@ -407,10 +407,8 @@ public abstract class BaseConfiguration {
public boolean setOptions() throws DocletException { public boolean setOptions() throws DocletException {
initPackages(); initPackages();
initModules(); initModules();
if (!finishOptionSettings0() || !finishOptionSettings()) return finishOptionSettings0()
return false; && finishOptionSettings();
return true;
} }
private void initDestDirectory() throws DocletException { private void initDestDirectory() throws DocletException {

View File

@ -26,7 +26,6 @@
package jdk.javadoc.internal.doclets.toolkit.util; package jdk.javadoc.internal.doclets.toolkit.util;
import com.sun.source.doctree.SerialFieldTree; import com.sun.source.doctree.SerialFieldTree;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
@ -279,44 +278,12 @@ public class Comparators {
return indexUseComparator; return indexUseComparator;
} }
/**
* Returns a comparator for the {@code IndexItem}s in the index page. This is a composite
* comparator that must be able to compare all kinds {@code Element}s as well as
* {@code SearchIndexItem}s.
*
* @return a comparator for index page items.
*/
public Comparator<IndexItem> makeIndexComparator(boolean classesOnly) {
Comparator<Element> elementComparator = classesOnly
? makeAllClassesComparator()
: makeIndexElementComparator();
Comparator<SearchIndexItem> searchTagComparator =
makeGenericSearchIndexComparator();
return (o1, o2) -> {
// Compare two elements
if (o1.getElement() != null && o2.getElement() != null) {
return elementComparator.compare(o1.getElement(), o2.getElement());
}
// Compare two search tags
if (o1.getSearchTag() != null && o2.getSearchTag() != null) {
return searchTagComparator.compare(o1.getSearchTag(), o2.getSearchTag());
}
// Compare an element with a search tag.
// Compares labels, if those are equal put the search tag first.
int d = utils.compareStrings(o1.getLabel(), o2.getLabel());
if (d == 0) {
d = o1.getElement() == null ? 1 : -1;
}
return d;
};
}
private Comparator<TypeMirror> typeMirrorClassUseComparator = null; private Comparator<TypeMirror> typeMirrorClassUseComparator = null;
/** /**
* Compares the FullyQualifiedNames of two TypeMirrors * Returns a comparator that compares the fully qualified names of two type mirrors.
* @return *
* @return the comparator
*/ */
public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() { public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() {
if (typeMirrorClassUseComparator == null) { if (typeMirrorClassUseComparator == null) {
@ -332,10 +299,10 @@ public class Comparators {
private Comparator<TypeMirror> typeMirrorIndexUseComparator = null; private Comparator<TypeMirror> typeMirrorIndexUseComparator = null;
/** /**
* Compares the SimpleNames of TypeMirrors if equal then the * Returns a comparator that compares the simple names of two type mirrors,
* FullyQualifiedNames of TypeMirrors. * or the fully qualified names if the simple names are equal.
* *
* @return * @return the comparator
*/ */
public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() { public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() {
if (typeMirrorIndexUseComparator == null) { if (typeMirrorIndexUseComparator == null) {
@ -468,7 +435,7 @@ public class Comparators {
* argument is less than, equal to, or greater than the second. * argument is less than, equal to, or greater than the second.
*/ */
protected int compareFullyQualifiedNames(Element e1, Element e2) { protected int compareFullyQualifiedNames(Element e1, Element e2) {
// add simplename to be compatible // add simple name to be compatible
String thisElement = getFullyQualifiedName(e1); String thisElement = getFullyQualifiedName(e1);
String thatElement = getFullyQualifiedName(e2); String thatElement = getFullyQualifiedName(e2);
return utils.compareStrings(thisElement, thatElement); return utils.compareStrings(thisElement, thatElement);
@ -527,20 +494,20 @@ public class Comparators {
} }
private int getKindIndex(Element e) { private int getKindIndex(Element e) {
switch (e.getKind()) { return switch (e.getKind()) {
case MODULE: return 0; case MODULE -> 0;
case PACKAGE: return 1; case PACKAGE -> 1;
case CLASS: return 2; case CLASS -> 2;
case ENUM: return 3; case ENUM -> 3;
case ENUM_CONSTANT: return 4; case ENUM_CONSTANT -> 4;
case RECORD: return 5; case RECORD -> 5;
case INTERFACE: return 6; case INTERFACE -> 6;
case ANNOTATION_TYPE: return 7; case ANNOTATION_TYPE -> 7;
case FIELD: return 8; case FIELD -> 8;
case CONSTRUCTOR: return 9; case CONSTRUCTOR -> 9;
case METHOD: return 10; case METHOD -> 10;
default: throw new IllegalArgumentException(e.getKind().toString()); default -> throw new IllegalArgumentException(e.getKind().toString());
} };
} }
@SuppressWarnings("preview") @SuppressWarnings("preview")
@ -598,48 +565,4 @@ public class Comparators {
}.visit(e); }.visit(e);
} }
} }
/**
* Returns a Comparator for SearchIndexItems representing types. Items are
* compared by short name, or full string representation if names are equal.
*
* @return a Comparator
*/
public Comparator<SearchIndexItem> makeTypeSearchIndexComparator() {
return (SearchIndexItem sii1, SearchIndexItem sii2) -> {
int result = utils.compareStrings(sii1.getSimpleName(), sii2.getSimpleName());
if (result == 0) {
// TreeSet needs this to be consistent with equal so we do
// a plain comparison of string representations as fallback.
result = sii1.toString().compareTo(sii2.toString());
}
return result;
};
}
private Comparator<SearchIndexItem> genericSearchIndexComparator = null;
/**
* Returns a Comparator for SearchIndexItems representing modules, packages, or members.
* Items are compared by label (member name plus signature for members, package name for
* packages, and module name for modules). If labels are equal then full string
* representation is compared.
*
* @return a Comparator
*/
public Comparator<SearchIndexItem> makeGenericSearchIndexComparator() {
if (genericSearchIndexComparator == null) {
genericSearchIndexComparator = (SearchIndexItem sii1, SearchIndexItem sii2) -> {
int result = utils.compareStrings(sii1.getLabel(), sii2.getLabel());
if (result == 0) {
// TreeSet needs this to be consistent with equal so we do
// a plain comparison of string representations as fallback.
result = sii1.toString().compareTo(sii2.toString());
}
return result;
};
}
return genericSearchIndexComparator;
}
} }

View File

@ -33,14 +33,18 @@ import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.PackageElement; import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem; import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
import jdk.javadoc.internal.doclets.toolkit.Messages; import jdk.javadoc.internal.doclets.toolkit.Messages;
import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*; import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*;
/** /**
* An alphabetical index of {@link Element elements}. * An alphabetical index of elements, search tags, and other items.
* Two tables are maintained:
* one is indexed by the first character of each items name;
* the other is index by the item's category, indicating the JavaScript
* file in which the item should be written.
* *
* <p><b>This is NOT part of any supported API. * <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk. * If you write code that depends on this, you do so at your own risk.
@ -50,10 +54,16 @@ import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.
public class IndexBuilder { public class IndexBuilder {
/** /**
* Sets of elements keyed by the first character of the names of the * Sets of items keyed by the first character of the names (labels)
* elements in those sets. * of the items in those sets.
*/ */
private final Map<Character, SortedSet<IndexItem>> indexMap; private final Map<Character, SortedSet<IndexItem>> itemsByFirstChar;
/**
* Sets of items keyed by the {@link IndexItem.Category category}
* of the items in those sets.
*/
private final Map<IndexItem.Category, SortedSet<IndexItem>> itemsByCategory;
/** /**
* Don't generate deprecated information if true. * Don't generate deprecated information if true.
@ -63,11 +73,15 @@ public class IndexBuilder {
/** /**
* Build this index only for classes? * Build this index only for classes?
*/ */
private final boolean classesOnly; protected final boolean classesOnly;
private final BaseConfiguration configuration; private final BaseConfiguration configuration;
private final Utils utils; private final Utils utils;
private final Comparator<IndexItem> comparator;
/**
* The comparator used for the sets in {@code itemsByFirstChar}.
*/
private final Comparator<IndexItem> mainComparator;
/** /**
* Creates a new {@code IndexBuilder}. * Creates a new {@code IndexBuilder}.
@ -106,15 +120,18 @@ public class IndexBuilder {
this.noDeprecated = noDeprecated; this.noDeprecated = noDeprecated;
this.classesOnly = classesOnly; this.classesOnly = classesOnly;
this.indexMap = new TreeMap<>();
comparator = utils.comparators.makeIndexComparator(classesOnly); itemsByFirstChar = new TreeMap<>();
buildIndex(); itemsByCategory = new EnumMap<>(IndexItem.Category.class);
mainComparator = makeIndexComparator(classesOnly);
} }
/** /**
* Indexes all the members in all the packages and all the classes. * Adds all the selected modules, packages, types and their members to the index,
* or just the type elements if {@code classesOnly} is {@code true}.
*/ */
private void buildIndex() { public void addElements() {
Set<TypeElement> classes = configuration.getIncludedTypeElements(); Set<TypeElement> classes = configuration.getIncludedTypeElements();
indexTypeElements(classes); indexTypeElements(classes);
if (classesOnly) { if (classesOnly) {
@ -138,6 +155,65 @@ public class IndexBuilder {
} }
} }
/**
* Adds an individual item to the two collections of items.
*
* @param item the item to add
*/
public void add(IndexItem item) {
Objects.requireNonNull(item);
itemsByFirstChar.computeIfAbsent(keyCharacter(item.getLabel()),
c -> new TreeSet<>(mainComparator))
.add(item);
itemsByCategory.computeIfAbsent(item.getCategory(),
c -> new TreeSet<>(mainComparator))
.add(item);
}
/**
* Returns a sorted list of items whose names start with the
* provided character.
*
* @param key index key
* @return list of items keyed by the provided character
*/
public SortedSet<IndexItem> getItems(Character key) {
return itemsByFirstChar.get(key);
}
/**
* Returns a list of index keys.
*/
public List<Character> getFirstCharacters() {
return new ArrayList<>(itemsByFirstChar.keySet());
}
/**
* Returns a sorted list of items in a given category.
*
* @param cat the category
* @return list of items keyed by the provided character
*/
public SortedSet<IndexItem> getItems(IndexItem.Category cat) {
Objects.requireNonNull(cat);
return itemsByCategory.getOrDefault(cat, Collections.emptySortedSet());
}
/**
* Returns a sorted list of items with a given kind of doc tree.
*
* @param kind the kind
* @return list of items keyed by the provided character
*/
public SortedSet<IndexItem> getItems(DocTree.Kind kind) {
Objects.requireNonNull(kind);
return itemsByCategory.getOrDefault(IndexItem.Category.TAGS, Collections.emptySortedSet()).stream()
.filter(i -> i.getDocTree().getKind() == kind)
.collect(Collectors.toCollection(() -> new TreeSet<>(mainComparator)));
}
/** /**
* Indexes all the members (fields, methods, constructors, etc.) of the * Indexes all the members (fields, methods, constructors, etc.) of the
* provided type element. * provided type element.
@ -146,26 +222,23 @@ public class IndexBuilder {
*/ */
private void indexMembers(TypeElement te) { private void indexMembers(TypeElement te) {
VisibleMemberTable vmt = configuration.getVisibleMemberTable(te); VisibleMemberTable vmt = configuration.getVisibleMemberTable(te);
indexElements(vmt.getVisibleMembers(FIELDS), te); indexMembers(te, vmt.getVisibleMembers(FIELDS));
indexElements(vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_OPTIONAL), te); indexMembers(te, vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_OPTIONAL));
indexElements(vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_REQUIRED), te); indexMembers(te, vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_REQUIRED));
indexElements(vmt.getVisibleMembers(METHODS), te); indexMembers(te, vmt.getVisibleMembers(METHODS));
indexElements(vmt.getVisibleMembers(CONSTRUCTORS), te); indexMembers(te, vmt.getVisibleMembers(CONSTRUCTORS));
indexElements(vmt.getVisibleMembers(ENUM_CONSTANTS), te); indexMembers(te, vmt.getVisibleMembers(ENUM_CONSTANTS));
} }
/** /**
* Indexes the provided elements. * Indexes the provided elements.
* *
* @param elements a collection of elements * @param members a collection of elements
*/ */
private void indexElements(Iterable<? extends Element> elements, TypeElement typeElement) { private void indexMembers(TypeElement typeElement, Iterable<? extends Element> members) {
for (Element element : elements) { for (Element member : members) {
if (shouldIndex(element)) { if (shouldIndex(member)) {
String name = utils.getSimpleName(element); add(IndexItem.of(typeElement, member, utils));
Character ch = keyCharacter(name);
SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
set.add(new IndexItem(element, typeElement, utils));
} }
} }
} }
@ -178,26 +251,17 @@ public class IndexBuilder {
private void indexTypeElements(Iterable<TypeElement> elements) { private void indexTypeElements(Iterable<TypeElement> elements) {
for (TypeElement typeElement : elements) { for (TypeElement typeElement : elements) {
if (shouldIndex(typeElement)) { if (shouldIndex(typeElement)) {
String name = utils.getSimpleName(typeElement); add(IndexItem.of(typeElement, utils));
Character ch = keyCharacter(name);
SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
set.add(new IndexItem(typeElement, utils));
} }
} }
} }
private static Character keyCharacter(String s) {
return s.isEmpty() ? '*' : Character.toUpperCase(s.charAt(0));
}
/** /**
* Indexes all the modules. * Indexes all the modules.
*/ */
private void indexModules() { private void indexModules() {
for (ModuleElement m : configuration.modules) { for (ModuleElement m : configuration.modules) {
Character ch = keyCharacter(m.getQualifiedName().toString()); add(IndexItem.of(m, utils));
SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
set.add(new IndexItem(m, utils));
} }
} }
@ -208,9 +272,7 @@ public class IndexBuilder {
*/ */
private void indexPackage(PackageElement packageElement) { private void indexPackage(PackageElement packageElement) {
if (shouldIndex(packageElement)) { if (shouldIndex(packageElement)) {
Character ch = keyCharacter(utils.getPackageName(packageElement)); add(IndexItem.of(packageElement, utils));
SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
set.add(new IndexItem(packageElement, utils));
} }
} }
@ -236,48 +298,46 @@ public class IndexBuilder {
} }
} }
/** private static Character keyCharacter(String s) {
* Returns a map representation of this index. return s.isEmpty() ? '*' : Character.toUpperCase(s.charAt(0));
*
* @return map
*/
public Map<Character, SortedSet<IndexItem>> asMap() {
return indexMap;
} }
/**
* Returns a sorted list of elements whose names start with the
* provided character.
*
* @param key index key
* @return list of elements keyed by the provided character
*/
public List<IndexItem> getMemberList(Character key) {
SortedSet<IndexItem> set = indexMap.get(key);
if (set == null) {
return null;
}
return new ArrayList<>(set);
}
/** /**
* Returns a list of index keys. * Returns a comparator for the {@code IndexItem}s in the index page.
*/ * This is a composite comparator that must be able to compare all kinds of items:
public List<Character> keys() { * for element items, tag items, and others.
return new ArrayList<>(indexMap.keySet());
}
/**
* Add search tags for the key {@code key}.
* *
* @param key the index key * @return a comparator for index page items
* @param searchTags the search tags
*/ */
public void addSearchTags(char key, List<SearchIndexItem> searchTags) { private Comparator<IndexItem> makeIndexComparator(boolean classesOnly) {
searchTags.forEach(searchTag -> { Comparator<Element> elementComparator = classesOnly
SortedSet<IndexItem> set = indexMap.computeIfAbsent(key, c -> new TreeSet<>(comparator)); ? utils.comparators.makeAllClassesComparator()
set.add(new IndexItem(searchTag)); : utils.comparators.makeIndexElementComparator();
});
Comparator<IndexItem> labelComparator =
(ii1, ii2) -> utils.compareStrings(ii1.getLabel(), ii2.getLabel());
Comparator<IndexItem> searchTagComparator =
labelComparator
.thenComparing(IndexItem::getHolder)
.thenComparing(IndexItem::getDescription)
.thenComparing(IndexItem::getUrl);
return (ii1, ii2) -> {
// If both are element items, compare the elements
if (ii1.isElementItem() && ii2.isElementItem()) {
return elementComparator.compare(ii1.getElement(), ii2.getElement());
}
// If one is an element item, compare labels; if equal, put element item last
if (ii1.isElementItem() || ii2.isElementItem()) {
int d = labelComparator.compare(ii1, ii2);
return d != 0 ? d : ii1.isElementItem() ? 1 : -1;
}
// Otherwise, compare labels and other fields of the items
return searchTagComparator.compare(ii1, ii2);
};
} }
} }

View File

@ -25,16 +25,56 @@
package jdk.javadoc.internal.doclets.toolkit.util; package jdk.javadoc.internal.doclets.toolkit.util;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem; import java.util.Objects;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement; import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.PackageElement; import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.SimpleElementVisitor14;
import com.sun.source.doctree.DocTree;
/** /**
* A holder for an indexed {@link Element} or {@link SearchIndexItem}. * An item to be included in the index pages and in interactive search.
*
* <p>
* Items are primarily defined by their position in the documentation,
* which is one of:
*
* <ul>
* <li>An element (module, package, type or member)
* <li>One of a small set of tags in the doc comment for an element:
* {@code {@index ...}}, {@code {@systemProperty ...}}, etc
* <li>One of a small set of outliers, corresponding to summary pages:
* "All Classes", "All Packages", etc
* </ul>
*
* <p>
* All items have a "label", which is the presentation string used
* to display the item in the list of matching choices. The
* label is specified when the item is created. Items also
* have a "url" and a "description", which are provided by
* the specific doclet.
*
* <p>
* Each item provides details to be included in the search index files
* read and processed by JavaScript.
* Items have a "category", which is normally derived from the element
* kind or doc tree kind; it corresponds to the JavaScript file
* in which this item will be written.
*
* <p>
* Items for an element may have one or more of the following:
* "containing module", "containing package", "containing type".
*
* <p>
* Items for a node in a doc tree have a "holder", which is a
* text form of the enclosing element or page.
* They will typically also have a "description" derived from
* content in the doc tree node.
*
* *
* <p><b>This is NOT part of any supported API. * <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk. * If you write code that depends on this, you do so at your own risk.
@ -43,71 +83,494 @@ import javax.lang.model.element.TypeElement;
*/ */
public class IndexItem { public class IndexItem {
/**
* The "category" used to group items for the interactive search index.
* Categories correspond directly to the JavaScript files that will be generated.
*/
public enum Category {
MODULES,
PACKAGES,
TYPES,
MEMBERS,
TAGS
}
/**
* The presentation string for the item. It must be non-empty.
*/
private final String label;
/**
* The element for the item. It is only null for items for summary pages that are not
* associated with any specific element.
*
*/
private final Element element; private final Element element;
private final SearchIndexItem searchTag;
private String label;
private TypeElement typeElement;
public IndexItem(SearchIndexItem searchTag) { /**
this.element = null; * The URL pointing to the element, doc tree or page being indexed.
this.searchTag = searchTag; * It may be empty if the information can be determined from other fields.
this.label = searchTag.getLabel(); */
private String url = "";
/**
* The containing module, if any, for the item.
* It will be empty if the element is not in a package, and may be omitted if the
* name of the package is unique.
*/
private String containingModule = "";
/**
* The containing package, if any, for the item.
*/
private String containingPackage = "";
/**
* The containing class, if any, for the item.
*/
private String containingClass = "";
/**
* Creates an index item for a module element.
*
* @param moduleElement the element
* @param utils the common utilities class
*
* @return the item
*/
public static IndexItem of(ModuleElement moduleElement, Utils utils) {
return new IndexItem(moduleElement, utils.getFullyQualifiedName(moduleElement));
} }
private IndexItem(Element element) { /**
this.element = element; * Creates an index item for a package element.
this.searchTag = null; *
* @param packageElement the element
* @param utils the common utilities class
*
* @return the item
*/
public static IndexItem of(PackageElement packageElement, Utils utils) {
return new IndexItem(packageElement, utils.getPackageName(packageElement));
} }
public IndexItem(TypeElement typeElement, Utils utils) { /**
this(typeElement); * Creates an index item for a type element.
this.label = utils.getSimpleName(typeElement); * Note: use {@code getElement()} to access this value, not {@code getTypeElement}.
*
* @param typeElement the element
* @param utils the common utilities class
*
* @return the item
*/
public static IndexItem of(TypeElement typeElement, Utils utils) {
return new IndexItem(typeElement, utils.getSimpleName(typeElement));
} }
public IndexItem(ModuleElement moduleElement, Utils utils) { /**
this(moduleElement); * Creates an index item for a member element.
this.label = utils.getFullyQualifiedName(moduleElement); * Note: the given type element may not be the same as the enclosing element of the member
} * in cases where the enclosing element is not visible in the documentation.
*
public IndexItem(PackageElement packageElement, Utils utils) { * @param typeElement the element that contains the member
this(packageElement); * @param member the member
this.label = utils.getPackageName(packageElement); * @param utils the common utilities class
} *
* @return the item
public IndexItem(Element member, TypeElement typeElement, Utils utils) { *
this(member); * @see #getContainingTypeElement()
this.typeElement = typeElement; */
public static IndexItem of(TypeElement typeElement, Element member, Utils utils) {
String name = utils.getSimpleName(member); String name = utils.getSimpleName(member);
if (utils.isExecutableElement(member)) { if (utils.isExecutableElement(member)) {
ExecutableElement ee = (ExecutableElement)member; ExecutableElement ee = (ExecutableElement)member;
name += utils.flatSignature(ee, typeElement); name += utils.flatSignature(ee, typeElement);
} }
this.label = name; return new IndexItem(member, name) {
@Override
public TypeElement getContainingTypeElement() {
return typeElement;
}
};
} }
/**
* Creates an index item for a node in the doc comment for an element.
* The node should only be one that gives rise to an entry in the index.
*
* @param element the element
* @param docTree the node in the doc comment
* @param label the label
* @param holder the holder for the comment
* @param description the description of the item
* @param link the root-relative link to the item in the generated docs
*
* @return the item
*/
public static IndexItem of(Element element, DocTree docTree, String label,
String holder, String description, DocLink link) {
Objects.requireNonNull(element);
Objects.requireNonNull(holder);
Objects.requireNonNull(description);
Objects.requireNonNull(link);
switch (docTree.getKind()) {
case INDEX, SYSTEM_PROPERTY -> { }
default -> throw new IllegalArgumentException(docTree.getKind().toString());
}
return new IndexItem(element, label, link.toString()) {
@Override
public DocTree getDocTree() {
return docTree;
}
@Override
public Category getCategory() {
return getCategory(docTree);
}
@Override
public String getHolder() {
return holder;
}
@Override
public String getDescription() {
return description;
}
};
}
/**
* Creates an index item for a summary page, that is not associated with any element or
* node in a doc comment.
*
* @param category the category for the item
* @param label the label for the item
* @param path the path for the page
*
* @return the item
*/
public static IndexItem of(Category category, String label, DocPath path) {
Objects.requireNonNull(category);
return new IndexItem(null, label, path.getPath()) {
@Override
public DocTree getDocTree() {
return null;
}
@Override
public Category getCategory() {
return category;
}
@Override
public String getHolder() {
return "";
}
@Override
public String getDescription() {
return "";
}
};
}
private IndexItem(Element element, String label) {
if (label.isEmpty()) {
throw new IllegalArgumentException();
}
this.element = element;
this.label = label;
}
private IndexItem(Element element, String label, String url) {
this(element, label);
setUrl(url);
}
/**
* Returns the label of the item.
*
* @return the label
*/
public String getLabel() { public String getLabel() {
return label; return label;
} }
/**
* Returns the part of the label after the last dot, or the whole label if there are no dots.
*
* @return the simple name
*/
public String getSimpleName() {
return label.substring(label.lastIndexOf('.') + 1);
}
/**
* Returns the label with a fully-qualified type name.
* (Used to determine if labels are unique or need to be qualified.)
*
* @param utils the common utilities class
*
* @return the fully qualified name
*/
public String getFullyQualifiedLabel(Utils utils) { public String getFullyQualifiedLabel(Utils utils) {
TypeElement typeElement = getContainingTypeElement();
if (typeElement != null) { if (typeElement != null) {
return utils.getFullyQualifiedName(typeElement) + "." + label; return utils.getFullyQualifiedName(typeElement) + "." + label;
} else if (element != null) { } else if (isElementItem()) {
return utils.getFullyQualifiedName(element); return utils.getFullyQualifiedName(element);
} else { } else {
return label; return label;
} }
} }
/**
* Returns the element associate with this item, or {@code null}.
*
* @return the element
*/
public Element getElement() { public Element getElement() {
return element; return element;
} }
public SearchIndexItem getSearchTag() { /**
return searchTag; * Returns the category for this item, that indicates the JavaScript file
* in which this item should be written.
*
* @return the category
*/
public Category getCategory() {
return getCategory(element);
} }
public TypeElement getTypeElement() { protected Category getCategory(DocTree docTree) {
return typeElement; return switch (docTree.getKind()) {
case INDEX, SYSTEM_PROPERTY -> Category.TAGS;
default -> throw new IllegalArgumentException(docTree.getKind().toString());
};
}
@SuppressWarnings("preview")
protected Category getCategory(Element element) {
return new SimpleElementVisitor14<Category, Void>() {
@Override
public Category visitModule(ModuleElement t, Void v) {
return Category.MODULES;
}
@Override
public Category visitPackage(PackageElement e, Void v) {
return Category.PACKAGES;
}
@Override
public Category visitType(TypeElement e, Void v) {
return Category.TYPES;
}
@Override
public Category visitVariable(VariableElement e, Void v) {
return Category.MEMBERS;
}
@Override
public Category visitExecutable(ExecutableElement e, Void v) {
return Category.MEMBERS;
}
@Override
public Category defaultAction(Element e, Void v) {
throw new IllegalArgumentException(e.toString());
}
}.visit(element);
}
/**
* Returns the type element that is documented as containing a member element,
* or {@code null} if this item does not represent a member element.
*
* @return the type element
*/
public TypeElement getContainingTypeElement() {
return null;
}
/**
* Returns the documentation tree node for this item, of {@code null} if this item
* does not represent a documentation tree node.
*
* @return the documentation tree node
*/
public DocTree getDocTree() {
return null;
}
/**
* Returns {@code true} if this index is for an element.
*
* @return {@code true} if this index is for an element
*/
public boolean isElementItem() {
return element != null && getDocTree() == null;
}
/**
* Returns {@code true} if this index is for a tag in a doc comment.
*
* @return {@code true} if this index is for a tag in a doc comment
*/
public boolean isTagItem() {
return getDocTree() != null;
}
/**
* Sets the URL for the item, when it cannot otherwise be inferred from other fields.
*
* @param u the url
*
* @return this item
*/
public IndexItem setUrl(String u) {
url = Objects.requireNonNull(u);
return this;
}
/**
* Returns the URL for this item, or an empty string if no value has been set.
*
* @return the URL for this item, or an empty string if no value has been set
*/
public String getUrl() {
return url;
}
/**
* Sets the name of the containing module for this item.
*
* @param m the module
*
* @return this item
*/
public IndexItem setContainingModule(String m) {
containingModule = Objects.requireNonNull(m);
return this;
}
/**
* Sets the name of the containing package for this item.
*
* @param p the package
*
* @return this item
*/
public IndexItem setContainingPackage(String p) {
containingPackage = Objects.requireNonNull(p);
return this;
}
/**
* Sets the name of the containing class for this item.
*
* @param c the class
*
* @return this item
*/
public IndexItem setContainingClass(String c) {
containingClass = Objects.requireNonNull(c);
return this;
}
/**
* Returns a description of the element owning the documentation comment for this item,
* or {@code null} if this is not a item for a tag for an item in a documentation tag.
*
* @return the description of the element that owns this item
*/
public String getHolder() {
return null;
}
/**
* Returns a description of the tag for this item or {@code null} if this is not a item
* for a tag for an item in a documentation tag.
*
* @return the description of the tag
*/
public String getDescription() {
return null;
}
/**
* Returns a string representing this item in JSON notation.
*
* @return a string representing this item in JSON notation
*/
public String toJSON() {
// TODO: Additional processing is required, see JDK-8238495
StringBuilder item = new StringBuilder();
Category category = getCategory();
switch (category) {
case MODULES:
item.append("{")
.append("\"l\":\"").append(label).append("\"")
.append("}");
break;
case PACKAGES:
item.append("{");
if (!containingModule.isEmpty()) {
item.append("\"m\":\"").append(containingModule).append("\",");
}
item.append("\"l\":\"").append(label).append("\"");
if (!url.isEmpty()) {
item.append(",\"u\":\"").append(url).append("\"");
}
item.append("}");
break;
case TYPES:
item.append("{");
if (!containingPackage.isEmpty()) {
item.append("\"p\":\"").append(containingPackage).append("\",");
}
if (!containingModule.isEmpty()) {
item.append("\"m\":\"").append(containingModule).append("\",");
}
item.append("\"l\":\"").append(label).append("\"");
if (!url.isEmpty()) {
item.append(",\"u\":\"").append(url).append("\"");
}
item.append("}");
break;
case MEMBERS:
item.append("{");
if (!containingModule.isEmpty()) {
item.append("\"m\":\"").append(containingModule).append("\",");
}
item.append("\"p\":\"").append(containingPackage).append("\",")
.append("\"c\":\"").append(containingClass).append("\",")
.append("\"l\":\"").append(label).append("\"");
if (!url.isEmpty()) {
item.append(",\"u\":\"").append(url).append("\"");
}
item.append("}");
break;
case TAGS:
String holder = getHolder();
String description = getDescription();
item.append("{")
.append("\"l\":\"").append(label).append("\",")
.append("\"h\":\"").append(holder).append("\",");
if (!description.isEmpty()) {
item.append("\"d\":\"").append(description).append("\",");
}
item.append("\"u\":\"").append(url).append("\"")
.append("}");
break;
default:
throw new AssertionError("Unexpected category: " + category);
}
return item.toString();
} }
} }

View File

@ -282,8 +282,7 @@ public class Utils {
} }
/** /**
* According to * According to <cite>The Java Language Specification</cite>,
* <cite>The Java Language Specification</cite>,
* all the outer classes and static inner classes are core classes. * all the outer classes and static inner classes are core classes.
*/ */
public boolean isCoreClass(TypeElement e) { public boolean isCoreClass(TypeElement e) {