8307652: sealed class hierarchy graph doesn't distinguish non-sealed classes

Reviewed-by: pminborg, jjg
This commit is contained in:
Chen Liang 2023-05-24 21:03:47 +00:00 committed by Jonathan Gibbons
parent 207fbcb083
commit 1451ac1770

View File

@ -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<String> 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<String, Map<String, String>> nodeStyleMap;
private final Map<String, Map<String, StyleItem>> 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("<I>&lt;any&gt;</I>"));
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());
});