8268422: Find a better way to select releases in "New API" page

Reviewed-by: jjg
This commit is contained in:
Hannes Wallnöfer 2022-05-27 11:09:26 +00:00
parent 22e2067349
commit 777f813e9b
22 changed files with 804 additions and 646 deletions

View File

@ -384,11 +384,7 @@ public abstract class AbstractMemberWriter implements MemberSummaryWriter, Membe
if (tElement != typeElement) {
throw new IllegalStateException();
}
Table table = getSummaryTable();
if (table.needsScript()) {
writer.getMainBodyScript().append(table.getScript());
}
return table;
return getSummaryTable();
}
@Override

View File

@ -137,9 +137,6 @@ public class AllClassesIndexWriter extends HtmlDocletWriter {
target.add(headerDiv);
if (!table.isEmpty()) {
target.add(table);
if (table.needsScript()) {
getMainBodyScript().append(table.getScript());
}
}
}

View File

@ -31,6 +31,8 @@ import java.util.ListIterator;
import javax.lang.model.element.Element;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
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.HtmlTree;
import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode;
@ -78,12 +80,25 @@ public class DeprecatedListWriter extends SummaryListWriter<DeprecatedAPIListBui
@Override
protected void addExtraSection(DeprecatedAPIListBuilder list, Content content) {
if (list.releases.size() > 1) {
content.add(HtmlTree.SPAN(contents.getContent("doclet.Deprecated_Tabs_Intro"))
.addStyle(HtmlStyle.helpNote));
List<String> releases = configuration.deprecatedAPIListBuilder.releases;
if (!releases.isEmpty()) {
Content tabs = HtmlTree.DIV(HtmlStyle.checkboxes, contents.getContent(
"doclet.Deprecated_API_Checkbox_Label"));
for (int i = 0; i < releases.size(); i++) {
int releaseIndex = i + 1;
String release = releases.get(i);
HtmlId htmlId = HtmlId.of("release-" + releaseIndex);
tabs.add(HtmlTree.LABEL(htmlId.name(),
HtmlTree.INPUT("checkbox", htmlId)
.put(HtmlAttr.CHECKED, "")
.put(HtmlAttr.ONCLICK,
"toggleGlobal(this, '" + releaseIndex + "', 3)"))
.add(HtmlTree.SPAN(Text.of(release))));
}
content.add(tabs);
}
addSummaryAPI(list.getForRemoval(), HtmlIds.FOR_REMOVAL,
TERMINALLY_DEPRECATED_KEY, "doclet.Element", content);
TERMINALLY_DEPRECATED_KEY, "doclet.Element", content);
}
@Override
@ -107,20 +122,53 @@ public class DeprecatedListWriter extends SummaryListWriter<DeprecatedAPIListBui
protected void addTableTabs(Table table, String headingKey) {
List<String> releases = configuration.deprecatedAPIListBuilder.releases;
if (!releases.isEmpty()) {
table.setDefaultTab(getTableCaption(headingKey)).setAlwaysShowDefaultTab(true);
ListIterator<String> it = releases.listIterator(releases.size());
while (it.hasPrevious()) {
String release = it.previous();
table.setDefaultTab(getTableCaption(headingKey))
.setAlwaysShowDefaultTab(true)
.setRenderTabs(false)
.setGridStyle(HtmlStyle.threeColumnReleaseSummary);
for (String release : releases) {
Content tab = TERMINALLY_DEPRECATED_KEY.equals(headingKey)
? contents.getContent("doclet.Terminally_Deprecated_In_Release", release)
: contents.getContent("doclet.Deprecated_In_Release", release);
table.addTab(tab,
element -> release.equals(utils.getDeprecatedSince(element)));
}
getMainBodyScript().append(table.getScript());
}
}
@Override
protected Content getExtraContent(Element element) {
List<String> releases = configuration.deprecatedAPIListBuilder.releases;
if (releases.isEmpty()) {
return null;
}
String deprecatedSince = utils.getDeprecatedSince(element);
return deprecatedSince == null || deprecatedSince.isEmpty()
? Text.EMPTY : Text.of(deprecatedSince);
}
@Override
protected TableHeader getTableHeader(String headerKey) {
List<String> releases = configuration.deprecatedAPIListBuilder.releases;
if (releases.isEmpty()) {
return super.getTableHeader(headerKey);
}
return new TableHeader(
contents.getContent(headerKey),
contents.getContent("doclet.Deprecated_Elements_Release_Column_Header"),
contents.descriptionLabel)
.sortable(true, true, false); // Allow sorting by element name and release
}
@Override
protected HtmlStyle[] getColumnStyles() {
List<String> releases = configuration.deprecatedAPIListBuilder.releases;
if (releases.isEmpty()) {
return super.getColumnStyles();
}
return new HtmlStyle[]{ HtmlStyle.colSummaryItemName, HtmlStyle.colSecond, HtmlStyle.colLast };
}
@Override
protected Content getTableCaption(String headingKey) {
Content caption = contents.getContent(headingKey);

View File

@ -111,10 +111,6 @@ public class ModuleIndexWriter extends AbstractOverviewIndexWriter {
}
target.add(table);
if (table.needsScript()) {
mainBodyScript.append(table.getScript());
}
}
}
}

