8309748: Improve host selection in External Specifications page

Reviewed-by: nbenalla
This commit is contained in:
Hannes Wallnöfer 2026-02-26 18:50:45 +00:00
parent 8b805630b4
commit aa6c06e166
5 changed files with 98 additions and 30 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2026, 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
@ -11233,7 +11233,7 @@ class Character implements java.io.Serializable, Comparable<Character>, Constabl
* @param codePoint the character (Unicode code point) to be tested.
* @return {@code true} if the character is an Emoji;
* {@code false} otherwise.
* @spec https://unicode.org/reports/tr51/ Unicode Emoji
* @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
* @since 21
*/
public static boolean isEmoji(int codePoint) {
@ -11252,7 +11252,7 @@ class Character implements java.io.Serializable, Comparable<Character>, Constabl
* @param codePoint the character (Unicode code point) to be tested.
* @return {@code true} if the character has the Emoji Presentation
* property; {@code false} otherwise.
* @spec https://unicode.org/reports/tr51/ Unicode Emoji
* @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
* @since 21
*/
public static boolean isEmojiPresentation(int codePoint) {
@ -11271,7 +11271,7 @@ class Character implements java.io.Serializable, Comparable<Character>, Constabl
* @param codePoint the character (Unicode code point) to be tested.
* @return {@code true} if the character is an Emoji Modifier;
* {@code false} otherwise.
* @spec https://unicode.org/reports/tr51/ Unicode Emoji
* @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
* @since 21
*/
public static boolean isEmojiModifier(int codePoint) {
@ -11290,7 +11290,7 @@ class Character implements java.io.Serializable, Comparable<Character>, Constabl
* @param codePoint the character (Unicode code point) to be tested.
* @return {@code true} if the character is an Emoji Modifier Base;
* {@code false} otherwise.
* @spec https://unicode.org/reports/tr51/ Unicode Emoji
* @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
* @since 21
*/
public static boolean isEmojiModifierBase(int codePoint) {
@ -11309,7 +11309,7 @@ class Character implements java.io.Serializable, Comparable<Character>, Constabl
* @param codePoint the character (Unicode code point) to be tested.
* @return {@code true} if the character is an Emoji Component;
* {@code false} otherwise.
* @spec https://unicode.org/reports/tr51/ Unicode Emoji
* @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
* @since 21
*/
public static boolean isEmojiComponent(int codePoint) {
@ -11328,7 +11328,7 @@ class Character implements java.io.Serializable, Comparable<Character>, Constabl
* @param codePoint the character (Unicode code point) to be tested.
* @return {@code true} if the character is an Extended Pictographic;
* {@code false} otherwise.
* @spec https://unicode.org/reports/tr51/ Unicode Emoji
* @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
* @since 21
*/
public static boolean isExtendedPictographic(int codePoint) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, 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
@ -57,7 +57,11 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
import jdk.javadoc.internal.html.Content;
import jdk.javadoc.internal.html.ContentBuilder;
import jdk.javadoc.internal.html.HtmlAttr;
import jdk.javadoc.internal.html.HtmlId;
import jdk.javadoc.internal.html.HtmlTag;
import jdk.javadoc.internal.html.HtmlTree;
import jdk.javadoc.internal.html.Script;
import jdk.javadoc.internal.html.Text;
import static java.util.stream.Collectors.groupingBy;
@ -108,6 +112,24 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING,
contents.getContent("doclet.External_Specifications"))))
.addMainContent(mainContent)
.addMainContent(new Script("""
let select = document.getElementById('specs-by-domain');
select.addEventListener("change", selectHost);
addEventListener("pageshow", selectHost);
function selectHost() {
const selectedClass = select.value ? "external-specs-tab" + select.value : "external-specs";
let tabPanel = document.getElementById("external-specs.tabpanel");
let count = 0;
tabPanel.querySelectorAll("div.external-specs").forEach(function(elem) {
elem.style.display = elem.classList.contains(selectedClass) ? "" : "none";
if (elem.style.display === "") {
let isEvenRow = count++ % 4 < 2;
toggleStyle(elem.classList, isEvenRow, evenRowColor, oddRowColor);
}
});
}
selectHost();
""").asContent())
.setFooter(getFooter()));
printHtmlDocument(null, "external specifications", body);
@ -180,7 +202,7 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
boolean noHost = false;
for (var searchIndexItems : searchIndexMap.values()) {
try {
URI uri = getSpecURI(searchIndexItems.get(0));
URI uri = getSpecURI(searchIndexItems.getFirst());
String host = uri.getHost();
if (host != null) {
hostNamesSet.add(host);
@ -191,14 +213,19 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
// ignore
}
}
var hostNamesList = new ArrayList<>(hostNamesSet);
var table = new Table<URI>(HtmlStyles.summaryTable)
.setCaption(contents.externalSpecifications)
.setHeader(new TableHeader(contents.specificationLabel, contents.referencedIn))
.setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast)
.setId(HtmlIds.EXTERNAL_SPECS);
.setId(HtmlIds.EXTERNAL_SPECS)
.setDefaultTab(contents.externalSpecifications)
.setRenderTabs(false);
var hostNamesList = new ArrayList<>(hostNamesSet);
Content selector = Text.EMPTY;
if ((hostNamesList.size() + (noHost ? 1 : 0)) > 1) {
selector = createHostSelect(hostNamesList, noHost);
for (var host : hostNamesList) {
table.addTab(Text.of(host), u -> host.equals(u.getHost()));
}
@ -207,10 +234,9 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
u -> u.getHost() == null);
}
}
table.setDefaultTab(Text.of(resources.getText("doclet.External_Specifications.All_Specifications")));
for (List<IndexItem> searchIndexItems : searchIndexMap.values()) {
IndexItem ii = searchIndexItems.get(0);
IndexItem ii = searchIndexItems.getFirst();
Content specName = createSpecLink(ii);
Content referencesList = HtmlTree.UL(HtmlStyles.refList, searchIndexItems,
item -> HtmlTree.LI(createLink(item)));
@ -227,6 +253,7 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
table.addRow(specName, references);
}
}
content.add(selector);
content.add(table);
}
@ -235,6 +262,29 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
.collect(groupingBy(IndexItem::getLabel, () -> new TreeMap<>(getTitleComparator()), toList()));
}
private Content createHostSelect(List<String> hosts, boolean hasLocal) {
var index = 1;
var id = HtmlId.of("specs-by-domain");
var specsByHost = resources.getText("doclet.External_Specifications.by-host");
var select = HtmlTree.of(HtmlTag.SELECT)
.setId(id)
.add(HtmlTree.of(HtmlTag.OPTION)
.put(HtmlAttr.VALUE, "")
.add(Text.of(resources.getText("doclet.External_Specifications.all-hosts"))));
for (var host : hosts) {
select.add(HtmlTree.of(HtmlTag.OPTION)
.put(HtmlAttr.VALUE, Integer.toString(index++))
.add(Text.of(host)));
}
if (hasLocal) {
select.add(HtmlTree.of(HtmlTag.OPTION)
.put(HtmlAttr.VALUE, Integer.toString(index))
.add(Text.of("Local")));
}
return new ContentBuilder(HtmlTree.LABEL(id.name(), Text.of(specsByHost)), Text.of(" "), select);
}
Comparator<String> getTitleComparator() {
Collator collator = Collator.getInstance();
return (s1, s2) -> {

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2010, 2026, 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
@ -180,7 +180,8 @@ doclet.Inheritance_Tree=Inheritance Tree
doclet.DefinedIn=Defined In
doclet.ReferencedIn=Referenced In
doclet.External_Specifications=External Specifications
doclet.External_Specifications.All_Specifications=All Specifications
doclet.External_Specifications.by-host=Show specifications by host name:
doclet.External_Specifications.all-hosts=All host names
doclet.External_Specifications.no-host=Local
doclet.Specification=Specification
doclet.Summary_Page=Summary Page

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
@ -1228,7 +1228,7 @@ input::placeholder {
input:focus::placeholder {
color: transparent;
}
select#search-modules {
select {
margin: 0 10px 10px 2px;
font-size: var(--nav-font-size);
padding: 3px 5px;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2026, 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
@ -23,7 +23,7 @@
/*
* @test
* @bug 6251738 8226279 8297802 8305407
* @bug 6251738 8226279 8297802 8305407 8309748
* @summary JDK-8226279 javadoc should support a new at-spec tag
* @library /tools/lib ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool
@ -343,16 +343,15 @@ public class TestSpecTag extends JavadocTester {
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>
<label for="specs-by-domain">Show specifications by host name:</label> <select id="specs-by-domain">
<option value>All host names</option>
<option value="1">example.com</option>
<option value="2">example.net</option>
</select>
<div id="external-specs">
<div class="table-tabs">
<div class="caption"><span>External Specifications</span></div>
</div>
<div id="external-specs.tabpanel" role="tabpanel" aria-labelledby="external-specs-tab0">
<div class="summary-table two-column-summary">
<div class="table-header col-first">Specification</div>
@ -369,7 +368,25 @@ public class TestSpecTag extends JavadocTester {
<ul class="ref-list">
<li><code><a href="p/C.html#example-2">class p.C</a></code></li>
</ul>
</div>""");
</div>""",
"""
<script type="text/javascript">let select = document.getElementById('specs-by-domain');
select.addEventListener("change", selectHost);
addEventListener("pageshow", selectHost);
function selectHost() {
const selectedClass = select.value ? "external-specs-tab" + select.value : "external-specs";
let tabPanel = document.getElementById("external-specs.tabpanel");
let count = 0;
tabPanel.querySelectorAll("div.external-specs").forEach(function(elem) {
elem.style.display = elem.classList.contains(selectedClass) ? "" : "none";
if (elem.style.display === "") {
let isEvenRow = count++ % 4 < 2;
toggleStyle(elem.classList, isEvenRow, evenRowColor, oddRowColor);
}
});
}
selectHost();
</script>""");
}
@Test