8295653: Add a graph of the sealed class hierarchy for marked classes

Co-authored-by: Per Minborg <pminborg@openjdk.org>
Reviewed-by: erikj, jjg
This commit is contained in:
Magnus Ihse Bursie 2022-11-03 14:51:24 +00:00
parent 59a13b1856
commit b7442d12e2
5 changed files with 366 additions and 50 deletions

View File

@ -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<sup>&reg;</sup> 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

View File

@ -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

View File

@ -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<Location> 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<String> 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 = "<span>"
+ getImage(simpleTypeName, imageFile, -1, true)
+ "</span>";
return "<dt>Sealed Class Hierarchy Graph:</dt>"
+ "<dd>"
+ "<a class=\"sealed-graph\" href=\"" + imageFile + "\">"
+ getImage(simpleTypeName, imageFile, thumbnailHeight, false)
+ hoverImage
+ "</a>"
+ "</dd>";
}
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("<img style=\"%s\" alt=\"Sealed class hierarchy graph for %s\" src=\"%s\"%s>",
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<String> exports) {
final State state = new State(rootClass);
traverse(state, rootClass, exports);
return state.render();
}
static void traverse(State state, TypeElement node, Set<String> 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<String, Map<String, String>> 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<TypeElement> permittedSubclasses(TypeElement node, Set<String> 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<String> 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);
}
}
}

View File

@ -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;

View File

@ -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");