8295088: Update External Spec page to show tabs for hosts

Reviewed-by: hannesw
This commit is contained in:
Jonathan Gibbons 2022-10-21 19:27:54 +00:00
parent 218104247e
commit f5dabf9440
27 changed files with 220 additions and 109 deletions

View File

@ -99,9 +99,9 @@ public abstract class AbstractMemberWriter implements MemberSummaryWriter, Membe
*/
public abstract TableHeader getSummaryTableHeader(Element member);
private Table summaryTable;
private Table<Element> summaryTable;
private Table getSummaryTable() {
private Table<Element> getSummaryTable() {
if (summaryTable == null) {
summaryTable = createSummaryTable();
}
@ -116,7 +116,7 @@ public abstract class AbstractMemberWriter implements MemberSummaryWriter, Membe
*
* @return the summary table
*/
protected abstract Table createSummaryTable();
protected abstract Table<Element> createSummaryTable();
/**
* Adds inherited summary label for the member.
@ -300,7 +300,7 @@ public abstract class AbstractMemberWriter implements MemberSummaryWriter, Membe
return;
}
boolean printedUseTableHeader = false;
Table useTable = new Table(HtmlStyle.summaryTable)
var useTable = new Table<Void>(HtmlStyle.summaryTable)
.setCaption(heading)
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast);
for (Element element : members) {
@ -345,7 +345,7 @@ public abstract class AbstractMemberWriter implements MemberSummaryWriter, Membe
if (tElement != typeElement) {
throw new IllegalStateException();
}
Table table = getSummaryTable();
var table = getSummaryTable();
List<Content> rowContents = new ArrayList<>();
Content summaryType = new ContentBuilder();
addSummaryType(member, summaryType);

View File

@ -111,16 +111,16 @@ public class AllClassesIndexWriter extends HtmlDocletWriter {
* @param target the content to which the links will be added
*/
protected void addContents(Content target) {
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<TypeElement>(HtmlStyle.summaryTable)
.setHeader(new TableHeader(contents.classLabel, contents.descriptionLabel))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast)
.setId(HtmlIds.ALL_CLASSES_TABLE)
.setDefaultTab(contents.allClassesAndInterfacesLabel)
.addTab(contents.interfaces, utils::isPlainInterface)
.addTab(contents.classes, e -> utils.isNonThrowableClass((TypeElement)e))
.addTab(contents.classes, e -> utils.isNonThrowableClass(e))
.addTab(contents.enums, utils::isEnum)
.addTab(contents.records, e -> utils.isRecord((TypeElement)e))
.addTab(contents.exceptionClasses, e -> utils.isThrowable((TypeElement)e))
.addTab(contents.records, e -> utils.isRecord(e))
.addTab(contents.exceptionClasses, e -> utils.isThrowable(e))
.addTab(contents.annotationTypes, utils::isAnnotationInterface);
for (Character unicode : indexBuilder.getFirstCharacters()) {
for (IndexItem indexItem : indexBuilder.getItems(unicode)) {
@ -146,7 +146,7 @@ public class AllClassesIndexWriter extends HtmlDocletWriter {
* @param table the table to which the row will be added
* @param klass the type to be added to the table
*/
protected void addTableRow(Table table, TypeElement klass) {
protected void addTableRow(Table<TypeElement> table, TypeElement klass) {
List<Content> rowContents = new ArrayList<>();
Content classLink = getLink(new HtmlLinkInfo(
configuration, HtmlLinkInfo.Kind.INDEX, klass));

View File

@ -94,7 +94,7 @@ public class AllPackagesIndexWriter extends HtmlDocletWriter {
* @param target the content to which the links will be added
*/
protected void addPackages(Content target) {
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<PackageElement>(HtmlStyle.summaryTable)
.setCaption(Text.of(contents.packageSummaryLabel.toString()))
.setHeader(new TableHeader(contents.packageLabel, contents.descriptionLabel))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);

View File

@ -214,8 +214,8 @@ public class AnnotationTypeMemberWriterImpl extends AbstractMemberWriter
}
@Override
protected Table createSummaryTable() {
return new Table(HtmlStyle.summaryTable)
protected Table<Element> createSummaryTable() {
return new Table<Element>(HtmlStyle.summaryTable)
.setCaption(getCaption())
.setHeader(getSummaryTableHeader(typeElement))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast);

View File

@ -242,7 +242,7 @@ public class ClassUseWriter extends SubWriterHolderWriter {
"doclet.ClassUse_Packages.that.use.0",
getLink(new HtmlLinkInfo(configuration,
HtmlLinkInfo.Kind.CLASS_USE_HEADER, typeElement)));
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<Void>(HtmlStyle.summaryTable)
.setCaption(caption)
.setHeader(getPackageTableHeader())
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
@ -268,7 +268,7 @@ public class ClassUseWriter extends SubWriterHolderWriter {
getLink(new HtmlLinkInfo(configuration,
HtmlLinkInfo.Kind.CLASS_USE_HEADER, typeElement)));
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<Void>(HtmlStyle.summaryTable)
.setCaption(caption)
.setHeader(getPackageTableHeader())
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
@ -309,7 +309,7 @@ public class ClassUseWriter extends SubWriterHolderWriter {
* @param pkg the package that uses the given class
* @param table the table to which the package use information will be added
*/
protected void addPackageUse(PackageElement pkg, Table table) {
protected void addPackageUse(PackageElement pkg, Table<?> table) {
Content pkgLink =
links.createLink(htmlIds.forPackage(pkg), getLocalizedPackageName(pkg));
Content summary = new ContentBuilder();

View File

@ -182,7 +182,7 @@ public class ConstantsSummaryWriterImpl extends HtmlDocletWriter implements Cons
}
caption.add(classLink);
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<Void>(HtmlStyle.summaryTable)
.setCaption(caption)
.setHeader(constantsTableHeader)
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast);

View File

@ -183,7 +183,7 @@ public class ConstructorWriterImpl extends AbstractExecutableMemberWriter
}
@Override
protected Table createSummaryTable() {
protected Table<Element> createSummaryTable() {
List<HtmlStyle> bodyRowStyles;
if (foundNonPubConstructor) {
@ -193,7 +193,7 @@ public class ConstructorWriterImpl extends AbstractExecutableMemberWriter
bodyRowStyles = Arrays.asList(HtmlStyle.colConstructorName, HtmlStyle.colLast);
}
return new Table(
return new Table<Element>(
HtmlStyle.summaryTable)
.setCaption(contents.constructors)
.setHeader(getSummaryTableHeader(typeElement))

View File

@ -132,7 +132,7 @@ public class DeprecatedListWriter extends SummaryListWriter<DeprecatedAPIListBui
}
@Override
protected void addTableTabs(Table table, String headingKey) {
protected void addTableTabs(Table<Element> table, String headingKey) {
List<String> releases = builder.releases;
if (!releases.isEmpty()) {
table.setGridStyle(HtmlStyle.threeColumnReleaseSummary);

View File

@ -140,8 +140,8 @@ public class EnumConstantWriterImpl extends AbstractMemberWriter
}
@Override
protected Table createSummaryTable() {
return new Table(HtmlStyle.summaryTable)
protected Table<Element> createSummaryTable() {
return new Table<Element>(HtmlStyle.summaryTable)
.setCaption(contents.getContent("doclet.Enum_Constants"))
.setHeader(getSummaryTableHeader(typeElement))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);

View File

@ -35,8 +35,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.WeakHashMap;
import java.util.function.Predicate;
import javax.lang.model.element.Element;
import javax.tools.Diagnostic;
@ -183,12 +185,43 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
protected void addExternalSpecs(Content content) {
final int USE_DETAILS_THRESHHOLD = 20;
Map<String, List<IndexItem>> searchIndexMap = groupExternalSpecs();
Table table = new Table(HtmlStyle.summaryTable)
var hostNamesSet = new TreeSet<String>();
boolean noHost = false;
for (var searchIndexItems : searchIndexMap.values()) {
try {
URI uri = getSpecURI(searchIndexItems.get(0));
String host = uri.getHost();
if (host != null) {
hostNamesSet.add(host);
} else {
noHost = true;
}
} catch (URISyntaxException e) {
// ignore
}
}
var hostNamesList = new ArrayList<>(hostNamesSet);
var table = new Table<URI>(HtmlStyle.summaryTable)
.setCaption(contents.externalSpecifications)
.setHeader(new TableHeader(contents.specificationLabel, contents.referencedIn))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast)
.setId(HtmlIds.EXTERNAL_SPECS);
if ((hostNamesList.size() + (noHost ? 1 : 0)) > 1) {
for (var host : hostNamesList) {
table.addTab(Text.of(host), u -> host.equals(u.getHost()));
}
if (noHost) {
table.addTab(Text.of(resources.getText("doclet.External_Specifications.no-host")),
u -> u.getHost() == null);
}
}
table.setDefaultTab(Text.of(resources.getText("doclet.External_Specifications.All_Specifications")));
for (List<IndexItem> searchIndexItems : searchIndexMap.values()) {
Content specName = createSpecLink(searchIndexItems.get(0));
IndexItem ii = searchIndexItems.get(0);
Content specName = createSpecLink(ii);
Content referencesList = HtmlTree.UL(HtmlStyle.refList, searchIndexItems,
item -> HtmlTree.LI(createLink(item)));
Content references = searchIndexItems.size() < USE_DETAILS_THRESHHOLD
@ -197,7 +230,12 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
.add(HtmlTree.SUMMARY(contents.getContent("doclet.references",
String.valueOf(searchIndexItems.size()))))
.add(referencesList);
table.addRow(specName, references);
try {
URI uri = getSpecURI(ii);
table.addRow(uri, specName, references);
} catch (URISyntaxException e) {
table.addRow(specName, references);
}
}
content.add(table);
}
@ -278,20 +316,31 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
}
}
private Content createSpecLink(IndexItem i) {
/**
* {@return the fully-resolved URI in index item for a {@code @spec} tag}
*
* While the signature declares that it may throw {@code URISynaxException},
* that should not occur: items with bad URIs should not make it into the index.
*
* @param i the index item
* @throws URISyntaxException if there is an issue creating the URI
*/
private URI getSpecURI(IndexItem i) throws URISyntaxException {
assert i.getDocTree().getKind() == DocTree.Kind.SPEC : i;
SpecTree specTree = (SpecTree) i.getDocTree();
Content title = Text.of(i.getLabel());
URI specURI = new URI(specTree.getURL().getBody());
return resolveExternalSpecURI(specURI);
}
URI specURI;
private Content createSpecLink(IndexItem i) {
Content title = Text.of(i.getLabel());
try {
specURI = new URI(specTree.getURL().getBody());
URI uri = getSpecURI(i);
return HtmlTree.A(uri, title);
} catch (URISyntaxException e) {
// should not happen: items with bad URIs should not make it into the index
return title;
}
return HtmlTree.A(resolveExternalSpecURI(specURI), title);
}
}

View File

@ -143,11 +143,11 @@ public class FieldWriterImpl extends AbstractMemberWriter
}
@Override
protected Table createSummaryTable() {
protected Table<Element> createSummaryTable() {
List<HtmlStyle> bodyRowStyles = Arrays.asList(HtmlStyle.colFirst, HtmlStyle.colSecond,
HtmlStyle.colLast);
return new Table(HtmlStyle.summaryTable)
return new Table<Element>(HtmlStyle.summaryTable)
.setCaption(contents.fields)
.setHeader(getSummaryTableHeader(typeElement))
.setColumnStyles(bodyRowStyles);

View File

@ -79,6 +79,7 @@ public class HtmlIds {
static final HtmlId CONSTRUCTOR_SUMMARY = HtmlId.of("constructor-summary");
static final HtmlId ENUM_CONSTANT_DETAIL = HtmlId.of("enum-constant-detail");
static final HtmlId ENUM_CONSTANT_SUMMARY = HtmlId.of("enum-constant-summary");
static final HtmlId EXTERNAL_SPECS = HtmlId.of("external-specs");
static final HtmlId FIELD_DETAIL = HtmlId.of("field-detail");
static final HtmlId FIELD_SUMMARY = HtmlId.of("field-summary");
static final HtmlId FOR_REMOVAL = HtmlId.of("for-removal");

View File

@ -185,8 +185,8 @@ public class MethodWriterImpl extends AbstractExecutableMemberWriter
}
@Override
protected Table createSummaryTable() {
return new Table(HtmlStyle.summaryTable)
protected Table<Element> createSummaryTable() {
return new Table<Element>(HtmlStyle.summaryTable)
.setHeader(getSummaryTableHeader(typeElement))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast)
.setId(HtmlIds.METHOD_SUMMARY_TABLE)

View File

@ -84,7 +84,7 @@ public class ModuleIndexWriter extends AbstractOverviewIndexWriter {
if (!groupModuleMap.keySet().isEmpty()) {
TableHeader tableHeader = new TableHeader(contents.moduleLabel, contents.descriptionLabel);
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<ModuleElement>(HtmlStyle.summaryTable)
.setHeader(tableHeader)
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast)
.setId(HtmlIds.ALL_MODULES_TABLE)

View File

@ -427,8 +427,8 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
* @param tableHeader the table header
* @return a content object
*/
private Table getTable2(Content caption, TableHeader tableHeader) {
return new Table(HtmlStyle.detailsTable)
private Table<?> getTable2(Content caption, TableHeader tableHeader) {
return new Table<Void>(HtmlStyle.detailsTable)
.setCaption(caption)
.setHeader(tableHeader)
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
@ -441,8 +441,8 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
* @param tableHeader the table header
* @return a content object
*/
private Table getTable3(Content caption, TableHeader tableHeader) {
return new Table(HtmlStyle.detailsTable)
private Table<?> getTable3(Content caption, TableHeader tableHeader) {
return new Table<Void>(HtmlStyle.detailsTable)
.setCaption(caption)
.setHeader(tableHeader)
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast);
@ -460,7 +460,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
if (display(requires)) {
String text = resources.getText("doclet.Requires_Summary");
Content caption = Text.of(text);
Table table = getTable3(caption, requiresTableHeader);
var table = getTable3(caption, requiresTableHeader);
addModulesList(requires, table);
section.add(table);
}
@ -468,7 +468,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
if (display(indirectModules)) {
String amrText = resources.getText("doclet.Indirect_Requires_Summary");
Content amrCaption = Text.of(amrText);
Table amrTable = getTable3(amrCaption, requiresTableHeader);
var amrTable = getTable3(amrCaption, requiresTableHeader);
addModulesList(indirectModules, amrTable);
section.add(amrTable);
}
@ -482,7 +482,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
* @param mdleMap map of modules and modifiers
* @param table the table to which the list will be added
*/
private void addModulesList(Map<ModuleElement, Content> mdleMap, Table table) {
private void addModulesList(Map<ModuleElement, Content> mdleMap, Table<?> table) {
for (ModuleElement m : mdleMap.keySet()) {
Content modifiers = mdleMap.get(m);
Content moduleLink = getModuleLink(m, Text.of(m.getQualifiedName()));
@ -506,13 +506,13 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
new TableHeader(contents.fromLabel, contents.packagesLabel);
if (display(indirectPackages)) {
String aepText = resources.getText("doclet.Indirect_Exports_Summary");
Table aepTable = getTable2(Text.of(aepText), indirectPackagesHeader);
var aepTable = getTable2(Text.of(aepText), indirectPackagesHeader);
addIndirectPackages(aepTable, indirectPackages);
section.add(aepTable);
}
if (display(indirectOpenPackages)) {
String aopText = resources.getText("doclet.Indirect_Opens_Summary");
Table aopTable = getTable2(Text.of(aopText), indirectPackagesHeader);
var aopTable = getTable2(Text.of(aopText), indirectPackagesHeader);
addIndirectPackages(aopTable, indirectOpenPackages);
section.add(aopTable);
}
@ -526,7 +526,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
* @param li
*/
public void addPackageSummary(HtmlTree li) {
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<PackageElement>(HtmlStyle.summaryTable)
.setId(HtmlIds.PACKAGE_SUMMARY_TABLE)
.setDefaultTab(contents.getContent("doclet.All_Packages"))
.addTab(contents.getContent("doclet.Exported_Packages_Summary"), this::isExported)
@ -643,7 +643,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
* @param table the table to which the content rows will be added
* @param ip indirect packages to be added
*/
public void addIndirectPackages(Table table, Map<ModuleElement, SortedSet<PackageElement>> ip) {
public void addIndirectPackages(Table<?> table, Map<ModuleElement, SortedSet<PackageElement>> ip) {
for (Map.Entry<ModuleElement, SortedSet<PackageElement>> entry : ip.entrySet()) {
ModuleElement m = entry.getKey();
SortedSet<PackageElement> pkgList = entry.getValue();
@ -673,7 +673,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
new TableHeader(contents.typeLabel, contents.descriptionLabel);
if (haveProvides) {
String label = resources.getText("doclet.Provides_Summary");
Table table = getTable2(Text.of(label), usesProvidesTableHeader);
var table = getTable2(Text.of(label), usesProvidesTableHeader);
addProvidesList(table);
if (!table.isEmpty()) {
section.add(table);
@ -681,7 +681,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
}
if (haveUses){
String label = resources.getText("doclet.Uses_Summary");
Table table = getTable2(Text.of(label), usesProvidesTableHeader);
var table = getTable2(Text.of(label), usesProvidesTableHeader);
addUsesList(table);
if (!table.isEmpty()) {
section.add(table);
@ -696,7 +696,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
*
* @param table the table to which the services used will be added
*/
public void addUsesList(Table table) {
public void addUsesList(Table<?> table) {
Content typeLinkContent;
Content description;
for (TypeElement t : uses) {
@ -724,7 +724,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
*
* @param table the table to which the services provided will be added
*/
public void addProvidesList(Table table) {
public void addProvidesList(Table<?> table) {
SortedSet<TypeElement> implSet;
Content description;
for (Map.Entry<TypeElement, SortedSet<TypeElement>> entry : provides.entrySet()) {

View File

@ -84,11 +84,11 @@ public class NestedClassWriterImpl extends AbstractMemberWriter
}
@Override
protected Table createSummaryTable() {
protected Table<Element> createSummaryTable() {
List<HtmlStyle> bodyRowStyles = Arrays.asList(HtmlStyle.colFirst, HtmlStyle.colSecond,
HtmlStyle.colLast);
return new Table(HtmlStyle.summaryTable)
return new Table<Element>(HtmlStyle.summaryTable)
.setCaption(contents.getContent("doclet.Nested_Classes"))
.setHeader(getSummaryTableHeader(typeElement))
.setColumnStyles(bodyRowStyles);

View File

@ -96,7 +96,7 @@ public class NewAPIListWriter extends SummaryListWriter<NewAPIBuilder> {
}
@Override
protected void addTableTabs(Table table, String headingKey) {
protected void addTableTabs(Table<Element> table, String headingKey) {
table.setGridStyle(HtmlStyle.threeColumnReleaseSummary);
List<String> releases = builder.releases;
if (releases.size() > 1) {

View File

@ -85,7 +85,7 @@ public class PackageIndexWriter extends AbstractOverviewIndexWriter {
= configuration.group.groupPackages(packages);
if (!groupPackageMap.keySet().isEmpty()) {
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<PackageElement>(HtmlStyle.summaryTable)
.setHeader(getPackageTableHeader())
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast)
.setId(HtmlIds.ALL_PACKAGES_TABLE)

View File

@ -149,7 +149,7 @@ public class PackageUseWriter extends SubWriterHolderWriter {
Content caption = contents.getContent(
"doclet.ClassUse_Packages.that.use.0",
getPackageLink(packageElement, getLocalizedPackageName(packageElement)));
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<Void>(HtmlStyle.summaryTable)
.setCaption(caption)
.setHeader(getPackageTableHeader())
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
@ -185,7 +185,7 @@ public class PackageUseWriter extends SubWriterHolderWriter {
"doclet.ClassUse_Classes.in.0.used.by.1",
getPackageLink(packageElement, getLocalizedPackageName(packageElement)),
getPackageLink(usingPackage, getLocalizedPackageName(usingPackage)));
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<Void>(HtmlStyle.summaryTable)
.setCaption(caption)
.setHeader(classTableHeader)
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);

View File

@ -241,16 +241,16 @@ public class PackageWriterImpl extends HtmlDocletWriter
* @param target the content to which the links will be added
*/
public void addAllClassesAndInterfacesSummary(Content target) {
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<TypeElement>(HtmlStyle.summaryTable)
.setHeader(new TableHeader(contents.classLabel, contents.descriptionLabel))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast)
.setId(HtmlIds.CLASS_SUMMARY)
.setDefaultTab(contents.allClassesAndInterfacesLabel)
.addTab(contents.interfaces, utils::isPlainInterface)
.addTab(contents.classes, e -> utils.isNonThrowableClass((TypeElement)e))
.addTab(contents.classes, e -> utils.isNonThrowableClass(e))
.addTab(contents.enums, utils::isEnum)
.addTab(contents.records, e -> utils.isRecord((TypeElement)e))
.addTab(contents.exceptionClasses, e -> utils.isThrowable((TypeElement)e))
.addTab(contents.records, e -> utils.isRecord(e))
.addTab(contents.exceptionClasses, e -> utils.isThrowable(e))
.addTab(contents.annotationTypes, utils::isAnnotationInterface);
for (TypeElement typeElement : allClasses) {
if (typeElement != null && utils.isCoreClass(typeElement)) {
@ -279,7 +279,7 @@ public class PackageWriterImpl extends HtmlDocletWriter
TableHeader tableHeader, Content summaryContent,
boolean showModules) {
if (!packages.isEmpty()) {
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<Void>(HtmlStyle.summaryTable)
.setId(HtmlIds.RELATED_PACKAGE_SUMMARY)
.setCaption(label)
.setHeader(tableHeader);

View File

@ -110,7 +110,7 @@ public class PreviewListWriter extends SummaryListWriter<PreviewAPIListBuilder>
}
@Override
protected void addTableTabs(Table table, String headingKey) {
protected void addTableTabs(Table<Element> table, String headingKey) {
table.setGridStyle(HtmlStyle.threeColumnSummary)
.setDefaultTab(getTableCaption(headingKey))
.setAlwaysShowDefaultTab(true)

View File

@ -154,8 +154,8 @@ public class PropertyWriterImpl extends AbstractMemberWriter
}
@Override
protected Table createSummaryTable() {
return new Table(HtmlStyle.summaryTable)
protected Table<Element> createSummaryTable() {
return new Table<Element>(HtmlStyle.summaryTable)
.setCaption(contents.properties)
.setHeader(getSummaryTableHeader(typeElement))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast);

View File

@ -35,7 +35,6 @@ import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
import jdk.javadoc.internal.doclets.formats.html.markup.Script;
import jdk.javadoc.internal.doclets.formats.html.markup.TagName;
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.Text;
@ -209,7 +208,7 @@ public abstract class SummaryListWriter<B extends SummaryAPIListBuilder> extends
if (apiList.size() > 0) {
TableHeader tableHeader = getTableHeader(headerKey);
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<Element>(HtmlStyle.summaryTable)
.setCaption(getTableCaption(headingKey))
.setHeader(tableHeader)
.setId(id)
@ -337,5 +336,5 @@ public abstract class SummaryListWriter<B extends SummaryAPIListBuilder> extends
* @param table the element table
* @param headingKey the key for the caption (default tab)
*/
protected abstract void addTableTabs(Table table, String headingKey);
protected abstract void addTableTabs(Table<Element> table, String headingKey);
}

View File

@ -32,7 +32,6 @@ import jdk.javadoc.internal.doclets.formats.html.markup.Text;
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.Navigation.PageMode;
import jdk.javadoc.internal.doclets.formats.html.markup.TextBuilder;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.DocletElement;
import jdk.javadoc.internal.doclets.toolkit.OverviewElement;
@ -121,7 +120,7 @@ public class SystemPropertiesWriter extends HtmlDocletWriter {
protected void addSystemProperties(Content target) {
Map<String, List<IndexItem>> searchIndexMap = groupSystemProperties();
Content separator = Text.of(", ");
Table table = new Table(HtmlStyle.summaryTable)
var table = new Table<Void>(HtmlStyle.summaryTable)
.setCaption(contents.systemPropertiesSummaryLabel)
.setHeader(new TableHeader(contents.propertyLabel, contents.referencedIn))
.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);

View File

@ -34,8 +34,6 @@ import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import javax.lang.model.element.Element;
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId;
@ -46,9 +44,10 @@ import jdk.javadoc.internal.doclets.formats.html.markup.Text;
import jdk.javadoc.internal.doclets.toolkit.Content;
/**
* An HTML container used to display summary tables for various kinds of elements.
* An HTML container used to display summary tables for various kinds of elements
* and other tabular data.
* This class historically used to generate an HTML {@code <table>} element but has been
* updated to render elements as a stream of {@code <div>} elements that rely on
* updated to render its content as a stream of {@code <div>} elements that rely on
* <a href="https://www.w3.org/TR/css-grid-1/">CSS Grid Layout</a> for styling.
* This provides for more flexible layout options, such as splitting up table rows on
* small displays.
@ -65,13 +64,20 @@ import jdk.javadoc.internal.doclets.toolkit.Content;
* A table may support filtered views, which can be selected by clicking on
* one of a list of tabs above the table. If the table does not support filtered
* views, the caption element is typically displayed as a single (inactive)
* tab.
* tab. The filtered views use a {@link Predicate} to identify the
* rows to be shown for each {@link #addTab(Content, Predicate) tab}. The
* type parameter for the predicate is the type parameter {@code T} for the table.
* The type parameter should be {@link Void} when the table is not configured
* to use tabs.
*
* @param <T> the class or interface used to distinguish the rows to be displayed
* for each tab, or {@code Void} when a table does not contain tabs
*/
public class Table extends Content {
public class Table<T> extends Content {
private final HtmlStyle tableStyle;
private Content caption;
private List<Tab> tabs;
private Set<Tab> occurringTabs;
private List<Tab<T>> tabs;
private Set<Tab<T>> occurringTabs;
private Content defaultTab;
private boolean renderTabs = true;
private TableHeader header;
@ -84,7 +90,7 @@ public class Table extends Content {
/**
* A record containing the data for a table tab.
*/
record Tab(Content label, Predicate<Element> predicate, int index) {}
record Tab<T>(Content label, Predicate<T> predicate, int index) {}
/**
* Creates a builder for an HTML element representing a table.
@ -104,7 +110,7 @@ public class Table extends Content {
* @param captionContent the caption
* @return this object
*/
public Table setCaption(Content captionContent) {
public Table<T> setCaption(Content captionContent) {
caption = getCaption(captionContent);
return this;
}
@ -112,21 +118,21 @@ public class Table extends Content {
/**
* Adds a tab to the table.
* Tabs provide a way to display subsets of rows, as determined by a
* predicate for the tab, and an element associated with each row.
* predicate for the tab, and an item associated with each row.
* Tabs will appear left-to-right in the order they are added.
*
* @param label the tab label
* @param predicate the predicate
* @return this object
*/
public Table addTab(Content label, Predicate<Element> predicate) {
public Table<T> addTab(Content label, Predicate<T> predicate) {
if (tabs == null) {
tabs = new ArrayList<>(); // preserves order that tabs are added
occurringTabs = new HashSet<>(); // order not significant
}
// Use current size of tabs list as id so we have tab ids that are consistent
// across tables with the same tabs but different content.
tabs.add(new Tab(label, predicate, tabs.size() + 1));
tabs.add(new Tab<>(label, predicate, tabs.size() + 1));
return this;
}
@ -137,7 +143,7 @@ public class Table extends Content {
* @param label the default tab label
* @return this object
*/
public Table setDefaultTab(Content label) {
public Table<T> setDefaultTab(Content label) {
defaultTab = label;
return this;
}
@ -147,19 +153,19 @@ public class Table extends Content {
* @param showDefaultTab true if default tab should always be shown
* @return this object
*/
public Table setAlwaysShowDefaultTab(boolean showDefaultTab) {
public Table<T> setAlwaysShowDefaultTab(boolean showDefaultTab) {
this.alwaysShowDefaultTab = showDefaultTab;
return this;
}
/**
* Allows to set whether tabs should be rendered for this table. Some pages use their
* own controls to select table catetories, in which case the tabs are omitted.
* own controls to select table categories, in which case the tabs are omitted.
*
* @param renderTabs true if table tabs should be rendered
* @return this object
*/
public Table setRenderTabs(boolean renderTabs) {
public Table<T> setRenderTabs(boolean renderTabs) {
this.renderTabs = renderTabs;
return this;
}
@ -175,7 +181,7 @@ public class Table extends Content {
* @param header the header
* @return this object
*/
public Table setHeader(TableHeader header) {
public Table<T> setHeader(TableHeader header) {
this.header = header;
return this;
}
@ -191,7 +197,7 @@ public class Table extends Content {
* @param styles the styles
* @return this object
*/
public Table setColumnStyles(HtmlStyle... styles) {
public Table<T> setColumnStyles(HtmlStyle... styles) {
return setColumnStyles(Arrays.asList(styles));
}
@ -206,7 +212,7 @@ public class Table extends Content {
* @param styles the styles
* @return this object
*/
public Table setColumnStyles(List<HtmlStyle> styles) {
public Table<T> setColumnStyles(List<HtmlStyle> styles) {
columnStyles = styles;
return this;
}
@ -219,7 +225,7 @@ public class Table extends Content {
* @param gridStyle the grid style
* @return this object
*/
public Table setGridStyle(HtmlStyle gridStyle) {
public Table<T> setGridStyle(HtmlStyle gridStyle) {
this.gridStyle = gridStyle;
return this;
}
@ -233,7 +239,7 @@ public class Table extends Content {
* @param id the id
* @return this object
*/
public Table setId(HtmlId id) {
public Table<T> setId(HtmlId id) {
this.id = id;
return this;
}
@ -256,7 +262,7 @@ public class Table extends Content {
* Each item of content should be suitable for use as the content of a
* {@code <th>} or {@code <td> cell}.
* This method should not be used when the table has tabs: use a method
* that takes an {@code element} parameter instead.
* that takes an {@code item} parameter instead.
*
* @param contents the contents for the row
*/
@ -269,18 +275,18 @@ public class Table extends Content {
* Each item of content should be suitable for use as the content of a
* {@code <th>} or {@code <td>} cell.
*
* If tabs have been added to the table, the specified element will be used
* If tabs have been added to the table, the specified item will be used
* to determine whether the row should be displayed when any particular tab
* is selected, using the predicate specified when the tab was
* {@link #addTab(Content, Predicate) added}.
*
* @param element the element
* @param item the item
* @param contents the contents for the row
* @throws NullPointerException if tabs have previously been added to the table
* and {@code element} is null
* and {@code item} is null
*/
public void addRow(Element element, Content... contents) {
addRow(element, Arrays.asList(contents));
public void addRow(T item, Content... contents) {
addRow(item, Arrays.asList(contents));
}
/**
@ -288,18 +294,18 @@ public class Table extends Content {
* Each item of content should be suitable for use as the content of a
* {@code <div>} cell.
*
* If tabs have been added to the table, the specified element will be used
* If tabs have been added to the table, the specified item will be used
* to determine whether the row should be displayed when any particular tab
* is selected, using the predicate specified when the tab was
* {@link #addTab(Content, Predicate) added}.
*
* @param element the element
* @param item the item
* @param contents the contents for the row
* @throws NullPointerException if tabs have previously been added to the table
* and {@code element} is null
* and {@code item} is null
*/
public void addRow(Element element, List<Content> contents) {
if (tabs != null && element == null) {
public void addRow(T item, List<Content> contents) {
if (tabs != null && item == null) {
throw new NullPointerException();
}
if (contents.size() != columnStyles.size()) {
@ -315,11 +321,11 @@ public class Table extends Content {
if (tabs != null) {
// Construct a series of values to add to the HTML 'class' attribute for the cells of
// this row, such that there is a default value and a value corresponding to each tab
// whose predicate matches the element. The values correspond to the equivalent ids.
// whose predicate matches the item. The values correspond to the equivalent ids.
// The values are used to determine the cells to make visible when a tab is selected.
tabClasses.add(id.name());
for (Tab tab : tabs) {
if (tab.predicate().test(element)) {
for (var tab : tabs) {
if (tab.predicate().test(item)) {
occurringTabs.add(tab);
tabClasses.add(HtmlIds.forTab(id, tab.index()).name());
}
@ -342,7 +348,7 @@ public class Table extends Content {
}
/**
* Returns whether or not the table is empty.
* Returns whether the table is empty.
* The table is empty if it has no (body) rows.
*
* @return true if the table has no rows
@ -400,7 +406,7 @@ public class Table extends Content {
}
table.put(HtmlAttr.ARIA_LABELLEDBY, defaultTabId.name());
if (renderTabs) {
for (Tab tab : tabs) {
for (var tab : tabs) {
if (occurringTabs.contains(tab)) {
tablist.add(createTab(HtmlIds.forTab(id, tab.index()), HtmlStyle.tableTab, false, tab.label()));
}

View File

@ -164,6 +164,8 @@ doclet.Inheritance_Tree=Inheritance Tree
doclet.ReferencedIn=Referenced In
doclet.External_Specification=External Specification
doclet.External_Specifications=External Specifications
doclet.External_Specifications.All_Specifications=All Specifications
doclet.External_Specifications.no-host=Local
doclet.Specification=Specification
doclet.System_Property=System Property
doclet.systemProperties=System Properties

View File

@ -290,6 +290,61 @@ public class TestSpecTag extends JavadocTester {
</div>
""");
}
@Test
public void testMultipleHosts(Path base) throws IOException {
Path src = base.resolve("src");
tb.writeJavaFiles(src, """
package p;
/**
* First sentence.
* @spec http://example.com/1 example-1
* @spec http://example.net/2 example-2
*/
public class C { }
""");
javadoc("-d", base.resolve("out").toString(),
"--source-path", src.toString(),
"p");
checkExit(Exit.OK);
checkOutput("p/C.html", true,
"""
<dt>External Specifications</dt>
<dd><a href="http://example.com/1"><span id="example-1" class="search-tag-result">example-1</span></a>,\s
<a href="http://example.net/2"><span id="example-2" class="search-tag-result">example-2</span></a></dd>
""");
checkOutput("external-specs.html", true,
"""
<div class="table-tabs" role="tablist" aria-orientation="horizontal">\
<button id="external-specs-tab0" role="tab" aria-selected="true" aria-controls="external-specs.tabpanel" \
tabindex="0" onkeydown="switchTab(event)" onclick="show('external-specs', 'external-specs', 2)" \
class="active-table-tab">All Specifications</button>\
<button id="external-specs-tab1" role="tab" aria-selected="false" aria-controls="external-specs.tabpanel" \
tabindex="-1" onkeydown="switchTab(event)" onclick="show('external-specs', 'external-specs-tab1', 2)" \
class="table-tab">example.com</button>\
<button id="external-specs-tab2" role="tab" aria-selected="false" aria-controls="external-specs.tabpanel" \
tabindex="-1" onkeydown="switchTab(event)" onclick="show('external-specs', 'external-specs-tab2', 2)" \
class="table-tab">example.net</button></div>
<div id="external-specs.tabpanel" role="tabpanel">
<div class="summary-table two-column-summary" aria-labelledby="external-specs-tab0">
<div class="table-header col-first">Specification</div>
<div class="table-header col-last">Referenced In</div>""",
"""
<div class="col-first even-row-color external-specs external-specs-tab1"><a href="http://example.com/1">example-1</a></div>
<div class="col-last even-row-color external-specs external-specs-tab1">
<ul class="ref-list">
<li><code><a href="p/C.html#example-1">class p.C</a></code></li>
</ul>
</div>
<div class="col-first odd-row-color external-specs external-specs-tab2"><a href="http://example.net/2">example-2</a></div>
<div class="col-last odd-row-color external-specs external-specs-tab2">
<ul class="ref-list">
<li><code><a href="p/C.html#example-2">class p.C</a></code></li>
</ul>
</div>""");
}
@Test
public void testMultipleTitlesForURL(Path base) throws IOException {