Skip to content

Commit 3af7372

Browse files
committed
インサイトのレンダリングをJSで行う
1 parent 600f010 commit 3af7372

3 files changed

Lines changed: 226 additions & 41 deletions

File tree

jig-core/src/main/java/org/dddjava/jig/adapter/thymeleaf/InsightAdapter.java

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
import org.dddjava.jig.domain.model.documents.stationery.JigDocumentContext;
88
import org.dddjava.jig.domain.model.information.JigRepository;
99
import org.dddjava.jig.domain.model.knowledge.insight.Insights;
10+
import org.dddjava.jig.domain.model.knowledge.insight.MethodInsight;
11+
import org.dddjava.jig.domain.model.knowledge.insight.PackageInsight;
12+
import org.dddjava.jig.domain.model.knowledge.insight.TypeInsight;
1013
import org.thymeleaf.TemplateEngine;
1114
import org.thymeleaf.context.Context;
1215

1316
import java.nio.file.Path;
1417
import java.util.List;
1518
import java.util.Locale;
1619
import java.util.Map;
20+
import java.util.stream.Collectors;
1721

1822

1923
@HandleDocument
@@ -34,11 +38,25 @@ public List<Path> invoke(JigRepository repository, JigDocument jigDocument) {
3438
Insights result = jigService.insights(repository);
3539
var jigDocumentWriter = new JigDocumentWriter(jigDocument, jigDocumentContext.outputDirectory());
3640

41+
String packagesJson = result.packageInsightList().stream()
42+
.map(this::formatPackageJson)
43+
.collect(Collectors.joining(",", "[", "]"));
44+
45+
String typesJson = result.typeInsightList().stream()
46+
.map(this::formatTypeJson)
47+
.collect(Collectors.joining(",", "[", "]"));
48+
49+
String methodsJson = result.methodInsightList().stream()
50+
.map(this::formatMethodJson)
51+
.collect(Collectors.joining(",", "[", "]"));
52+
53+
String insightJson = """
54+
{"packages": %s, "types": %s, "methods": %s}
55+
""".formatted(packagesJson, typesJson, methodsJson);
56+
3757
Map<String, Object> contextMap = Map.of(
3858
"title", jigDocumentWriter.jigDocument().label(),
39-
"packageInsightList", result.packageInsightList(),
40-
"typeInsightList", result.typeInsightList(),
41-
"methodInsightList", result.methodInsightList()
59+
"insightJson", insightJson
4260
);
4361

4462
Context context = new Context(Locale.ROOT, contextMap);
@@ -48,4 +66,53 @@ public List<Path> invoke(JigRepository repository, JigDocument jigDocument) {
4866
writer -> templateEngine.process(template, context, writer));
4967
return jigDocumentWriter.outputFilePaths();
5068
}
69+
70+
private String formatPackageJson(PackageInsight insight) {
71+
return """
72+
{"fqn": "%s", "label": "%s", "numberOfTypes": %d, "numberOfMethods": %d, "numberOfUsingTypes": %d, "cyclomaticComplexity": %d, "size": %d}
73+
""".formatted(
74+
escape(insight.fqn()),
75+
escape(insight.label()),
76+
insight.numberOfTypes(),
77+
insight.numberOfMethods(),
78+
insight.numberOfUsingTypes(),
79+
insight.cyclomaticComplexity(),
80+
insight.size());
81+
}
82+
83+
private String formatTypeJson(TypeInsight insight) {
84+
return """
85+
{"fqn": "%s", "label": "%s", "numberOfMethods": %d, "numberOfUsingTypes": %d, "cyclomaticComplexity": %d, "size": %d, "packageFqn": "%s"}
86+
""".formatted(
87+
escape(insight.fqn()),
88+
escape(insight.label()),
89+
insight.numberOfMethods(),
90+
insight.numberOfUsingTypes(),
91+
insight.cyclomaticComplexity(),
92+
insight.size(),
93+
escape(insight.packageFqn()));
94+
}
95+
96+
private String formatMethodJson(MethodInsight insight) {
97+
return """
98+
{"fqn": "%s", "label": "%s", "cyclomaticComplexity": %d, "numberOfUsingTypes": %d, "numberOfUsingMethods": %d, "numberOfUsingFields": %d, "size": %d, "packageFqn": "%s", "typeFqn": "%s"}
99+
""".formatted(
100+
escape(insight.fqn()),
101+
escape(insight.label()),
102+
insight.cyclomaticComplexity(),
103+
insight.numberOfUsingTypes(),
104+
insight.numberOfUsingMethods(),
105+
insight.numberOfUsingFields(),
106+
insight.size(),
107+
escape(insight.packageFqn()),
108+
escape(insight.typeFqn()));
109+
}
110+
111+
private String escape(String string) {
112+
return string
113+
.replace("\\", "\\\\")
114+
.replace("\"", "\\\"")
115+
.replace("\r", "\\r")
116+
.replace("\n", "\\n");
117+
}
51118
}

