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.
*/
protected String getErasureAnchor(ExecutableElement executableElement) {
final StringBuilder buf = new StringBuilder(writer.anchorName(executableElement));
final StringBuilder buf = new StringBuilder(executableElement.getSimpleName());
buf.append("(");
List<? extends VariableElement> parameters = executableElement.getParameters();
boolean foundTypeVariable = false;

View File

@ -25,24 +25,17 @@
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.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.SortedSet;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
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.HtmlStyle;
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.markup.StringContent;
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.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
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
* deletion without notice.</b>
*
* @see IndexBuilder
* @see IndexBuilder
*/
public class AbstractIndexWriter extends HtmlDocletWriter {
/**
* The index of all the members with unicode character.
*/
protected IndexBuilder indexBuilder;
protected final IndexBuilder mainIndex;
protected Navigation navBar;
protected final Map<Character, List<SearchIndexItem>> tagSearchIndexMap;
protected final Navigation navBar;
/**
* This constructor will be used by {@link SplitIndexWriter}. Initializes
* path to this file and relative path from this file.
* Initializes the common data for writers that can generate index files
* based on the information in {@code configuration.mainIndex}.
*
* @param configuration The current configuration
* @param path Path to the file which is getting generated.
* @param indexBuilder Unicode based Index from {@link IndexBuilder}
* @param configuration the current configuration
* @param path path to the file which is getting generated.
*/
protected AbstractIndexWriter(HtmlConfiguration configuration,
DocPath path,
IndexBuilder indexBuilder) {
DocPath path) {
super(configuration, path);
this.indexBuilder = indexBuilder;
this.mainIndex = configuration.mainIndex;
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) {
addHeading(uc, contentTree);
HtmlTree dl = HtmlTree.DL(HtmlStyle.index);
Map<String,Integer> duplicateLabelCheck = new HashMap<>();
memberlist.forEach(e -> duplicateLabelCheck.compute(e.getFullyQualifiedLabel(utils),
(k, v) -> v == null ? 1 : v + 1));
for (IndexItem indexItem : memberlist) {
addDescription(indexItem, dl,
duplicateLabelCheck.get(indexItem.getFullyQualifiedLabel(utils)) > 1);
for (IndexItem item : memberlist) {
addDescription(item, dl);
}
contentTree.add(dl);
}
@ -125,79 +100,62 @@ public class AbstractIndexWriter extends HtmlDocletWriter {
contentTree.add(heading);
}
protected void addDescription(IndexItem indexItem, Content dl, boolean addModuleInfo) {
SearchIndexItem si = indexItem.getSearchTag();
if (si != null) {
addDescription(si, dl);
} else {
si = new SearchIndexItem();
si.setLabel(indexItem.getLabel());
addElementDescription(indexItem, dl, si, addModuleInfo);
searchItems.add(si);
protected void addDescription(IndexItem indexItem, Content dl) {
if (indexItem.isTagItem()) {
addTagDescription(indexItem, dl);
} else if (indexItem.isElementItem()) {
addElementDescription(indexItem, dl);
}
}
/**
* 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 si the search index item
* @param addModuleInfo whether to include module information
*/
protected void addElementDescription(IndexItem indexItem, Content dlTree, SearchIndexItem si,
boolean addModuleInfo) {
protected void addElementDescription(IndexItem item, Content dlTree) {
Content dt;
Element element = indexItem.getElement();
String label = indexItem.getLabel();
Element element = item.getElement();
String label = item.getLabel();
switch (element.getKind()) {
case MODULE:
dt = HtmlTree.DT(getModuleLink((ModuleElement)element, new StringContent(label)));
si.setCategory(Category.MODULES);
dt = HtmlTree.DT(getModuleLink((ModuleElement) element, new StringContent(label)));
dt.add(" - ").add(contents.module_).add(" " + label);
break;
case PACKAGE:
dt = HtmlTree.DT(getPackageLink((PackageElement)element, new StringContent(label)));
dt = HtmlTree.DT(getPackageLink((PackageElement) element, new StringContent(label)));
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);
break;
case CLASS:
case ENUM:
case RECORD:
case ANNOTATION_TYPE:
case INTERFACE:
dt = HtmlTree.DT(getLink(new LinkInfoImpl(configuration,
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);
LinkInfoImpl.Kind.INDEX, (TypeElement) element).strong(true)));
dt.add(" - ");
addClassInfo((TypeElement)element, dt);
addClassInfo((TypeElement) element, dt);
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,
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(" - ");
addMemberDesc(element, containingType, dt);
break;
default:
throw new Error();
}
dlTree.add(dt);
Content dd = new HtmlTree(TagName.DD);
@ -224,19 +182,19 @@ public class AbstractIndexWriter extends HtmlDocletWriter {
));
}
protected void addDescription(SearchIndexItem sii, Content dlTree) {
String siiPath = pathToRoot.isEmpty() ? "" : pathToRoot.getPath() + "/";
siiPath += sii.getUrl();
HtmlTree labelLink = HtmlTree.A(siiPath, new StringContent(sii.getLabel()));
protected void addTagDescription(IndexItem item, Content dlTree) {
String itemPath = pathToRoot.isEmpty() ? "" : pathToRoot.getPath() + "/";
itemPath += item.getUrl();
HtmlTree labelLink = HtmlTree.A(itemPath, new StringContent(item.getLabel()));
Content dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.searchTagLink, labelLink));
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);
Content dd = new HtmlTree(TagName.DD);
if (sii.getDescription().isEmpty()) {
if (item.getDescription().isEmpty()) {
dd.add(Entity.NO_BREAK_SPACE);
} else {
dd.add(sii.getDescription());
dd.add(item.getDescription());
}
dlTree.add(dd);
}
@ -313,85 +271,4 @@ public class AbstractIndexWriter extends HtmlDocletWriter {
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.annotationTypeSummary, utils::isAnnotationType)
.setTabScript(i -> "show(" + i + ");");
for (Character unicode : indexBuilder.keys()) {
for (IndexItem indexItem : indexBuilder.getMemberList(unicode)) {
for (Character unicode : indexBuilder.getFirstCharacters()) {
for (IndexItem indexItem : indexBuilder.getItems(unicode)) {
TypeElement typeElement = (TypeElement) indexItem.getElement();
if (typeElement == null || !utils.isCoreClass(typeElement)) {
continue;
if (typeElement != null && utils.isCoreClass(typeElement)) {
addTableRow(table, typeElement);
}
addTableRow(table, typeElement);
}
}
Content titleContent = contents.allClassesLabel;

View File

@ -125,7 +125,7 @@ public class ConstructorWriterImpl extends AbstractExecutableMemberWriter
}
constructorDocTree.add(heading);
return HtmlTree.SECTION(HtmlStyle.detail, constructorDocTree)
.setId(links.getName(writer.getAnchor(constructor)));
.setId(links.getAnchor(constructor));
}
@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.DocPath;
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.
@ -93,7 +94,16 @@ public class HtmlConfiguration extends BaseConfiguration {
*/
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;
@ -201,6 +211,9 @@ public class HtmlConfiguration extends BaseConfiguration {
}
}
}
if (options.createIndex()) {
mainIndex = new HtmlIndexBuilder(this);
}
docPaths = new DocPaths(utils);
setCreateOverview();
setTopFile(docEnv);
@ -352,10 +365,4 @@ public class HtmlConfiguration extends BaseConfiguration {
}
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()) {
IndexBuilder indexBuilder = new IndexBuilder(configuration, nodeprecated);
SystemPropertiesWriter.generate(configuration);
configuration.mainIndex.addElements();
if (options.splitIndex()) {
SplitIndexWriter.generate(configuration, indexBuilder);
SplitIndexWriter.generate(configuration);
} else {
SingleIndexWriter.generate(configuration, indexBuilder);
SingleIndexWriter.generate(configuration);
}
AllClassesIndexWriter.generate(configuration,
new IndexBuilder(configuration, nodeprecated, true));
IndexBuilder allClassesIndex = new IndexBuilder(configuration, nodeprecated, true);
allClassesIndex.addElements();
AllClassesIndexWriter.generate(configuration, allClassesIndex);
if (!configuration.packages.isEmpty()) {
AllPackagesIndexWriter.generate(configuration);
}
SystemPropertiesWriter.generate(configuration);
configuration.mainIndex.createSearchIndexFiles();
}
if (options.createOverview()) {

View File

@ -155,8 +155,6 @@ public class HtmlDocletWriter {
*/
public final HtmlConfiguration configuration;
protected final SearchIndexItems searchItems;
protected final HtmlOptions options;
protected final Utils utils;
@ -216,12 +214,11 @@ public class HtmlDocletWriter {
*/
public HtmlDocletWriter(HtmlConfiguration configuration, DocPath path) {
this.configuration = configuration;
this.searchItems = configuration.searchItems;
this.options = configuration.getOptions();
this.contents = configuration.contents;
this.messages = configuration.messages;
this.resources = configuration.docResources;
this.links = new Links(path);
this.links = new Links(path, configuration.utils);
this.utils = configuration.utils;
this.comparators = utils.comparators;
this.path = path;
@ -955,7 +952,7 @@ public class HtmlDocletWriter {
ExecutableElement ee = (ExecutableElement)element;
return getLink(new LinkInfoImpl(configuration, context, typeElement)
.label(label)
.where(links.getName(getAnchor(ee, isProperty)))
.where(links.getAnchor(ee, isProperty))
.strong(strong));
}
@ -988,7 +985,7 @@ public class HtmlDocletWriter {
ExecutableElement emd = (ExecutableElement) element;
return getLink(new LinkInfoImpl(configuration, context, typeElement)
.label(label)
.where(links.getName(getAnchor(emd))));
.where(links.getAnchor(emd)));
} else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
return getLink(new LinkInfoImpl(configuration, context, typeElement)
.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) {
Kind kind = see.getKind();
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);
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 methlink = writer.getLink(
new LinkInfoImpl(writer.configuration, LinkInfoImpl.Kind.MEMBER, holder)
.where(writer.links.getName(writer.getAnchor(method)))
.where(writer.links.getAnchor(method))
.label(method.getSimpleName()));
Content codeMethLink = HtmlTree.CODE(methlink);
Content dd = HtmlTree.DD(codeMethLink);

View File

@ -142,7 +142,7 @@ public class Navigation {
this.documentedPage = page;
this.path = path;
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.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.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.ContentBuilder;
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.toolkit.Content;
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.Category;
/**
@ -57,34 +57,26 @@ import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
*/
public class SingleIndexWriter extends AbstractIndexWriter {
private Set<Character> elements;
private Set<Character> firstCharacters;
/**
* Construct the SingleIndexWriter with filename "index-all.html" and the
* {@link IndexBuilder}
*
* @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,
DocPath filename,
IndexBuilder indexBuilder) {
super(configuration, filename, indexBuilder);
public SingleIndexWriter(HtmlConfiguration configuration) {
super(configuration, DocPaths.INDEX_ALL);
}
/**
* Generate single index file, for all Unicode characters.
*
* @param configuration the configuration for this doclet
* @param indexBuilder IndexBuilder built by {@link IndexBuilder}
* @throws DocFileIOException if there is a problem generating the index
*/
public static void generate(HtmlConfiguration configuration,
IndexBuilder indexBuilder) throws DocFileIOException {
DocPath filename = DocPaths.INDEX_ALL;
SingleIndexWriter indexgen = new SingleIndexWriter(configuration,
filename, indexBuilder);
public static void generate(HtmlConfiguration configuration) throws DocFileIOException {
SingleIndexWriter indexgen = new SingleIndexWriter(configuration);
indexgen.generateIndexFile();
}
@ -101,14 +93,10 @@ public class SingleIndexWriter extends AbstractIndexWriter {
navBar.setUserHeader(getUserHeaderFooter(true));
headerContent.add(navBar.getContent(Navigation.Position.TOP));
Content mainContent = new ContentBuilder();
elements = new TreeSet<>(indexBuilder.asMap().keySet());
elements.addAll(tagSearchIndexMap.keySet());
firstCharacters = new TreeSet<>(mainIndex.getFirstCharacters());
addLinksForIndexes(mainContent);
for (Character unicode : elements) {
if (tagSearchIndexMap.get(unicode) != null) {
indexBuilder.addSearchTags(unicode, tagSearchIndexMap.get(unicode));
}
addContents(unicode, indexBuilder.getMemberList(unicode), mainContent);
for (Character ch : firstCharacters) {
addContents(ch, mainIndex.getItems(ch), mainContent);
}
addLinksForIndexes(mainContent);
HtmlTree footer = HtmlTree.FOOTER();
@ -122,7 +110,6 @@ public class SingleIndexWriter extends AbstractIndexWriter {
contents.getContent("doclet.Index"))))
.addMainContent(mainContent)
.setFooter(footer));
createSearchIndexFiles();
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
*/
protected void addLinksForIndexes(Content contentTree) {
for (Character ch : elements) {
for (Character ch : firstCharacters) {
String unicode = ch.toString();
contentTree.add(
links.createLink(getNameForIndex(unicode),
@ -147,7 +134,8 @@ public class SingleIndexWriter extends AbstractIndexWriter {
contentTree.add(links.createLink(DocPaths.ALLPACKAGES_INDEX,
contents.allPackagesLabel));
}
if (searchItems.containsAnyOfCategories(Category.SYSTEM_PROPERTY)) {
boolean anySystemProperties = !mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).isEmpty();
if (anySystemProperties) {
contentTree.add(getVerticalSeparator());
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.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.SortedSet;
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.ContentBuilder;
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.DocPaths;
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
@ -70,14 +69,12 @@ public class SplitIndexWriter extends AbstractIndexWriter {
*
* @param configuration the configuration for this doclet
* @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
*/
public SplitIndexWriter(HtmlConfiguration configuration,
DocPath path,
IndexBuilder indexBuilder,
Collection<Character> elements) {
super(configuration, path, indexBuilder);
super(configuration, path);
this.indexElements = new ArrayList<>(elements);
}
@ -86,30 +83,19 @@ public class SplitIndexWriter extends AbstractIndexWriter {
* the members starting with the particular unicode character.
*
* @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
*/
public static void generate(HtmlConfiguration configuration,
IndexBuilder indexBuilder) throws DocFileIOException
{
public static void generate(HtmlConfiguration configuration) throws DocFileIOException {
DocPath path = DocPaths.INDEX_FILES;
SortedSet<Character> keys = new TreeSet<>(indexBuilder.asMap().keySet());
Set<Character> searchItemsKeys = configuration.searchItems
.itemsOfCategories(Category.INDEX, Category.SYSTEM_PROPERTY)
.map(i -> keyCharacter(i.getLabel()))
.collect(Collectors.toSet());
keys.addAll(searchItemsKeys);
IndexBuilder mainIndex = configuration.mainIndex;
SortedSet<Character> keys = new TreeSet<>(mainIndex.getFirstCharacters());
ListIterator<Character> li = new ArrayList<>(keys).listIterator();
while (li.hasNext()) {
Character ch = li.next();
DocPath filename = DocPaths.indexN(li.nextIndex());
SplitIndexWriter indexgen = new SplitIndexWriter(configuration,
path.resolve(filename),
indexBuilder, keys);
path.resolve(filename), keys);
indexgen.generateIndexFile(ch);
if (!li.hasNext()) {
indexgen.createSearchIndexFiles();
}
}
}
@ -135,10 +121,7 @@ public class SplitIndexWriter extends AbstractIndexWriter {
contents.getContent("doclet.Index"))));
Content mainContent = new ContentBuilder();
addLinksForIndexes(mainContent);
if (tagSearchIndexMap.get(unicode) != null) {
indexBuilder.addSearchTags(unicode, tagSearchIndexMap.get(unicode));
}
addContents(unicode, indexBuilder.getMemberList(unicode), mainContent);
addContents(unicode, mainIndex.getItems(unicode), mainContent);
addLinksForIndexes(mainContent);
main.add(mainContent);
HtmlTree footer = HtmlTree.FOOTER();
@ -173,7 +156,8 @@ public class SplitIndexWriter extends AbstractIndexWriter {
contentTree.add(links.createLink(pathToRoot.resolve(DocPaths.ALLPACKAGES_INDEX),
contents.allPackagesLabel));
}
if (searchItems.containsAnyOfCategories(Category.SYSTEM_PROPERTY)) {
boolean anySystemProperties = !mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).isEmpty();
if (anySystemProperties) {
contentTree.add(getVerticalSeparator());
contentTree.add(links.createLink(pathToRoot.resolve(DocPaths.SYSTEM_PROPERTIES),
contents.systemPropertiesLabel));

View File

@ -24,7 +24,7 @@
*/
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.ContentBuilder;
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.DocPath;
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 java.nio.file.Path;
@ -85,8 +87,8 @@ public class SystemPropertiesWriter extends HtmlDocletWriter {
}
private static void generate(HtmlConfiguration configuration, DocPath fileName) throws DocFileIOException {
boolean hasSystemProperties = configuration.searchItems
.containsAnyOfCategories(Category.SYSTEM_PROPERTY);
boolean hasSystemProperties = configuration.mainIndex != null
&& !configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).isEmpty();
if (!hasSystemProperties) {
// Cannot defer this check any further, because of the super() call
// 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
*/
protected void addSystemProperties(Content content) {
Map<String, List<SearchIndexItem>> searchIndexMap = groupSystemProperties();
Map<String, List<IndexItem>> searchIndexMap = groupSystemProperties();
Content separator = new StringContent(", ");
Table table = new Table(HtmlStyle.systemPropertiesSummary, HtmlStyle.summaryTable)
.setCaption(contents.systemPropertiesSummaryLabel)
.setHeader(new TableHeader(contents.propertyLabel, contents.referencedIn))
.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());
List<SearchIndexItem> searchIndexItems = entry.getValue();
List<IndexItem> searchIndexItems = entry.getValue();
Content separatedReferenceLinks = new ContentBuilder();
separatedReferenceLinks.add(createLink(searchIndexItems.get(0)));
for (int i = 1; i < searchIndexItems.size(); i++) {
@ -152,24 +154,23 @@ public class SystemPropertiesWriter extends HtmlDocletWriter {
content.add(table);
}
private Map<String, List<SearchIndexItem>> groupSystemProperties() {
return searchItems
.itemsOfCategories(Category.SYSTEM_PROPERTY)
.collect(groupingBy(SearchIndexItem::getLabel, TreeMap::new, toList()));
private Map<String, List<IndexItem>> groupSystemProperties() {
return configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).stream()
.collect(groupingBy(IndexItem::getLabel, TreeMap::new, toList()));
}
private Content createLink(SearchIndexItem i) {
assert i.getCategory() == Category.SYSTEM_PROPERTY : i;
if (i.getElement() != null) {
if (i.getElement() instanceof OverviewElement) {
return links.createLink(pathToRoot.resolve(i.getUrl()),
resources.getText("doclet.Overview"));
}
DocletElement e = ((DocletElement) i.getElement());
private Content createLink(IndexItem i) {
assert i.getDocTree().getKind() == DocTree.Kind.SYSTEM_PROPERTY : i;
Element element = i.getElement();
if (element instanceof OverviewElement) {
return links.createLink(pathToRoot.resolve(i.getUrl()),
resources.getText("doclet.Overview"));
} else if (element instanceof DocletElement) {
DocletElement e = (DocletElement) element;
// Implementations of DocletElement do not override equals and
// hashCode; putting instances of DocletElement in a map is not
// incorrect, but might well be inefficient
String t = titles.computeIfAbsent(i.getElement(), utils::getHTMLTitle);
String t = titles.computeIfAbsent(element, utils::getHTMLTitle);
if (t.isBlank()) {
// The user should probably be notified (a warning?) that this
// 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.ParamTree;
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.HtmlStyle;
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.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
/**
@ -111,7 +111,7 @@ public class TagletWriterImpl extends TagletWriter {
@Override
protected Content indexTagOutput(Element element, DocTree tag) {
CommentHelper ch = utils.getCommentHelper(element);
IndexTree itt = (IndexTree)tag;
IndexTree itt = (IndexTree) tag;
String tagText = ch.getText(itt.getSearchTerm());
if (tagText.charAt(0) == '"' && tagText.charAt(tagText.length() - 1) == '"') {
@ -120,7 +120,7 @@ public class TagletWriterImpl extends TagletWriter {
}
String desc = ch.getText(itt.getDescription());
return createAnchorAndSearchIndex(element, tagText, desc, false);
return createAnchorAndSearchIndex(element, tagText, desc, tag);
}
@Override
@ -283,7 +283,7 @@ public class TagletWriterImpl extends TagletWriter {
SystemPropertyTree itt = (SystemPropertyTree) tag;
String tagText = itt.getPropertyName().toString();
return HtmlTree.CODE(createAnchorAndSearchIndex(element, tagText,
resources.getText("doclet.System_Property"), true));
resources.getText("doclet.System_Property"), tag));
}
@Override
@ -366,7 +366,7 @@ public class TagletWriterImpl extends TagletWriter {
}
@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;
if (isFirstSentence && inSummary) {
result = new StringContent(tagText);
@ -379,77 +379,60 @@ public class TagletWriterImpl extends TagletWriter {
}
result = HtmlTree.SPAN(anchorName, HtmlStyle.searchTagResult, new StringContent(tagText));
if (options.createIndex() && !tagText.isEmpty()) {
SearchIndexItem si = new SearchIndexItem();
si.setLabel(tagText);
si.setDescription(desc);
si.setUrl(htmlWriter.path.getPath() + "#" + anchorName);
new SimpleElementVisitor14<Void, Void>() {
String holder = new SimpleElementVisitor14<String, Void>() {
@Override
public Void visitModule(ModuleElement e, Void p) {
si.setHolder(resources.getText("doclet.module")
+ " " + utils.getFullyQualifiedName(e));
return null;
public String visitModule(ModuleElement e, Void p) {
return resources.getText("doclet.module")
+ " " + utils.getFullyQualifiedName(e);
}
@Override
public Void visitPackage(PackageElement e, Void p) {
si.setHolder(resources.getText("doclet.package")
+ " " + utils.getFullyQualifiedName(e));
return null;
public String visitPackage(PackageElement e, Void p) {
return resources.getText("doclet.package")
+ " " + utils.getFullyQualifiedName(e);
}
@Override
public Void visitType(TypeElement e, Void p) {
si.setHolder(utils.getTypeElementName(e, true)
+ " " + utils.getFullyQualifiedName(e));
return null;
public String visitType(TypeElement e, Void p) {
return utils.getTypeElementName(e, true)
+ " " + utils.getFullyQualifiedName(e);
}
@Override
public Void visitExecutable(ExecutableElement e, Void p) {
si.setHolder(utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e))
+ "." + utils.getSimpleName(e)
+ utils.flatSignature(e, htmlWriter.getCurrentPageElement()));
return null;
public String visitExecutable(ExecutableElement e, Void p) {
return utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e))
+ "." + utils.getSimpleName(e)
+ utils.flatSignature(e, htmlWriter.getCurrentPageElement());
}
@Override
public Void visitVariable(VariableElement e, Void p) {
TypeElement te = utils.getEnclosingTypeElement(e);
si.setHolder(utils.getFullyQualifiedName(te) + "." + utils.getSimpleName(e));
return null;
public String visitVariable(VariableElement e, Void p) {
return utils.getFullyQualifiedName(utils.getEnclosingTypeElement(e))
+ "." + utils.getSimpleName(e);
}
@Override
public Void visitUnknown(Element e, Void p) {
public String visitUnknown(Element e, Void p) {
if (e instanceof DocletElement) {
DocletElement de = (DocletElement) e;
si.setElement(de);
switch (de.getSubKind()) {
case OVERVIEW:
si.setHolder(resources.getText("doclet.Overview"));
break;
case DOCFILE:
si.setHolder(getHolderName(de));
break;
default:
throw new IllegalStateException();
}
return null;
return switch (de.getSubKind()) {
case OVERVIEW -> resources.getText("doclet.Overview");
case DOCFILE -> getHolderName(de);
};
} else {
return super.visitUnknown(e, p);
}
}
@Override
protected Void defaultAction(Element e, Void p) {
si.setHolder(utils.getFullyQualifiedName(e));
return null;
protected String defaultAction(Element e, Void p) {
return utils.getFullyQualifiedName(e);
}
}.visit(element);
si.setCategory(isSystemProperty ? Category.SYSTEM_PROPERTY : Category.INDEX);
configuration.searchItems.add(si);
IndexItem item = IndexItem.of(element, tree, tagText, holder, desc,
new DocLink(htmlWriter.path, anchorName));
configuration.mainIndex.add(item);
}
}
return result;

View File

@ -25,10 +25,13 @@
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.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
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)
@ -47,6 +50,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
public class Links {
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.
@ -56,8 +60,9 @@ public class Links {
*
* @param file the file
*/
public Links(DocPath file) {
public Links(DocPath file, Utils utils) {
this.file = file;
this.utils = utils;
}
/**
@ -260,10 +265,36 @@ public class Links {
}
/**
* Converts a name to a valid HTML name (id).
* This depends on the HTML version specified when the {@code Links} object was created.
* Returns the HTML id to use for an executable element.
*
* @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
*/
public String getName(String name) {

View File

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

View File

@ -26,7 +26,6 @@
package jdk.javadoc.internal.doclets.toolkit.util;
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.ExecutableElement;
@ -279,44 +278,12 @@ public class Comparators {
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;
/**
* Compares the FullyQualifiedNames of two TypeMirrors
* @return
* Returns a comparator that compares the fully qualified names of two type mirrors.
*
* @return the comparator
*/
public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() {
if (typeMirrorClassUseComparator == null) {
@ -332,10 +299,10 @@ public class Comparators {
private Comparator<TypeMirror> typeMirrorIndexUseComparator = null;
/**
* Compares the SimpleNames of TypeMirrors if equal then the
* FullyQualifiedNames of TypeMirrors.
* Returns a comparator that compares the simple names of two type mirrors,
* or the fully qualified names if the simple names are equal.
*
* @return
* @return the comparator
*/
public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() {
if (typeMirrorIndexUseComparator == null) {
@ -468,7 +435,7 @@ public class Comparators {
* argument is less than, equal to, or greater than the second.
*/
protected int compareFullyQualifiedNames(Element e1, Element e2) {
// add simplename to be compatible
// add simple name to be compatible
String thisElement = getFullyQualifiedName(e1);
String thatElement = getFullyQualifiedName(e2);
return utils.compareStrings(thisElement, thatElement);
@ -527,20 +494,20 @@ public class Comparators {
}
private int getKindIndex(Element e) {
switch (e.getKind()) {
case MODULE: return 0;
case PACKAGE: return 1;
case CLASS: return 2;
case ENUM: return 3;
case ENUM_CONSTANT: return 4;
case RECORD: return 5;
case INTERFACE: return 6;
case ANNOTATION_TYPE: return 7;
case FIELD: return 8;
case CONSTRUCTOR: return 9;
case METHOD: return 10;
default: throw new IllegalArgumentException(e.getKind().toString());
}
return switch (e.getKind()) {
case MODULE -> 0;
case PACKAGE -> 1;
case CLASS -> 2;
case ENUM -> 3;
case ENUM_CONSTANT -> 4;
case RECORD -> 5;
case INTERFACE -> 6;
case ANNOTATION_TYPE -> 7;
case FIELD -> 8;
case CONSTRUCTOR -> 9;
case METHOD -> 10;
default -> throw new IllegalArgumentException(e.getKind().toString());
};
}
@SuppressWarnings("preview")
@ -598,48 +565,4 @@ public class Comparators {
}.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.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.Messages;
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.
* 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 {
/**
* Sets of elements keyed by the first character of the names of the
* elements in those sets.
* Sets of items keyed by the first character of the names (labels)
* 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.
@ -63,11 +73,15 @@ public class IndexBuilder {
/**
* Build this index only for classes?
*/
private final boolean classesOnly;
protected final boolean classesOnly;
private final BaseConfiguration configuration;
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}.
@ -106,15 +120,18 @@ public class IndexBuilder {
this.noDeprecated = noDeprecated;
this.classesOnly = classesOnly;
this.indexMap = new TreeMap<>();
comparator = utils.comparators.makeIndexComparator(classesOnly);
buildIndex();
itemsByFirstChar = new TreeMap<>();
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();
indexTypeElements(classes);
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
* provided type element.
@ -146,26 +222,23 @@ public class IndexBuilder {
*/
private void indexMembers(TypeElement te) {
VisibleMemberTable vmt = configuration.getVisibleMemberTable(te);
indexElements(vmt.getVisibleMembers(FIELDS), te);
indexElements(vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_OPTIONAL), te);
indexElements(vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_REQUIRED), te);
indexElements(vmt.getVisibleMembers(METHODS), te);
indexElements(vmt.getVisibleMembers(CONSTRUCTORS), te);
indexElements(vmt.getVisibleMembers(ENUM_CONSTANTS), te);
indexMembers(te, vmt.getVisibleMembers(FIELDS));
indexMembers(te, vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_OPTIONAL));
indexMembers(te, vmt.getVisibleMembers(ANNOTATION_TYPE_MEMBER_REQUIRED));
indexMembers(te, vmt.getVisibleMembers(METHODS));
indexMembers(te, vmt.getVisibleMembers(CONSTRUCTORS));
indexMembers(te, vmt.getVisibleMembers(ENUM_CONSTANTS));
}
/**
* 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) {
for (Element element : elements) {
if (shouldIndex(element)) {
String name = utils.getSimpleName(element);
Character ch = keyCharacter(name);
SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
set.add(new IndexItem(element, typeElement, utils));
private void indexMembers(TypeElement typeElement, Iterable<? extends Element> members) {
for (Element member : members) {
if (shouldIndex(member)) {
add(IndexItem.of(typeElement, member, utils));
}
}
}
@ -178,26 +251,17 @@ public class IndexBuilder {
private void indexTypeElements(Iterable<TypeElement> elements) {
for (TypeElement typeElement : elements) {
if (shouldIndex(typeElement)) {
String name = utils.getSimpleName(typeElement);
Character ch = keyCharacter(name);
SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
set.add(new IndexItem(typeElement, utils));
add(IndexItem.of(typeElement, utils));
}
}
}
private static Character keyCharacter(String s) {
return s.isEmpty() ? '*' : Character.toUpperCase(s.charAt(0));
}
/**
* Indexes all the modules.
*/
private void indexModules() {
for (ModuleElement m : configuration.modules) {
Character ch = keyCharacter(m.getQualifiedName().toString());
SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
set.add(new IndexItem(m, utils));
add(IndexItem.of(m, utils));
}
}
@ -208,9 +272,7 @@ public class IndexBuilder {
*/
private void indexPackage(PackageElement packageElement) {
if (shouldIndex(packageElement)) {
Character ch = keyCharacter(utils.getPackageName(packageElement));
SortedSet<IndexItem> set = indexMap.computeIfAbsent(ch, c -> new TreeSet<>(comparator));
set.add(new IndexItem(packageElement, utils));
add(IndexItem.of(packageElement, utils));
}
}
@ -236,48 +298,46 @@ public class IndexBuilder {
}
}
/**
* Returns a map representation of this index.
*
* @return map
*/
public Map<Character, SortedSet<IndexItem>> asMap() {
return indexMap;
private static Character keyCharacter(String s) {
return s.isEmpty() ? '*' : Character.toUpperCase(s.charAt(0));
}
/**
* 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.
*/
public List<Character> keys() {
return new ArrayList<>(indexMap.keySet());
}
/**
* Add search tags for the key {@code key}.
* 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:
* for element items, tag items, and others.
*
* @param key the index key
* @param searchTags the search tags
* @return a comparator for index page items
*/
public void addSearchTags(char key, List<SearchIndexItem> searchTags) {
searchTags.forEach(searchTag -> {
SortedSet<IndexItem> set = indexMap.computeIfAbsent(key, c -> new TreeSet<>(comparator));
set.add(new IndexItem(searchTag));
});
private Comparator<IndexItem> makeIndexComparator(boolean classesOnly) {
Comparator<Element> elementComparator = classesOnly
? utils.comparators.makeAllClassesComparator()
: 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;
import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem;
import java.util.Objects;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.PackageElement;
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.
* 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 {
/**
* 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 SearchIndexItem searchTag;
private String label;
private TypeElement typeElement;
public IndexItem(SearchIndexItem searchTag) {
this.element = null;
this.searchTag = searchTag;
this.label = searchTag.getLabel();
/**
* The URL pointing to the element, doc tree or page being indexed.
* It may be empty if the information can be determined from other fields.
*/
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;
this.searchTag = null;
/**
* Creates an index item for a package element.
*
* @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);
this.label = utils.getSimpleName(typeElement);
/**
* Creates an index item for a type element.
* 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);
this.label = utils.getFullyQualifiedName(moduleElement);
}
public IndexItem(PackageElement packageElement, Utils utils) {
this(packageElement);
this.label = utils.getPackageName(packageElement);
}
public IndexItem(Element member, TypeElement typeElement, Utils utils) {
this(member);
this.typeElement = typeElement;
/**
* Creates an index item for a member element.
* 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.
*
* @param typeElement the element that contains the member
* @param member the member
* @param utils the common utilities class
*
* @return the item
*
* @see #getContainingTypeElement()
*/
public static IndexItem of(TypeElement typeElement, Element member, Utils utils) {
String name = utils.getSimpleName(member);
if (utils.isExecutableElement(member)) {
ExecutableElement ee = (ExecutableElement)member;
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() {
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) {
TypeElement typeElement = getContainingTypeElement();
if (typeElement != null) {
return utils.getFullyQualifiedName(typeElement) + "." + label;
} else if (element != null) {
} else if (isElementItem()) {
return utils.getFullyQualifiedName(element);
} else {
return label;
}
}
/**
* Returns the element associate with this item, or {@code null}.
*
* @return the element
*/
public Element getElement() {
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() {
return typeElement;
protected Category getCategory(DocTree docTree) {
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
* <cite>The Java Language Specification</cite>,
* According to <cite>The Java Language Specification</cite>,
* all the outer classes and static inner classes are core classes.
*/
public boolean isCoreClass(TypeElement e) {