View File

@ -603,9 +603,6 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
}
li.add(table);
if (table.needsScript()) {
mainBodyScript.append(table.getScript());
}
}
private boolean isExported(Element e) {

View File

@ -29,6 +29,8 @@ import javax.lang.model.element.Element;
import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
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.HtmlTree;
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
@ -40,7 +42,6 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.NewAPIBuilder;
import java.util.List;
import java.util.ListIterator;
import static com.sun.source.doctree.DocTree.Kind.SINCE;
@ -56,7 +57,7 @@ public class NewAPIListWriter extends SummaryListWriter<NewAPIBuilder> {
*/
public NewAPIListWriter(NewAPIBuilder builder, HtmlConfiguration configuration, DocPath filename) {
super(configuration, filename, PageMode.NEW, "new elements",
Text.of(getHeading(builder, configuration)),
Text.of(getHeading(configuration)),
"doclet.Window_New_List");
}
@ -77,30 +78,39 @@ public class NewAPIListWriter extends SummaryListWriter<NewAPIBuilder> {
@Override
protected void addExtraSection(NewAPIBuilder list, Content content) {
if (list.releases.size() > 1) {
content.add(HtmlTree.SPAN(contents.getContent("doclet.New_Tabs_Intro"))
.addStyle(HtmlStyle.helpNote));
}
List<String> releases = configuration.newAPIPageBuilder.releases;
if (!releases.isEmpty()) {
Content tabs = HtmlTree.DIV(HtmlStyle.checkboxes,
contents.getContent("doclet.New_API_Checkbox_Label"));
for (int i = 0; i < releases.size(); i++) {
int releaseIndex = i + 1;
String release = releases.get(i);
HtmlId htmlId = HtmlId.of("release-" + releaseIndex);
tabs.add(HtmlTree.LABEL(htmlId.name(),
HtmlTree.INPUT("checkbox", htmlId)
.put(HtmlAttr.CHECKED, "")
.put(HtmlAttr.ONCLICK,
"toggleGlobal(this, '" + releaseIndex + "', 3)"))
.add(HtmlTree.SPAN(Text.of(release))));
}
content.add(tabs); }
}
@Override
protected void addTableTabs(Table table, String headingKey) {
List<String> releases = configuration.newAPIPageBuilder.releases;
if (!releases.isEmpty()) {
table.setDefaultTab(getTableCaption(headingKey)).setAlwaysShowDefaultTab(true);
ListIterator<String> it = releases.listIterator(releases.size());
while (it.hasPrevious()) {
String release = it.previous();
table.setDefaultTab(getTableCaption(headingKey))
.setAlwaysShowDefaultTab(true)
.setRenderTabs(false)
.setGridStyle(HtmlStyle.threeColumnReleaseSummary);
for (String release : releases) {
table.addTab(
releases.size() == 1
? getTableCaption(headingKey)
: contents.getContent(
"doclet.New_Elements_Added_In_Release", release),
: Text.of(release),
element -> {
if (!utils.hasDocCommentTree(element)) {
return false;
}
List<? extends DocTree> since = utils.getBlockTags(element, SINCE);
List<? extends DocTree> since = getSinceTree(element);
if (since.isEmpty()) {
return false;
}
@ -108,7 +118,6 @@ public class NewAPIListWriter extends SummaryListWriter<NewAPIBuilder> {
return since.stream().anyMatch(tree -> release.equals(ch.getBody(tree).toString()));
});
}
getMainBodyScript().append(table.getScript());
}
}
@ -122,7 +131,35 @@ public class NewAPIListWriter extends SummaryListWriter<NewAPIBuilder> {
return contents.getContent("doclet.New_Elements", super.getTableCaption(headingKey));
}
private static String getHeading(NewAPIBuilder builder, HtmlConfiguration configuration) {
@Override
protected Content getExtraContent(Element element) {
List<? extends DocTree> sinceTree = getSinceTree(element);
if (!sinceTree.isEmpty()) {
CommentHelper ch = utils.getCommentHelper(element);
return Text.of(ch.getBody(sinceTree.get(0)).toString());
}
return Text.EMPTY;
}
@Override
protected TableHeader getTableHeader(String headerKey) {
return new TableHeader(
contents.getContent(headerKey),
contents.getContent("doclet.New_Elements_Release_Column_Header"),
contents.descriptionLabel)
.sortable(true, true, false); // Allow sorting by element name and release
}
@Override
protected HtmlStyle[] getColumnStyles() {
return new HtmlStyle[]{ HtmlStyle.colSummaryItemName, HtmlStyle.colSecond, HtmlStyle.colLast };
}
private List<? extends DocTree> getSinceTree(Element element) {
return utils.hasDocCommentTree(element) ? utils.getBlockTags(element, SINCE) : List.of();
}
private static String getHeading(HtmlConfiguration configuration) {
String label = configuration.getOptions().sinceLabel();
return label == null ? configuration.docResources.getText("doclet.New_API") : label;
}

View File

@ -109,10 +109,6 @@ public class PackageIndexWriter extends AbstractOverviewIndexWriter {
}
target.add(table);
if (table.needsScript()) {
getMainBodyScript().append(table.getScript());
}
}
}
}

View File

@ -272,9 +272,6 @@ public class PackageWriterImpl extends HtmlDocletWriter
}
if (!table.isEmpty()) {
target.add(HtmlTree.LI(table));
if (table.needsScript()) {
getMainBodyScript().append(table.getScript());
}
}
}

View File

@ -196,32 +196,35 @@ public class SummaryListWriter<L extends SummaryAPIListBuilder> extends SubWrite
String headingKey, String headerKey,
Content content) {
if (apiList.size() > 0) {
TableHeader tableHeader = new TableHeader(
contents.getContent(headerKey), contents.descriptionLabel);
TableHeader tableHeader = getTableHeader(headerKey);
Table table = new Table(HtmlStyle.summaryTable)
.setCaption(getTableCaption(headingKey))
.setHeader(tableHeader)
.setId(id)
.setColumnStyles(HtmlStyle.colSummaryItemName, HtmlStyle.colLast);
.setColumnStyles(getColumnStyles());
addTableTabs(table, headingKey);
for (Element e : apiList) {
Content link;
switch (e.getKind()) {
case MODULE:
case MODULE -> {
ModuleElement m = (ModuleElement) e;
link = getModuleLink(m, Text.of(m.getQualifiedName()));
break;
case PACKAGE:
}
case PACKAGE -> {
PackageElement pkg = (PackageElement) e;
link = getPackageLink(pkg, getLocalizedPackageName(pkg));
break;
default:
link = getSummaryLink(e);
}
default -> link = getSummaryLink(e);
}
Content extraContent = getExtraContent(e);
Content desc = new ContentBuilder();
addComments(e, desc);
table.addRow(e, link, desc);
if (extraContent != null) {
table.addRow(e, link, extraContent, desc);
} else {
table.addRow(e, link, desc);
}
}
// note: singleton list
content.add(HtmlTree.UL(HtmlStyle.blockList, HtmlTree.LI(table)));
@ -271,6 +274,37 @@ public class SummaryListWriter<L extends SummaryAPIListBuilder> extends SubWrite
protected void addExtraIndexLink(L list, Content target) {
}
/**
* Some subclasses of this class display an extra column in their element tables.
* This methods allows them to return the content to show for {@code element}.
* @param element the element
* @return content for extra content or null
*/
protected Content getExtraContent(Element element) {
return null;
}
/**
* Gets the table header to use for a table with the first column identified by {@code headerKey}.
*
* @param headerKey the header key for the first table column
* @return the table header
*/
protected TableHeader getTableHeader(String headerKey) {
return new TableHeader(
contents.getContent(headerKey), contents.descriptionLabel);
}
/**
* Gets the array of styles to use for table columns. The length of the returned
* array must match the number of column headers returned by {@link #getTableHeader(String)}.
*
* @return the styles to use for table columns
*/
protected HtmlStyle[] getColumnStyles() {
return new HtmlStyle[]{ HtmlStyle.colSummaryItemName, HtmlStyle.colLast };
}
/**
* Returns the caption for the table with the given {@code headingKey}.
*

View File

@ -72,19 +72,22 @@ import jdk.javadoc.internal.doclets.toolkit.Content;
public class Table extends Content {
private final HtmlStyle tableStyle;
private Content caption;
private Map<Content, Predicate<Element>> tabMap;
private List<Tab> tabs;
private Set<Tab> occurringTabs;
private Content defaultTab;
private Set<Content> tabs;
private HtmlStyle tabListStyle = HtmlStyle.tableTabs;
private HtmlStyle activeTabStyle = HtmlStyle.activeTableTab;
private HtmlStyle tabStyle = HtmlStyle.tableTab;
private boolean renderTabs = true;
private TableHeader header;
private List<HtmlStyle> columnStyles;
private List<HtmlStyle> stripedStyles = Arrays.asList(HtmlStyle.evenRowColor, HtmlStyle.oddRowColor);
private HtmlStyle gridStyle;
private final List<Content> bodyRows;
private HtmlId id;
private boolean alwaysShowDefaultTab = false;
/**
* A record containing the data for a table tab.
*/
record Tab(Content label, Predicate<Element> predicate, int index) {}
/**
* Creates a builder for an HTML element representing a table.
*
@ -119,11 +122,13 @@ public class Table extends Content {
* @return this object
*/
public Table addTab(Content label, Predicate<Element> predicate) {
if (tabMap == null) {
tabMap = new LinkedHashMap<>(); // preserves order that tabs are added
tabs = new HashSet<>(); // order not significant
if (tabs == null) {
tabs = new ArrayList<>(); // preserves order that tabs are added
occurringTabs = new HashSet<>(); // order not significant
}
tabMap.put(label, predicate);
// 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));
return this;
}
@ -150,17 +155,14 @@ public class Table extends Content {
}
/**
* Sets the name of the styles used to display the tabs.
* 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.
*
* @param tabListStyle the style for the {@code <div>} element containing the tabs
* @param activeTabStyle the style for the active tab
* @param tabStyle the style for other tabs
* @return this object
* @param renderTabs true if table tabs should be rendered
* @return this object
*/
public Table setTabStyles(HtmlStyle tabListStyle, HtmlStyle activeTabStyle, HtmlStyle tabStyle) {
this.tabListStyle = tabListStyle;
this.activeTabStyle = activeTabStyle;
this.tabStyle = tabStyle;
public Table setRenderTabs(boolean renderTabs) {
this.renderTabs = renderTabs;
return this;
}
@ -180,19 +182,6 @@ public class Table extends Content {
return this;
}
/**
* Sets the styles used for {@code <tr>} tags, to give a "striped" appearance.
* The defaults are currently {@code evenRowColor} and {@code oddRowColor}.
*
* @param evenRowStyle the style to use for even-numbered rows
* @param oddRowStyle the style to use for odd-numbered rows
* @return this object
*/
public Table setStripedStyles(HtmlStyle evenRowStyle, HtmlStyle oddRowStyle) {
stripedStyles = Arrays.asList(evenRowStyle, oddRowStyle);
return this;
}
/**
* Sets the styles for be used for the cells in each row.
*
@ -224,6 +213,19 @@ public class Table extends Content {
return this;
}
/**
* Sets the style for the table's grid which controls allocation of space among table columns.
* The style should contain a {@code display: grid;} property and its number of columns must
* match the number of column styles and content passed to other methods in this class.
*
* @param gridStyle the grid style
* @return this object
*/
public Table setGridStyle(HtmlStyle gridStyle) {
this.gridStyle = gridStyle;
return this;
}
/**
* Sets the id attribute of the table.
* This is required if the table has tabs, in which case a subsidiary id
@ -299,7 +301,7 @@ public class Table extends Content {
* and {@code element} is null
*/
public void addRow(Element element, List<Content> contents) {
if (tabMap != null && element == null) {
if (tabs != null && element == null) {
throw new NullPointerException();
}
if (contents.size() != columnStyles.size()) {
@ -308,27 +310,21 @@ public class Table extends Content {
Content row = new ContentBuilder();
HtmlStyle rowStyle = null;
if (stripedStyles != null) {
int rowIndex = bodyRows.size();
rowStyle = stripedStyles.get(rowIndex % 2);
}
int rowIndex = bodyRows.size();
HtmlStyle rowStyle = rowIndex % 2 == 0 ? HtmlStyle.evenRowColor : HtmlStyle.oddRowColor;
List<String> tabClasses = new ArrayList<>();
if (tabMap != null) {
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.
// The values are used to determine the cells to make visible when a tab is selected.
tabClasses.add(id.name());
int tabIndex = 1;
for (Map.Entry<Content, Predicate<Element>> e : tabMap.entrySet()) {
Content label = e.getKey();
Predicate<Element> predicate = e.getValue();
if (predicate.test(element)) {
tabs.add(label);
tabClasses.add(HtmlIds.forTab(id, tabIndex).name());
for (Tab tab : tabs) {
if (tab.predicate().test(element)) {
occurringTabs.add(tab);
tabClasses.add(HtmlIds.forTab(id, tab.index()).name());
}
tabIndex++;
}
}
int colIndex = 0;
@ -336,9 +332,8 @@ public class Table extends Content {
HtmlStyle cellStyle = columnStyles.get(colIndex);
// Always add content to make sure the cell isn't dropped
var cell = HtmlTree.DIV(cellStyle).addUnchecked(c.isEmpty() ? Text.EMPTY : c);
if (rowStyle != null) {
cell.addStyle(rowStyle);
}
cell.addStyle(rowStyle);
for (String tabClass : tabClasses) {
cell.addStyle(tabClass);
}
@ -375,35 +370,42 @@ public class Table extends Content {
} else {
main = new ContentBuilder();
}
HtmlStyle columnStyle = switch (columnStyles.size()) {
case 2 -> HtmlStyle.twoColumnSummary;
case 3 -> HtmlStyle.threeColumnSummary;
case 4 -> HtmlStyle.fourColumnSummary;
default -> throw new IllegalStateException();
};
// If no grid style is set use on of the default styles
if (gridStyle == null) {
gridStyle = switch (columnStyles.size()) {
case 2 -> HtmlStyle.twoColumnSummary;
case 3 -> HtmlStyle.threeColumnSummary;
case 4 -> HtmlStyle.fourColumnSummary;
default -> throw new IllegalStateException();
};
}
var table = HtmlTree.DIV(tableStyle).addStyle(columnStyle);
if ((tabMap == null || tabs.size() == 1) && !alwaysShowDefaultTab) {
if (tabMap == null) {
var table = HtmlTree.DIV(tableStyle).addStyle(gridStyle);
if ((tabs == null || occurringTabs.size() == 1) && !alwaysShowDefaultTab) {
if (tabs == null) {
main.add(caption);
} else {
main.add(getCaption(tabs.iterator().next()));
main.add(getCaption(occurringTabs.iterator().next().label()));
}
table.add(getTableBody());
main.add(table);
} else {
var tablist = HtmlTree.DIV(tabListStyle)
var tablist = HtmlTree.DIV(HtmlStyle.tableTabs)
.put(HtmlAttr.ROLE, "tablist")
.put(HtmlAttr.ARIA_ORIENTATION, "horizontal");
int tabIndex = 0;
tablist.add(createTab(HtmlIds.forTab(id, tabIndex), activeTabStyle, true, defaultTab));
table.put(HtmlAttr.ARIA_LABELLEDBY, HtmlIds.forTab(id, tabIndex).name());
for (Content tabLabel : tabMap.keySet()) {
tabIndex++;
if (tabs.contains(tabLabel)) {
HtmlTree tab = createTab(HtmlIds.forTab(id, tabIndex), tabStyle, false, tabLabel);
tablist.add(tab);
HtmlId defaultTabId = HtmlIds.forTab(id, 0);
if (renderTabs) {
tablist.add(createTab(defaultTabId, HtmlStyle.activeTableTab, true, defaultTab));
} else {
tablist.add(getCaption(defaultTab));
}
table.put(HtmlAttr.ARIA_LABELLEDBY, defaultTabId.name());
if (renderTabs) {
for (Tab tab : tabs) {
if (occurringTabs.contains(tab)) {
tablist.add(createTab(HtmlIds.forTab(id, tab.index()), HtmlStyle.tableTab, false, tab.label()));
}
}
}
if (id == null) {
@ -442,40 +444,6 @@ public class Table extends Content {
return tableContent;
}
/**
* Returns whether or not the table needs JavaScript support.
* It requires such support if tabs have been added.
*
* @return true if JavaScript is required
*/
public boolean needsScript() {
return (tabs != null) && (tabs.size() > 1);
}
/**
* Returns the script to be used in conjunction with the table.
*
* @return the script
*/
public String getScript() {
if (tabMap == null)
throw new IllegalStateException();
StringBuilder sb = new StringBuilder();
// Add the variables defining the stylenames
appendStyleInfo(sb,
stripedStyles.get(0), stripedStyles.get(1), tabStyle, activeTabStyle);
return sb.toString();
}
private void appendStyleInfo(StringBuilder sb, HtmlStyle... styles) {
for (HtmlStyle style : styles) {
sb.append("var ").append(style.name()).append(" = \"").append(style.cssName()).append("\";\n");
}
}
private HtmlTree getCaption(Content title) {
return HtmlTree.DIV(HtmlStyle.caption, HtmlTree.SPAN(title));
}

View File

@ -29,9 +29,9 @@ import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
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.HtmlStyle;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
import jdk.javadoc.internal.doclets.toolkit.Content;
@ -55,6 +55,8 @@ public class TableHeader extends Content {
*/
private List<HtmlStyle> styles;
private boolean[] sortable;
/**
* Creates a header row, with localized content for each cell.
* Resources keys will be converted to content using {@link Contents#getContent(String)}.
@ -97,6 +99,20 @@ public class TableHeader extends Content {
return this;
}
/**
* Makes the table sortable by the content of columns for which the
* argument boolean array contains {@code true}.
* @param sortable boolean array specifying sortable columns
* @return this object
*/
public TableHeader sortable(boolean... sortable) {
if (sortable.length != cellContents.size()) {
throw new IllegalStateException();
}
this.sortable = sortable;
return this;
}
/**
* Set the style class names for each header cell.
* The number of names must match the number of cells given to the constructor.
@ -144,6 +160,13 @@ public class TableHeader extends Content {
if (style != null) {
cell.addStyle(style);
}
if (sortable != null && sortable[i]) {
cell.put(HtmlAttr.ONCLICK, "sortTable(this, " + i + ", " + sortable.length +")");
// Current tables happen to be sorted by first column by default, this may not hold true for future uses.
if (i == 0) {
cell.addStyle("sort-asc");
}
}
header.add(cell);
i++;
}

View File

@ -38,6 +38,7 @@ public enum HtmlAttr {
ARIA_LABELLEDBY("aria-labelledby"),
ARIA_ORIENTATION("aria-orientation"),
ARIA_SELECTED("aria-selected"),
CHECKED,
CLASS,
CLEAR,
COLS,

View File

@ -400,6 +400,13 @@ public enum HtmlStyle {
*/
threeColumnSummary,
/**
* The class of a {@code div} element whose content should be rendered as a table
* with three columns where the middle column requires less space as it only contains
* a release name.
*/
threeColumnReleaseSummary,
/**
* The class of a {@code div} element whose content should be rendered as a table
* with four columns.
@ -430,6 +437,12 @@ public enum HtmlStyle {
*/
caption,
/**
* The class for a {@code div} element containing a row of checkboxes to select
* items to view in summary tables.
*/
checkboxes,
/**
* The class of an element that is part of a table header.
*/

View File

@ -108,21 +108,15 @@ doclet.tag.invalid_usage=invalid usage of tag {0}
doclet.tag.invalid_input=invalid input: ''{0}''
doclet.tag.invalid=invalid @{0}
doclet.Deprecated_API=Deprecated API
doclet.Deprecated_API_Checkbox_Label=Show API deprecated in:
doclet.Deprecated_Elements=Deprecated {0}
doclet.Deprecated_Elements_Release_Column_Header=Deprecated in
doclet.Deprecated_In_Release=Deprecated in {0}
doclet.Deprecated_Tabs_Intro=(The leftmost tab "Deprecated ..." indicates all the \
deprecated elements, regardless of the releases in which they were deprecated. \
Each of the other tabs "Deprecated in ..." indicates the elements deprecated \
in a specific release.)
doclet.New_API=New API
doclet.New_API_Checkbox_Label=Show API added in:
doclet.New_Elements=New {0}
doclet.New_Elements_Added_In_Release=Added in {0}
doclet.New_Elements_Release_Column_Header=Added in
doclet.New_Label=New
doclet.New_Tabs_Intro=(The leftmost tab "New ..." indicates all the new elements, \
regardless of the releases in which they were added. Each of the other tabs \
"Added in ..." indicates the new elements added in a specific release. \
Any element shown under the leftmost tab is also shown under one of the \
righthand tabs.)
doclet.Preview_API=Preview API
doclet.Preview_Label=Preview
doclet.Preview_Mark=PREVIEW

View File

@ -28,6 +28,14 @@ var packageSearchIndex;
var typeSearchIndex;
var memberSearchIndex;
var tagSearchIndex;
var oddRowColor = "odd-row-color";
var evenRowColor = "even-row-color";
var sortAsc = "sort-asc";
var sortDesc = "sort-desc";
var tableTab = "table-tab";
var activeTableTab = "active-table-tab";
function loadScripts(doc, tag) {
createElem(doc, tag, 'search.js');
@ -45,6 +53,88 @@ function createElem(doc, tag, path) {
scriptElement.parentNode.insertBefore(script, scriptElement);
}
// Helper for making content containing release names comparable lexicographically
function makeComparable(s) {
return s.toLowerCase().replace(/(\d+)/g,
function(n, m) {
return ("000" + m).slice(-4);
});
}
// Switches between two styles depending on a condition
function toggleStyle(classList, condition, trueStyle, falseStyle) {
if (condition) {
classList.remove(falseStyle);
classList.add(trueStyle);
} else {
classList.remove(trueStyle);
classList.add(falseStyle);
}
}
// Sorts the rows in a table lexicographically by the content of a specific column
function sortTable(header, columnIndex, columns) {
var container = header.parentElement;
var descending = header.classList.contains(sortAsc);
container.querySelectorAll("div.table-header").forEach(
function(header) {
header.classList.remove(sortAsc);
header.classList.remove(sortDesc);
}
)
var cells = container.children;
var rows = [];
for (var i = columns; i < cells.length; i += columns) {
rows.push(Array.prototype.slice.call(cells, i, i + columns));
}
var comparator = function(a, b) {
var ka = makeComparable(a[columnIndex].textContent);
var kb = makeComparable(b[columnIndex].textContent);
if (ka < kb)
return -1;
if (ka > kb)
return 1;
return 0;
};
var sorted = rows.sort(comparator);
if (descending) {
sorted = sorted.reverse();
}
var visible = 0;
sorted.forEach(function(row) {
if (row[0].style.display === '') {
var isEvenRow = visible++ % 2 === 0;
}
row.forEach(function(cell) {
toggleStyle(cell.classList, isEvenRow, evenRowColor, oddRowColor);
container.appendChild(cell);
})
});
toggleStyle(header.classList, descending, sortDesc, sortAsc);
}
// Toggles the visibility of a table category in all tables in a page
function toggleGlobal(checkbox, selected, columns) {
var display = checkbox.checked ? '' : 'none';
document.querySelectorAll("div.table-tabs").forEach(function(t) {
var id = t.parentElement.getAttribute("id");
selectedClass = id + "-tab" + selected;
var visible = 0;
document.querySelectorAll('div.' + id)
.forEach(function(elem) {
if (elem.classList.contains(selectedClass)) {
elem.style.display = display;
}
if (elem.style.display === '') {
var isEvenRow = visible++ % (columns * 2) < columns;
toggleStyle(elem.classList, isEvenRow, evenRowColor, oddRowColor);
}
});
t.parentElement.style.display = visible === 0 ? 'none' : '';
})
}
// Shows the elements of a table belonging to a specific category
function show(tableId, selected, columns) {
if (tableId !== selected) {
document.querySelectorAll('div.' + tableId + ':not(.' + selected + ')')
@ -56,8 +146,7 @@ function show(tableId, selected, columns) {
.forEach(function(elem, index) {
elem.style.display = '';
var isEvenRow = index % (columns * 2) < columns;
elem.classList.remove(isEvenRow ? oddRowColor : evenRowColor);
elem.classList.add(isEvenRow ? evenRowColor : oddRowColor);
toggleStyle(elem.classList, isEvenRow, evenRowColor, oddRowColor);
});
updateTabs(tableId, selected);
}
@ -171,4 +260,9 @@ document.addEventListener("DOMContentLoaded", function(e) {
if (!location.hash) {
history.replaceState(contentDiv.scrollTop, document.title);
}
document.querySelectorAll('input[type="checkbox"]').forEach(
function(c, i) {
c.disabled = false;
toggleGlobal(c, String(i + 1), 3)
});
});

View File

@ -356,14 +356,12 @@ ul.see-list-long li:not(:last-child):after {
text-align:left;
background-repeat:no-repeat;
color:#253441;
font-weight:bold;
clear:none;
overflow:hidden;
padding:0;
padding-top:10px;
padding-left:1px;
margin:0;
white-space:pre;
}
.caption a:link, .caption a:visited {
color:#1f389c;
@ -373,6 +371,7 @@ ul.see-list-long li:not(:last-child):after {
color:#FFFFFF;
}
.caption span {
font-weight:bold;
white-space:nowrap;
padding:5px 12px 7px 12px;
display:inline-block;
@ -385,14 +384,20 @@ div.table-tabs {
padding:10px 0 0 1px;
margin:10px 0 0 0;
}
div.table-tabs > span {
background-color: #EEE;
color: #000;
border: none;
padding: 5px 12px 8px 12px;
}
div.table-tabs > button {
border: none;
cursor: pointer;
padding: 5px 12px 7px 12px;
font-weight: bold;
margin-right: 3px;
margin-right: 8px;
}
div.table-tabs > button.active-table-tab {
div.table-tabs > .active-table-tab {
background: #F8981D;
color: #253441;
}
@ -404,6 +409,18 @@ div.table-tabs > button.table-tab {
display: grid;
grid-template-columns: minmax(400px, max-content) minmax(400px, auto);
}
div.checkboxes {
line-height: 18px;
}
div.checkboxes > span {
margin-left: 10px;
}
div.checkboxes > label {
margin-left: 8px;
}
div.checkboxes > label > input {
margin: 0 2px;
}
.two-column-summary {
display: grid;
grid-template-columns: minmax(25%, max-content) minmax(25%, auto);
@ -435,6 +452,10 @@ div.table-tabs > button.table-tab {
grid-column-end: span 2;
}
}
.three-column-release-summary {
display: grid;
grid-template-columns: minmax(40%, max-content) minmax(10%, max-content) minmax(40%, auto);
}
@media screen and (max-width: 600px) {
.two-column-summary {
display: grid;
@ -455,6 +476,35 @@ div.table-tabs > button.table-tab {
background:#dee3e9;
font-weight: bold;
}
/* Sortable table columns */
.table-header[onclick] {
cursor: pointer;
}
.table-header[onclick]::after {
content:"";
display:inline-block;
background-image:url('data:image/svg+xml; utf8, \
<svg xmlns="http://www.w3.org/2000/svg" width="125" height="170"> \
<path d="M10.101 57.059L63.019 4.142l52.917 52.917M10.101 86.392l52.917 52.917 52.917-52.917" style="opacity:.35;"/></svg>');
background-size:100% 100%;
width:9px;
height:14px;
margin-left:4px;
margin-bottom:-3px;
}
.table-header[onclick].sort-asc::after {
background-image:url('data:image/svg+xml; utf8, \
<svg xmlns="http://www.w3.org/2000/svg" width="125" height="170"> \
<path d="M10.101 57.059L63.019 4.142l52.917 52.917" style="opacity:.75;"/> \
<path d="M10.101 86.392l52.917 52.917 52.917-52.917" style="opacity:.35;"/></svg>');
}
.table-header[onclick].sort-desc::after {
background-image:url('data:image/svg+xml; utf8, \
<svg xmlns="http://www.w3.org/2000/svg" width="125" height="170"> \
<path d="M10.101 57.059L63.019 4.142l52.917 52.917" style="opacity:.35;"/> \
<path d="M10.101 86.392l52.917 52.917 52.917-52.917" style="opacity:.75;"/></svg>');
}
.col-first, .col-first {
font-size:13px;
}
@ -717,10 +767,6 @@ button.page-search-header {
span#page-search-link {
text-decoration: underline;
}
.active-table-tab {
background: #F8981D;
color: #253441;
}
.module-graph span {
display:none;
position:absolute;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, 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
@ -202,7 +202,7 @@ public class CheckStylesheetClasses {
throw new AssertionError("Cannot find or access resource " + resource);
}
String s = new String(in.readAllBytes());
Pattern p = Pattern.compile("(?i)(\\s|([a-z][a-z0-9-]*))\\.(?<name>[a-z0-9-]+)\\b");
Pattern p = Pattern.compile("(?i)(\\s|([a-z][a-z0-9-]*))\\.(?<name>[a-z][a-z0-9-]+)\\b");
Matcher m = p.matcher(s);
while (m.find()) {
names.add(m.group("name"));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2022, 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
@ -1184,12 +1184,7 @@ public class TestModules extends JavadocTester {
her Modules</button>\
</div>
<div id="all-modules-table.tabpanel" role="tabpanel">
<div class="summary-table two-column-summary" aria-labelledby="all-modules-table-tab0">""",
"""
var evenRowColor = "even-row-color";
var oddRowColor = "odd-row-color";
var tableTab = "table-tab";
var activeTableTab = "active-table-tab";""");
<div class="summary-table two-column-summary" aria-labelledby="all-modules-table-tab0">""");
checkOutput("index.html", false,
"""
<div class="overview-summary">
@ -1270,12 +1265,7 @@ public class TestModules extends JavadocTester {
ackage Group 1</button>\
</div>
<div id="all-packages-table.tabpanel" role="tabpanel">
<div class="summary-table two-column-summary" aria-labelledby="all-packages-table-tab0">""",
"""
var evenRowColor = "even-row-color";
var oddRowColor = "odd-row-color";
var tableTab = "table-tab";
var activeTableTab = "active-table-tab";""");
<div class="summary-table two-column-summary" aria-labelledby="all-packages-table-tab0">""");
}
void checkGroupOptionPackageOrdering() {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2022, 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
@ -95,17 +95,16 @@ public class TestStylesheet extends JavadocTester {
text-align:left;
background-repeat:no-repeat;
color:#253441;
font-weight:bold;
clear:none;
overflow:hidden;
padding:0;
padding-top:10px;
padding-left:1px;
margin:0;
white-space:pre;
}""",
"""
.caption span {
font-weight:bold;
white-space:nowrap;
padding:5px 12px 7px 12px;
display:inline-block;
@ -120,9 +119,9 @@ public class TestStylesheet extends JavadocTester {
cursor: pointer;
padding: 5px 12px 7px 12px;
font-weight: bold;
margin-right: 3px;
margin-right: 8px;
}
div.table-tabs > button.active-table-tab {
div.table-tabs > .active-table-tab {
background: #F8981D;
color: #253441;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2022, 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
@ -1086,7 +1086,7 @@ public abstract class JavadocTester {
name = null;
content = null;
} else {
name = file;
name = outputDir + "/" + file;
content = c;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, 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
@ -202,13 +202,13 @@ public class TestJavadocTester extends JavadocTester {
messages.forEach(this::report);
checkMessages(
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
Second sentence""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
abc123""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
def456""");
}
@ -220,7 +220,7 @@ public class TestJavadocTester extends JavadocTester {
.check("Third sentence.");
checkMessages(
"""
Passed: p/C.html: following text not found:
Passed: out/p/C.html: following text not found:
Third sentence""");
}
@ -231,7 +231,7 @@ public class TestJavadocTester extends JavadocTester {
.check("Third sentence.");
checkMessages(
"""
FAILED: p/C.html: following text not found:
FAILED: out/p/C.html: following text not found:
Third sentence""");
}
@ -244,13 +244,13 @@ public class TestJavadocTester extends JavadocTester {
Pattern.compile("d.f4.6"));
checkMessages(
"""
Passed: p/C.html: following pattern found:
Passed: out/p/C.html: following pattern found:
S.cond s.nt.nc.""",
"""
Passed: p/C.html: following pattern found:
Passed: out/p/C.html: following pattern found:
[abc]{3}[123]{3}""",
"""
Passed: p/C.html: following pattern found:
Passed: out/p/C.html: following pattern found:
d.f4.6""");
}
@ -271,28 +271,28 @@ public class TestJavadocTester extends JavadocTester {
checkMessages(
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
<h2>Method Summary</h2>""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
<a href="#m1()" class="member-name-link">m1</a>""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
<a href="#m2()" class="member-name-link">m2</a>""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
<a href="#m3()" class="member-name-link">m3</a>""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
<h2>Method Details</h2>""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
<section class="detail" id="m3()">""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
<section class="detail" id="m2()">""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
<section class="detail" id="m1()">"""
);
}
@ -306,10 +306,10 @@ public class TestJavadocTester extends JavadocTester {
"First sentence");
checkMessages(
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
Second sentence""",
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
First sentence""");
}
@ -321,10 +321,10 @@ public class TestJavadocTester extends JavadocTester {
"First sentence");
checkMessages(
"""
Passed: p/C.html: following text found:
Passed: out/p/C.html: following text found:
Second sentence""",
"""
FAILED: p/C.html: following text was found on line""");
FAILED: out/p/C.html: following text was found on line""");
}
@Test
@ -408,12 +408,12 @@ public class TestJavadocTester extends JavadocTester {
.setExpectOrdered(false)
.checkUnique("id=\"m1()\"", "id=\"m2()\"", "id=\"m3()\"") // expect unique
.checkUnique("m1()", "m2()", "m3()"); // expect not unique
checkMessages("Passed: p/C.html: id=\"m1()\" is unique",
"Passed: p/C.html: id=\"m2()\" is unique",
"Passed: p/C.html: id=\"m3()\" is unique",
"FAILED: p/C.html: m1() is not unique",
"FAILED: p/C.html: m2() is not unique",
"FAILED: p/C.html: m3() is not unique");
checkMessages("Passed: out/p/C.html: id=\"m1()\" is unique",
"Passed: out/p/C.html: id=\"m2()\" is unique",
"Passed: out/p/C.html: id=\"m3()\" is unique",
"FAILED: out/p/C.html: m1() is not unique",
"FAILED: out/p/C.html: m2() is not unique",
"FAILED: out/p/C.html: m3() is not unique");
}
/**