diff --git a/make/Docs.gmk b/make/Docs.gmk
index c1c9f7330ba..0cc5309813f 100644
--- a/make/Docs.gmk
+++ b/make/Docs.gmk
@@ -1,4 +1,4 @@
-# Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 1997, 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
@@ -80,6 +80,7 @@ JAVADOC_TAGS := \
-taglet build.tools.taglet.JSpec\$$JLS \
-taglet build.tools.taglet.JSpec\$$JVMS \
-taglet build.tools.taglet.ModuleGraph \
+ -taglet build.tools.taglet.SealedGraph \
-taglet build.tools.taglet.ToolGuide \
-tag since \
-tag serialData \
@@ -187,25 +188,55 @@ JAVASE_LONG_NAME := Java® Platform, Standard Edition
# Functions
# Helper function for creating a svg file from a dot file generated by the
-# GenGraphs tool.
+# GenGraphs tool for a module.
# param 1: SetupJavadocGeneration namespace ($1)
# param 2: module name
#
-define setup_gengraph_dot_to_svg
- $1_$2_DOT_SRC := $$($1_GENGRAPHS_DIR)/$2.dot
+define setup_module_graph_dot_to_svg
+ $1_$2_DOT_SRC := $$($1_MODULE_GRAPHS_DIR)/$2.dot
$1_$2_SVG_TARGET := $$($1_TARGET_DIR)/$2/module-graph.svg
# For each module needing a graph, create a svg file from the dot file
# generated by the GenGraphs tool and store it in the target dir.
- $$(eval $$(call SetupExecute, gengraphs_svg_$1_$2, \
+ $$(eval $$(call SetupExecute, module_graphs_svg_$1_$2, \
INFO := Running dot for module graphs for $2, \
- DEPS := $$(gengraphs_$1_TARGET), \
+ DEPS := $$(module_graphs_dot_$1_TARGET), \
OUTPUT_FILE := $$($1_$2_SVG_TARGET), \
- SUPPORT_DIR := $$($1_GENGRAPHS_DIR), \
+ SUPPORT_DIR := $$($1_MODULE_GRAPHS_DIR), \
COMMAND := $$(DOT) -Tsvg -o $$($1_$2_SVG_TARGET) $$($1_$2_DOT_SRC), \
))
- $1_MODULEGRAPH_TARGETS += $$($1_$2_SVG_TARGET)
+ $1_GRAPHS_TARGETS += $$($1_$2_SVG_TARGET)
+endef
+
+# Helper function for creating a svg file for a class for which the SealedGraph
+# taglet has generated a dot file. The dot file has a special name which
+# encodes the module and class the graph belongs to.
+#
+# param 1: SetupJavadocGeneration namespace ($1)
+# param 2: dot file name
+#
+define setup_sealed_graph_dot_to_svg
+ $1_$2_DOT_SRC := $$($1_SEALED_GRAPHS_DIR)/$2.dot
+ $1_$2_TARGET_CLASS := $$(word 2, $$(subst _, , $2))
+ $1_$2_SLASHED_NAME := $$(subst .,/, $$($1_$2_TARGET_CLASS))
+ $1_$2_TARGET_MODULE := $$(word 1, $$(subst _, , $2))
+ $1_$2_TARGET_PATH := $$($1_TARGET_DIR)/$$($1_$2_TARGET_MODULE)/$$(dir $$($1_$2_SLASHED_NAME))
+ $1_$2_TARGET_NAME := $$(notdir $$($1_$2_SLASHED_NAME))
+ $1_$2_SVG_TARGET := $$($1_$2_TARGET_PATH)/$$($1_$2_TARGET_NAME)-sealed-graph.svg
+ $$(call MakeDir, $$($1_$2_TARGET_PATH))
+
+ # For each class needing a graph, create a svg file from the dot file
+ # generated by the SealedGraph taglet and store it in the target dir.
+ $$(eval $$(call SetupExecute, sealed_graphs_svg_$1_$2, \
+ INFO := Running dot for sealed graphs for $$($1_$2_TARGET_MODULE)/$$($1_$2_TARGET_CLASS), \
+ DEPS := $$($1_$2_DOT_SRC), \
+ OUTPUT_FILE := $$($1_$2_SVG_TARGET), \
+ SUPPORT_DIR := $$($1_SEALED_GRAPHS_DIR), \
+ COMMAND := $$(DOT) -Tsvg -o $$($1_$2_SVG_TARGET) $$($1_$2_DOT_SRC), \
+ ))
+
+ $1_GRAPHS_TARGETS += $$($1_$2_SVG_TARGET)
endef
# Helper function to create the overview.html file to use with the -overview
@@ -253,7 +284,7 @@ endef
#
# Parameter 1 is the name of the rule. This name is used as variable prefix.
# Targets generated are returned as $1_JAVADOC_TARGETS and
-# $1_MODULEGRAPH_TARGETS. Note that the index.html file will work as a "touch
+# $1_GRAPHS_TARGETS. Note that the index.html file will work as a "touch
# file" for all the magnitude of files that are generated by javadoc.
#
# Remaining parameters are named arguments. These include:
@@ -276,9 +307,12 @@ define SetupApiDocsGenerationBody
-Djspec.version=$$(VERSION_SPECIFICATION)
ifeq ($$(ENABLE_FULL_DOCS), true)
- # Tell the ModuleGraph taglet to generate html links to soon-to-be-created
- # svg files with module graphs.
- $1_JAVA_ARGS += -DenableModuleGraph=true
+ $1_SEALED_GRAPHS_DIR := $$(SUPPORT_OUTPUTDIR)/docs/$1-sealed-graphs
+
+ # Tell the ModuleGraph and SealedGraph taglets to generate html links to
+ # soon-to-be-created svg files with module/sealed graphs.
+ $1_JAVA_ARGS += -DenableModuleGraph=true -DsealedDotOutputDir=$$($1_SEALED_GRAPHS_DIR)
+ $$(call MakeDir, $$($1_SEALED_GRAPHS_DIR))
endif
# Start with basic options and tags
@@ -384,30 +418,46 @@ define SetupApiDocsGenerationBody
# First we run the GenGraph tool. It will query the module structure of the
# running JVM and output .dot files for all existing modules.
- GENGRAPHS_PROPS := \
+ MODULE_GRAPHS_PROPS := \
$$(TOPDIR)/make/jdk/src/classes/build/tools/jigsaw/javadoc-graphs.properties
- $1_GENGRAPHS_DIR := $$(SUPPORT_OUTPUTDIR)/docs/$1-gengraphs
+ $1_MODULE_GRAPHS_DIR := $$(SUPPORT_OUTPUTDIR)/docs/$1-module-graphs
- $$(eval $$(call SetupExecute, gengraphs_$1, \
- INFO := Running gengraphs for $1 documentation, \
- DEPS := $$(BUILD_JIGSAW_TOOLS) $$(GENGRAPHS_PROPS), \
- OUTPUT_DIR := $$($1_GENGRAPHS_DIR), \
- COMMAND := $$(TOOL_GENGRAPHS) --spec --output $$($1_GENGRAPHS_DIR) \
- --dot-attributes $$(GENGRAPHS_PROPS), \
+ $$(eval $$(call SetupExecute, module_graphs_dot_$1, \
+ INFO := Generating module graphs for $1 documentation, \
+ DEPS := $$(BUILD_JIGSAW_TOOLS) $$(MODULE_GRAPHS_PROPS), \
+ OUTPUT_DIR := $$($1_MODULE_GRAPHS_DIR), \
+ COMMAND := $$(TOOL_GENGRAPHS) --spec --output $$($1_MODULE_GRAPHS_DIR) \
+ --dot-attributes $$(MODULE_GRAPHS_PROPS), \
))
# For each module needing a graph, create a svg file from the dot file
# generated by the GenGraphs tool and store it in the target dir.
- # They will depend on gengraphs_$1_TARGET, and will be added to $1.
+ # They will depend on module_graphs_dot_$1_TARGET, and will be added to
+ # $1_GRAPHS_TARGETS.
$$(foreach m, $$($1_MODULES_NEEDING_GRAPH), \
- $$(eval $$(call setup_gengraph_dot_to_svg,$1,$$m)) \
+ $$(eval $$(call setup_module_graph_dot_to_svg,$1,$$m)) \
+ )
+
+ # We have asked SealedGraph to generate dot files and links to svg files.
+ # Now we must produce the svg files from the dot files.
+
+ # Get a list of classes for which SealedGraph has generated dot files
+ $1_SEALED_CLASSES := $$(patsubst %.dot,%,$$(patsubst \
+ $$($1_SEALED_GRAPHS_DIR)/%,%, \
+ $$(wildcard $$($1_SEALED_GRAPHS_DIR)/*.dot)))
+
+ # For each class needing a graph, create a svg file from the dot file
+ # generated by the SealedGraph taglet and store it in the target dir.
+ # They will will be added to $1_GRAPHS_TARGETS.
+ $$(foreach c, $$($1_SEALED_CLASSES), \
+ $$(eval $$(call setup_sealed_graph_dot_to_svg,$1,$$c)) \
)
endif
endef
################################################################################
-# Setup generation of the JDK API documentation (javadoc + modulegraph)
+# Setup generation of the JDK API documentation (javadoc + graphs)
# Define the groups of the JDK API documentation
JavaSE_GROUP_NAME := Java SE
@@ -456,10 +506,10 @@ $(eval $(call SetupApiDocsGeneration, JDK_API, \
))
# Targets generated are returned in JDK_API_JAVADOC_TARGETS and
-# JDK_API_MODULEGRAPH_TARGETS.
+# JDK_API_GRAPHS_TARGETS.
################################################################################
-# Setup generation of the Java SE API documentation (javadoc + modulegraph)
+# Setup generation of the Java SE API documentation (javadoc + graphs)
# The Java SE module scope is just java.se and its transitive indirect
# exports.
@@ -473,10 +523,10 @@ $(eval $(call SetupApiDocsGeneration, JAVASE_API, \
))
# Targets generated are returned in JAVASE_API_JAVADOC_TARGETS and
-# JAVASE_API_MODULEGRAPH_TARGETS.
+# JAVASE_API_GRAPHS_TARGETS.
################################################################################
-# Setup generation of the reference Java SE API documentation (javadoc + modulegraph)
+# Setup generation of the reference Java SE API documentation (javadoc + graphs)
# The reference javadoc is just the same as javase, but using the BootJDK javadoc
# and a stable set of javadoc options. Typically it is used for generating
@@ -494,7 +544,7 @@ $(eval $(call SetupApiDocsGeneration, REFERENCE_API, \
))
# Targets generated are returned in REFERENCE_API_JAVADOC_TARGETS and
-# REFERENCE_API_MODULEGRAPH_TARGETS.
+# REFERENCE_API_GRAPHS_TARGETS.
################################################################################
@@ -711,7 +761,7 @@ JAVADOC_ZIP_FILE := $(OUTPUTDIR)/bundles/$(JAVADOC_ZIP_NAME)
$(eval $(call SetupZipArchive, BUILD_JAVADOC_ZIP, \
SRC := $(DOCS_OUTPUTDIR), \
ZIP := $(JAVADOC_ZIP_FILE), \
- EXTRA_DEPS := $(JDK_API_JAVADOC_TARGETS) $(JDK_API_MODULEGRAPH_TARGETS) \
+ EXTRA_DEPS := $(JDK_API_JAVADOC_TARGETS) $(JDK_API_GRAPHS_TARGETS) \
$(JDK_SPECS_TARGETS), \
))
@@ -739,15 +789,15 @@ SPECS_ZIP_TARGETS += $(BUILD_SPECS_ZIP)
docs-jdk-api-javadoc: $(JDK_API_JAVADOC_TARGETS) $(JDK_API_CUSTOM_TARGETS)
-docs-jdk-api-modulegraph: $(JDK_API_MODULEGRAPH_TARGETS)
+docs-jdk-api-graphs: $(JDK_API_GRAPHS_TARGETS)
docs-javase-api-javadoc: $(JAVASE_API_JAVADOC_TARGETS) $(JAVASE_API_CUSTOM_TARGETS)
-docs-javase-api-modulegraph: $(JAVASE_API_MODULEGRAPH_TARGETS)
+docs-javase-api-graphs: $(JAVASE_API_GRAPHS_TARGETS)
docs-reference-api-javadoc: $(REFERENCE_API_JAVADOC_TARGETS) $(REFERENCE_API_CUSTOM_TARGETS)
-docs-reference-api-modulegraph: $(REFERENCE_API_MODULEGRAPH_TARGETS)
+docs-reference-api-graphs: $(REFERENCE_API_GRAPHS_TARGETS)
docs-jdk-specs: $(JDK_SPECS_TARGETS)
@@ -757,12 +807,12 @@ docs-zip: $(ZIP_TARGETS)
docs-specs-zip: $(SPECS_ZIP_TARGETS)
-all: docs-jdk-api-javadoc docs-jdk-api-modulegraph docs-javase-api-javadoc \
- docs-javase-api-modulegraph docs-reference-api-javadoc \
- docs-reference-api-modulegraph docs-jdk-specs docs-jdk-index docs-zip \
+all: docs-jdk-api-javadoc docs-jdk-api-graphs docs-javase-api-javadoc \
+ docs-javase-api-graphs docs-reference-api-javadoc \
+ docs-reference-api-graphs docs-jdk-specs docs-jdk-index docs-zip \
docs-specs-zip
-.PHONY: default all docs-jdk-api-javadoc docs-jdk-api-modulegraph \
- docs-javase-api-javadoc docs-javase-api-modulegraph \
- docs-reference-api-javadoc docs-reference-api-modulegraph docs-jdk-specs \
+.PHONY: default all docs-jdk-api-javadoc docs-jdk-api-graphs \
+ docs-javase-api-javadoc docs-javase-api-graphs \
+ docs-reference-api-javadoc docs-reference-api-graphs docs-jdk-specs \
docs-jdk-index docs-zip docs-specs-zip
diff --git a/make/Main.gmk b/make/Main.gmk
index 45bed0d0457..fec5d268301 100644
--- a/make/Main.gmk
+++ b/make/Main.gmk
@@ -466,15 +466,15 @@ ALL_TARGETS += bootcycle-images
# Docs targets
# If building full docs, to complete docs-*-api we need both the javadoc and
-# modulegraph targets.
+# graphs targets.
$(eval $(call SetupTarget, docs-jdk-api-javadoc, \
MAKEFILE := Docs, \
TARGET := docs-jdk-api-javadoc, \
))
-$(eval $(call SetupTarget, docs-jdk-api-modulegraph, \
+$(eval $(call SetupTarget, docs-jdk-api-graphs, \
MAKEFILE := Docs, \
- TARGET := docs-jdk-api-modulegraph, \
+ TARGET := docs-jdk-api-graphs, \
DEPS := buildtools-modules runnable-buildjdk, \
))
@@ -483,9 +483,9 @@ $(eval $(call SetupTarget, docs-javase-api-javadoc, \
TARGET := docs-javase-api-javadoc, \
))
-$(eval $(call SetupTarget, docs-javase-api-modulegraph, \
+$(eval $(call SetupTarget, docs-javase-api-graphs, \
MAKEFILE := Docs, \
- TARGET := docs-javase-api-modulegraph, \
+ TARGET := docs-javase-api-graphs, \
DEPS := buildtools-modules runnable-buildjdk, \
))
@@ -494,9 +494,9 @@ $(eval $(call SetupTarget, docs-reference-api-javadoc, \
TARGET := docs-reference-api-javadoc, \
))
-$(eval $(call SetupTarget, docs-reference-api-modulegraph, \
+$(eval $(call SetupTarget, docs-reference-api-graphs, \
MAKEFILE := Docs, \
- TARGET := docs-reference-api-modulegraph, \
+ TARGET := docs-reference-api-graphs, \
DEPS := buildtools-modules runnable-buildjdk, \
))
@@ -1107,9 +1107,14 @@ docs-reference-api: docs-reference-api-javadoc
# If we're building full docs, we must also generate the module graphs to
# get non-broken api documentation.
ifeq ($(ENABLE_FULL_DOCS), true)
- docs-jdk-api: docs-jdk-api-modulegraph
- docs-javase-api: docs-javase-api-modulegraph
- docs-reference-api: docs-reference-api-modulegraph
+ docs-jdk-api: docs-jdk-api-graphs
+ docs-javase-api: docs-javase-api-graphs
+ docs-reference-api: docs-reference-api-graphs
+
+ # We must generate javadoc first so we know what graphs are needed
+ docs-jdk-api-graphs: docs-jdk-api-javadoc
+ docs-javase-api-graphs: docs-javase-api-javadoc
+ docs-reference-api-graphs: docs-reference-api-javadoc
endif
docs-jdk: docs-jdk-api docs-jdk-specs docs-jdk-index
diff --git a/make/jdk/src/classes/build/tools/taglet/SealedGraph.java b/make/jdk/src/classes/build/tools/taglet/SealedGraph.java
new file mode 100644
index 00000000000..da79c17b387
--- /dev/null
+++ b/make/jdk/src/classes/build/tools/taglet/SealedGraph.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2017, 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
+ * 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 build.tools.taglet;
+
+import com.sun.source.doctree.DocTree;
+import jdk.javadoc.doclet.Doclet;
+import jdk.javadoc.doclet.DocletEnvironment;
+import jdk.javadoc.doclet.Taglet;
+
+import javax.lang.model.element.*;
+import javax.lang.model.type.DeclaredType;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.lang.System.lineSeparator;
+import static java.nio.file.StandardOpenOption.*;
+import static jdk.javadoc.doclet.Taglet.Location.TYPE;
+
+/**
+ * A block tag to optionally insert a reference to a sealed class hierarchy graph,
+ * and generate the corresponding dot file.
+ */
+public final class SealedGraph implements Taglet {
+ private static final String sealedDotOutputDir =
+ System.getProperty("sealedDotOutputDir");
+
+ private DocletEnvironment docletEnvironment;
+
+ /**
+ * Returns the set of locations in which a taglet may be used.
+ */
+ @Override
+ public Set getAllowedLocations() {
+ return EnumSet.of(TYPE);
+ }
+
+ @Override
+ public boolean isInlineTag() {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "sealedGraph";
+ }
+
+ @Override
+ public void init(DocletEnvironment env, Doclet doclet) {
+ docletEnvironment = env;
+ }
+
+ @Override
+ public String toString(List extends DocTree> tags, Element element) {
+ if (sealedDotOutputDir == null || sealedDotOutputDir.isEmpty()) {
+ return "";
+ }
+ if (docletEnvironment == null || !(element instanceof TypeElement typeElement)) {
+ return "";
+ }
+
+ ModuleElement module = docletEnvironment.getElementUtils().getModuleOf(element);
+ Path dotFile = Path.of(sealedDotOutputDir,
+ module.getQualifiedName() + "_" + typeElement.getQualifiedName() + ".dot");
+
+ Set exports = module.getDirectives().stream()
+ .filter(ModuleElement.ExportsDirective.class::isInstance)
+ .map(ModuleElement.ExportsDirective.class::cast)
+ // Only include packages that are globally exported (i.e. no "to" exports)
+ .filter(ed -> ed.getTargetModules() == null)
+ .map(ModuleElement.ExportsDirective::getPackage)
+ .map(PackageElement::getQualifiedName)
+ .map(Objects::toString)
+ .collect(Collectors.toUnmodifiableSet());
+
+ String dotContent = Renderer.graph(typeElement, exports);
+
+ try {
+ Files.writeString(dotFile, dotContent, WRITE, CREATE, TRUNCATE_EXISTING);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ String simpleTypeName = element.getSimpleName().toString();
+ String imageFile = simpleTypeName + "-sealed-graph.svg";
+ int thumbnailHeight = 100; // also appears in the stylesheet
+ String hoverImage = ""
+ + getImage(simpleTypeName, imageFile, -1, true)
+ + "";
+
+ return "Sealed Class Hierarchy Graph:"
+ + ""
+ + ""
+ + getImage(simpleTypeName, imageFile, thumbnailHeight, false)
+ + hoverImage
+ + ""
+ + "";
+ }
+
+ private static final String VERTICAL_ALIGN = "vertical-align:top";
+ private static final String BORDER = "border: solid lightgray 1px;";
+
+ private String getImage(String typeName, String file, int height, boolean useBorder) {
+ return String.format("",
+ useBorder ? BORDER + " " + VERTICAL_ALIGN : VERTICAL_ALIGN,
+ typeName,
+ file,
+ (height <= 0 ? "" : " height=\"" + height + "\""));
+ }
+
+ private static final class Renderer {
+
+ private Renderer() {
+ }
+
+ // Generates a graph in DOT format
+ static String graph(TypeElement rootClass, Set exports) {
+ final State state = new State(rootClass);
+ traverse(state, rootClass, exports);
+ return state.render();
+ }
+
+ static void traverse(State state, TypeElement node, Set exports) {
+ state.addNode(node);
+ for (TypeElement subNode : permittedSubclasses(node, exports)) {
+ if (isInPublicApi(node, exports) && isInPublicApi(subNode, exports)) {
+ state.addEdge(node, subNode);
+ }
+ traverse(state, subNode, exports);
+ }
+ }
+
+ private static final class State {
+
+ private static final String LABEL = "label";
+ private static final String TOOLTIP = "tooltip";
+ private static final String STYLE = "style";
+
+ private final StringBuilder builder;
+
+ private final Map> nodeStyleMap;
+
+ public State(TypeElement rootNode) {
+ nodeStyleMap = new LinkedHashMap<>();
+ builder = new StringBuilder()
+ .append("digraph G {")
+ .append(lineSeparator())
+ .append(" nodesep=0.500000;")
+ .append(lineSeparator())
+ .append(" ranksep=0.600000;")
+ .append(lineSeparator())
+ .append(" pencolor=transparent;")
+ .append(lineSeparator())
+ .append(" node [shape=plaintext, fontcolor=\"#e76f00\", fontname=\"DejaVuSans\", fontsize=12, margin=\".2,.2\"];")
+ .append(lineSeparator())
+ .append(" edge [penwidth=2, color=\"#999999\", arrowhead=open, arrowsize=1];")
+ .append(lineSeparator())
+ .append(" rankdir=\"BT\";")
+ .append(lineSeparator());
+ }
+
+ public void addNode(TypeElement node) {
+ var styles = nodeStyleMap.computeIfAbsent(id(node), n -> new LinkedHashMap<>());
+ styles.put(LABEL, node.getSimpleName().toString());
+ styles.put(TOOLTIP, node.getQualifiedName().toString());
+ if (!(node.getModifiers().contains(Modifier.SEALED) || node.getModifiers().contains(Modifier.FINAL))) {
+ // This indicates that the hierarchy is not closed
+ styles.put(STYLE, "dashed");
+ }
+ }
+
+ public void addEdge(TypeElement node, TypeElement subNode) {
+ builder.append(" ")
+ .append(quotedId(subNode))
+ .append(" -> ")
+ .append(quotedId(node))
+ .append(";")
+ .append(lineSeparator());
+ }
+
+ public String render() {
+ nodeStyleMap.forEach((nodeName, styles) -> {
+ builder.append(" ")
+ .append('"').append(nodeName).append("\" ")
+ .append(styles.entrySet().stream()
+ .map(e -> e.getKey() + "=\"" + e.getValue() + "\"")
+ .collect(Collectors.joining(" ", "[", "]")))
+ .append(System.lineSeparator());
+ });
+ builder.append("}");
+ return builder.toString();
+ }
+
+ private String id(TypeElement node) {
+ return node.getQualifiedName().toString();
+ }
+
+ private String quotedId(TypeElement node) {
+ return "\"" + id(node) + "\"";
+ }
+
+ private String simpleName(String name) {
+ int lastDot = name.lastIndexOf('.');
+ return lastDot < 0
+ ? name
+ : name.substring(lastDot);
+ }
+
+ }
+
+ private static List permittedSubclasses(TypeElement node, Set exports) {
+ return node.getPermittedSubclasses().stream()
+ .filter(DeclaredType.class::isInstance)
+ .map(DeclaredType.class::cast)
+ .map(DeclaredType::asElement)
+ .filter(TypeElement.class::isInstance)
+ .map(TypeElement.class::cast)
+ .filter(te -> isInPublicApi(te, exports))
+ .toList();
+ }
+
+ private static boolean isInPublicApi(TypeElement typeElement, Set exports) {
+ return (exports.contains(packageName(typeElement.getQualifiedName().toString())) ||
+ exports.contains(packageName(typeElement.getSuperclass().toString()))) &&
+ typeElement.getModifiers().contains(Modifier.PUBLIC);
+ }
+
+ private static String packageName(String name) {
+ int lastDot = name.lastIndexOf('.');
+ return lastDot < 0
+ ? ""
+ : name.substring(0, lastDot);
+ }
+ }
+}
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css
index 29463ad9d90..cb8f9159b16 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css
@@ -835,11 +835,11 @@ button.page-search-header {
span#page-search-link {
text-decoration: underline;
}
-.module-graph span {
+.module-graph span, .sealed-graph span {
display:none;
position:absolute;
}
-.module-graph:hover span {
+.module-graph:hover span, .sealed-graph:hover span {
display:block;
margin: -100px 0 0 100px;
z-index: 1;
diff --git a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java
index bdcc957592f..5ec8284244f 100644
--- a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java
+++ b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java
@@ -142,6 +142,7 @@ public class CheckStylesheetClasses {
// very JDK specific
styleSheetNames.remove("module-graph");
+ styleSheetNames.remove("sealed-graph");
boolean ok = check(htmlStyleNames, "HtmlStyle", styleSheetNames, "stylesheet")
& check(styleSheetNames, "stylesheet", htmlStyleNames, "HtmlStyle");