jig-core/src/main/resources/templates/assets/insight.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,103 @@
1+
function parseInsightData() {
2+
const dataElement = document.getElementById("insight-data");
3+
if (!dataElement) {
4+
return null;
5+
}
6+
try {
7+
return JSON.parse(dataElement.textContent);
8+
} catch (error) {
9+
console.error("Failed to parse insight JSON.", error);
10+
return null;
11+
}
12+
}
13+
14+
function setInsightCount(elementId, count) {
15+
const element = document.getElementById(elementId);
16+
if (element) {
17+
element.textContent = count.toString();
18+
}
19+
}
20+
21+
function createCell(text, className) {
22+
const cell = document.createElement("td");
23+
if (className) {
24+
cell.className = className;
25+
}
26+
cell.textContent = text;
27+
return cell;
28+
}
29+
30+
function createZoomCell() {
31+
const cell = document.createElement("td");
32+
const icon = document.createElement("i");
33+
icon.className = "zoom";
34+
icon.textContent = "🔍";
35+
cell.appendChild(icon);
36+
return cell;
37+
}
38+
39+
function renderPackageInsights(packages) {
40+
const tbody = document.querySelector("#package-insight-list tbody");
41+
if (!tbody) {
42+
return;
43+
}
44+
packages.forEach(packageInsight => {
45+
const row = document.createElement("tr");
46+
row.dataset.fqn = packageInsight.fqn;
47+
row.appendChild(createZoomCell());
48+
row.appendChild(createCell(packageInsight.fqn, "fqn"));
49+
row.appendChild(createCell(packageInsight.label));
50+
row.appendChild(createCell(packageInsight.numberOfTypes.toString(), "number"));
51+
row.appendChild(createCell(packageInsight.numberOfMethods.toString(), "number"));
52+
row.appendChild(createCell(packageInsight.numberOfUsingTypes.toString(), "number"));
53+
row.appendChild(createCell(packageInsight.cyclomaticComplexity.toString(), "number"));
54+
row.appendChild(createCell(packageInsight.size.toString(), "number"));
55+
tbody.appendChild(row);
56+
});
57+
}
58+
59+
function renderTypeInsights(types) {
60+
const tbody = document.querySelector("#type-insight-list tbody");
61+
if (!tbody) {
62+
return;
63+
}
64+
types.forEach(typeInsight => {
65+
const row = document.createElement("tr");
66+
row.dataset.fqn = typeInsight.fqn;
67+
row.dataset.packageFqn = typeInsight.packageFqn;
68+
row.appendChild(createZoomCell());
69+
row.appendChild(createCell(typeInsight.fqn, "fqn"));
70+
row.appendChild(createCell(typeInsight.label));
71+
row.appendChild(createCell(typeInsight.numberOfMethods.toString(), "number"));
72+
row.appendChild(createCell(typeInsight.numberOfUsingTypes.toString(), "number"));
73+
row.appendChild(createCell(typeInsight.cyclomaticComplexity.toString(), "number"));
74+
row.appendChild(createCell(typeInsight.size.toString(), "number"));
75+
tbody.appendChild(row);
76+
});
77+
}
78+
79+
function renderMethodInsights(methods) {
80+
const tbody = document.querySelector("#method-insight-list tbody");
81+
if (!tbody) {
82+
return;
83+
}
84+
methods.forEach(methodInsight => {
85+
const row = document.createElement("tr");
86+
row.dataset.fqn = methodInsight.fqn;
87+
row.dataset.packageFqn = methodInsight.packageFqn;
88+
row.dataset.typeFqn = methodInsight.typeFqn;
89+
row.appendChild(createZoomCell());
90+
row.appendChild(createCell(methodInsight.fqn, "fqn"));
91+
row.appendChild(createCell(methodInsight.label));
92+
row.appendChild(createCell(methodInsight.cyclomaticComplexity.toString(), "number"));
93+
row.appendChild(createCell(methodInsight.numberOfUsingTypes.toString(), "number"));
94+
row.appendChild(createCell(methodInsight.numberOfUsingMethods.toString(), "number"));
95+
row.appendChild(createCell(methodInsight.numberOfUsingFields.toString(), "number"));
96+
row.appendChild(createCell(methodInsight.size.toString(), "number"));
97+
tbody.appendChild(row);
98+
});
99+
}
100+
1101
// 拡大アイコンをクリックしたときに、その行以外を非表示にする
2102
function setupZoomIcons() {
3103
const zoomIcons = document.querySelectorAll("i.zoom");
@@ -84,6 +184,16 @@ document.addEventListener("DOMContentLoaded", function () {
84184
return;
85185
}
86186

187+
const insightData = parseInsightData();
188+
if (insightData) {
189+
renderPackageInsights(insightData.packages || []);
190+
renderTypeInsights(insightData.types || []);
191+
renderMethodInsights(insightData.methods || []);
192+
setInsightCount("package-count", (insightData.packages || []).length);
193+
setInsightCount("type-count", (insightData.types || []).length);
194+
setInsightCount("method-count", (insightData.methods || []).length);
195+
}
196+
87197
setupSortableTables();
88198
setupZoomIcons();
89199
document.getElementById("cancel-zoom").addEventListener("click", cancelZoom);

jig-core/src/main/resources/templates/insight.html

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ <h1>インサイト</h1>
1717

1818
<button id="cancel-zoom" class="hidden">ズーム解除</button>
1919

20-
<h2>パッケージ (<span th:text="${packageInsightList.size()}">1</span>)</h2>
20+
<h2>パッケージ (<span id="package-count">0</span>)</h2>
2121
<table id="package-insight-list" class="zebra sortable">
2222
<thead>
2323
<tr>
@@ -31,21 +31,10 @@ <h2>パッケージ (<span th:text="${packageInsightList.size()}">1</span>)</h2>
3131
<th>規模合計</th>
3232
</tr>
3333
</thead>
34-
<tbody>
35-
<tr th:each="insight: ${packageInsightList}" th:data-fqn="${insight.fqn()}">
36-
<td><i class="zoom">🔍</i></td>
37-
<td th:text="${insight.fqn()}" class="fqn">com.example</td>
38-
<td th:text="${insight.label()}">えぐざんぷる</td>
39-
<td th:text="${insight.numberOfTypes()}" class="number">11</td>
40-
<td th:text="${insight.numberOfMethods()}" class="number">11</td>
41-
<td th:text="${insight.numberOfUsingTypes()}" class="number">2</td>
42-
<td th:text="${insight.cyclomaticComplexity()}" class="number">5</td>
43-
<td th:text="${insight.size()}" class="number">129</td>
44-
</tr>
45-
</tbody>
34+
<tbody></tbody>
4635
</table>
4736

48-
<h2>クラス (<span th:text="${typeInsightList.size()}">1</span>)</h2>
37+
<h2>クラス (<span id="type-count">0</span>)</h2>
4938
<table id="type-insight-list" class="zebra sortable">
5039
<thead>
5140
<tr>
@@ -58,20 +47,10 @@ <h2>クラス (<span th:text="${typeInsightList.size()}">1</span>)</h2>
5847
<th>規模合計</th>
5948
</tr>
6049
</thead>
61-
<tbody>
62-
<tr th:each="insight: ${typeInsightList}" th:data-fqn="${insight.fqn()}" th:data-package-fqn="${insight.packageFqn()}">
63-
<td><i class="zoom">🔍</i></td>
64-
<td th:text="${insight.fqn()}" class="fqn">com.example</td>
65-
<td th:text="${insight.label()}">えぐざんぷる</td>
66-
<td th:text="${insight.numberOfMethods()}" class="number">11</td>
67-
<td th:text="${insight.numberOfUsingTypes()}" class="number">2</td>
68-
<td th:text="${insight.cyclomaticComplexity()}" class="number">5</td>
69-
<td th:text="${insight.size()}" class="number">129</td>
70-
</tr>
71-
</tbody>
50+
<tbody></tbody>
7251
</table>
7352

74-
<h2>メソッド (<span th:text="${methodInsightList.size()}">1</span>)</h2>
53+
<h2>メソッド (<span id="method-count">0</span>)</h2>
7554
<table id="method-insight-list" class="zebra sortable">
7655
<thead>
7756
<tr>
@@ -85,21 +64,50 @@ <h2>メソッド (<span th:text="${methodInsightList.size()}">1</span>)</h2>
8564
<th>規模</th>
8665
</tr>
8766
</thead>
88-
<tbody>
89-
<tr th:each="insight: ${methodInsightList}" th:data-fqn="${insight.fqn()}" th:data-package-fqn="${insight.packageFqn()}" th:data-type-fqn="${insight.typeFqn()}">
90-
<td><i class="zoom">🔍</i></td>
91-
<td th:text="${insight.fqn()}" class="fqn">com.example</td>
92-
<td th:text="${insight.label()}">えぐざんぷる</td>
93-
<td th:text="${insight.cyclomaticComplexity()}" class="number">5</td>
94-
<td th:text="${insight.numberOfUsingTypes()}" class="number">2</td>
95-
<td th:text="${insight.numberOfUsingMethods()}" class="number">11</td>
96-
<td th:text="${insight.numberOfUsingFields()}" class="number">6</td>
97-
<td th:text="${insight.size()}" class="number">129</td>
98-
</tr>
99-
</tbody>
67+
<tbody></tbody>
10068
</table>
10169
</main>
10270

71+
<script id="insight-data" type="application/json" th:utext="${insightJson}">
72+
{
73+
"packages": [
74+
{
75+
"fqn": "com.example",
76+
"label": "example",
77+
"numberOfTypes": 3,
78+
"numberOfMethods": 10,
79+
"numberOfUsingTypes": 2,
80+
"cyclomaticComplexity": 5,
81+
"size": 120
82+
}
83+
],
84+
"types": [
85+
{
86+
"fqn": "com.example.Example",
87+
"label": "Example",
88+
"numberOfMethods": 5,
89+
"numberOfUsingTypes": 2,
90+
"cyclomaticComplexity": 3,
91+
"size": 60,
92+
"packageFqn": "com.example"
93+
}
94+
],
95+
"methods": [
96+
{
97+
"fqn": "com.example.Example#method()",
98+
"label": "method",
99+
"cyclomaticComplexity": 1,
100+
"numberOfUsingTypes": 1,
101+
"numberOfUsingMethods": 2,
102+
"numberOfUsingFields": 0,
103+
"size": 10,
104+
"packageFqn": "com.example",
105+
"typeFqn": "com.example.Example"
106+
}
107+
]
108+
}
109+
</script>
110+
103111
<th:block th:replace="~{fragment-base::scripts}">
104112
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.7/marked.min.js"></script>
105113
<script src="https://cdn.jsdelivr.net/npm/mermaid@11.4.1/dist/mermaid.min.js"></script>

0 commit comments

Comments
 (0)