diff --git a/make/jdk/src/classes/build/tools/taglet/SealedGraph.java b/make/jdk/src/classes/build/tools/taglet/SealedGraph.java index a53a8b16aab..787789fa0d0 100644 --- a/make/jdk/src/classes/build/tools/taglet/SealedGraph.java +++ b/make/jdk/src/classes/build/tools/taglet/SealedGraph.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -145,11 +145,15 @@ public final class SealedGraph implements Taglet { 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); + if (!(node.getModifiers().contains(Modifier.SEALED) || node.getModifiers().contains(Modifier.FINAL))) { + state.addNonSealedEdge(node); + } else { + for (TypeElement subNode : permittedSubclasses(node, exports)) { + if (isInPublicApi(node, exports) && isInPublicApi(subNode, exports)) { + state.addEdge(node, subNode); + } + traverse(state, subNode, exports); } - traverse(state, subNode, exports); } } @@ -158,13 +162,31 @@ public final class SealedGraph implements Taglet { private static final String LABEL = "label"; private static final String TOOLTIP = "tooltip"; private static final String LINK = "href"; - private static final String STYLE = "style"; private final TypeElement rootNode; private final StringBuilder builder; - private final Map> nodeStyleMap; + private final Map> nodeStyleMap; + + private int nonSealedHierarchyCount = 0; + + private sealed interface StyleItem { + String valueString(); + + record PlainString(String text) implements StyleItem { + @Override + public String valueString() { + return "\"" + text + "\""; + } + } + record HtmlString(String text) implements StyleItem { + @Override + public String valueString() { + return "<" + text + ">"; + } + } + } public State(TypeElement rootNode) { this.rootNode = rootNode; @@ -188,13 +210,9 @@ public final class SealedGraph implements Taglet { 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()); - styles.put(LINK, relativeLink(node)); - if (!(node.getModifiers().contains(Modifier.SEALED) || node.getModifiers().contains(Modifier.FINAL))) { - // This indicates that the hierarchy is not closed - styles.put(STYLE, "dashed"); - } + styles.put(LABEL, new StyleItem.PlainString(node.getSimpleName().toString())); + styles.put(TOOLTIP, new StyleItem.PlainString(node.getQualifiedName().toString())); + styles.put(LINK, new StyleItem.PlainString(relativeLink(node))); } // A permitted class must be in the same package or in the same module. @@ -223,12 +241,32 @@ public final class SealedGraph implements Taglet { .append(lineSeparator()); } + public void addNonSealedEdge(TypeElement node) { + // prepare open node + var openNodeId = "open node #" + nonSealedHierarchyCount++; + var styles = nodeStyleMap.computeIfAbsent(openNodeId, n -> new LinkedHashMap<>()); + styles.put(LABEL, new StyleItem.HtmlString("<any>")); + styles.put(TOOLTIP, new StyleItem.PlainString("Non-sealed Hierarchy")); + + // add link to parent node + builder.append(" ") + .append('"') + .append(openNodeId) + .append('"') + .append(" -> ") + .append(quotedId(node)) + .append(" ") + .append("[style=\"dashed\"]") + .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() + "\"") + .map(e -> e.getKey() + "=" + e.getValue().valueString()) .collect(joining(" ", "[", "]"))) .append(lineSeparator()); });