From 9263071ca2b7c9568f349b572a10048b78b0c580 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 13:56:08 +0200
Subject: [PATCH 01/20] Create maven.yml
---
.github/workflows/maven.yml | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 .github/workflows/maven.yml
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
new file mode 100644
index 000000000..a661e8938
--- /dev/null
+++ b/.github/workflows/maven.yml
@@ -0,0 +1,35 @@
+# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
+
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+name: Java CI with Maven
+
+on:
+ push:
+ branches: [ "develop" ]
+ pull_request:
+ branches: [ "develop" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'temurin'
+ cache: maven
+ - name: Build with Maven
+ run: mvn -B package --file pom.xml
+
+ # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
+ - name: Update dependency graph
+ uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
From ec8fd863e3d4d682d3d31738a8114a7b69dc2b35 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 14:03:36 +0200
Subject: [PATCH 02/20] Update maven.yml
---
.github/workflows/maven.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index a661e8938..26284b6f1 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -28,7 +28,7 @@ jobs:
distribution: 'temurin'
cache: maven
- name: Build with Maven
- run: mvn -B package --file pom.xml
+ run: mvn clean install
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- name: Update dependency graph
From 477dbe13172202023382f3d9416cbfe8b461b496 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 14:05:59 +0200
Subject: [PATCH 03/20] Update maven.yml
---
.github/workflows/maven.yml | 4 ----
1 file changed, 4 deletions(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 26284b6f1..b4ac768e8 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -29,7 +29,3 @@ jobs:
cache: maven
- name: Build with Maven
run: mvn clean install
-
- # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- - name: Update dependency graph
- uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
From 60ccf95f0706c64ab162f085098d2809ef83afa0 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 14:42:42 +0200
Subject: [PATCH 04/20] Create maven-settings.xml
---
.github/workflows/maven-settings.xml | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 .github/workflows/maven-settings.xml
diff --git a/.github/workflows/maven-settings.xml b/.github/workflows/maven-settings.xml
new file mode 100644
index 000000000..0de651b15
--- /dev/null
+++ b/.github/workflows/maven-settings.xml
@@ -0,0 +1,9 @@
+
+
+
+ github
+ ${env.GITHUB_ACTOR}
+ ${env.GITHUB_TOKEN}
+
+
+
From 4ed5cd88cc984b778d9f87dda6ab2b56ea5fcaeb Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
Date: Tue, 16 Sep 2025 14:45:51 +0200
Subject: [PATCH 05/20] Update pom with github packages
---
pom.xml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/pom.xml b/pom.xml
index 5f6c7eef5..6a4d16d88 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,6 +8,13 @@
JHotDraw
+
+
+ github
+ GitHub Packages
+ https://maven.pkg.github.com/Benjamand/JHotDraw15
+
+
github
From ce8964043554e99c3a7780ee5ce1cd04f255b08b Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 14:47:29 +0200
Subject: [PATCH 06/20] Update maven.yml
---
.github/workflows/maven.yml | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index b4ac768e8..19bf4f6c2 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -19,6 +19,10 @@ jobs:
runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
@@ -27,5 +31,14 @@ jobs:
java-version: '21'
distribution: 'temurin'
cache: maven
+ server-id: github
+ server-username: GITHUB_ACTOR
+ server-password: GITHUB_TOKEN
+ settings-path: ${{ github.workspace }}/.github
- name: Build with Maven
- run: mvn clean install
+ run: mvn clean install --settings .github/maven-settings.xml
+
+ - name: Publish to GitHub Packages
+ run: mvn deploy --settings .github/maven-settings.xml
+
+
From c94cfda20455cc999b57e6bef13c894e560267ef Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 14:50:02 +0200
Subject: [PATCH 07/20] Update maven.yml
---
.github/workflows/maven.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 19bf4f6c2..8f3fa7260 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -9,6 +9,7 @@
name: Java CI with Maven
on:
+ workflow_dispatch:
push:
branches: [ "develop" ]
pull_request:
From 15b98e0a9fcafc9ef456b607d5685ce9ca2a5deb Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 14:53:42 +0200
Subject: [PATCH 08/20] Update maven.yml
---
.github/workflows/maven.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 8f3fa7260..4446eebe5 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -37,9 +37,9 @@ jobs:
server-password: GITHUB_TOKEN
settings-path: ${{ github.workspace }}/.github
- name: Build with Maven
- run: mvn clean install --settings .github/maven-settings.xml
+ run: mvn clean install
- name: Publish to GitHub Packages
- run: mvn deploy --settings .github/maven-settings.xml
+ run: mvn deploy
From a8f470ecea7f0e9dc6e8b49cf06609ec4cf955f5 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
Date: Tue, 16 Sep 2025 14:57:52 +0200
Subject: [PATCH 09/20] Update pom with github packages snapshot management
---
pom.xml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/pom.xml b/pom.xml
index 6a4d16d88..ab56a90cc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,6 +14,11 @@
GitHub Packages
https://maven.pkg.github.com/Benjamand/JHotDraw15
+
+ github
+ GitHub Packages
+ https://maven.pkg.github.com/Benjamand/JHotDraw15
+
From 1c476403e05992a32779377d8d1fa88ab543e382 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 15:00:20 +0200
Subject: [PATCH 10/20] Delete .github/workflows/maven-settings.xml
---
.github/workflows/maven-settings.xml | 9 ---------
1 file changed, 9 deletions(-)
delete mode 100644 .github/workflows/maven-settings.xml
diff --git a/.github/workflows/maven-settings.xml b/.github/workflows/maven-settings.xml
deleted file mode 100644
index 0de651b15..000000000
--- a/.github/workflows/maven-settings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- github
- ${env.GITHUB_ACTOR}
- ${env.GITHUB_TOKEN}
-
-
-
From cc18c0210fce3f02114d67e26f6636630be3f148 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 15:05:02 +0200
Subject: [PATCH 11/20] Update maven.yml
---
.github/workflows/maven.yml | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 4446eebe5..1945958fc 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -23,7 +23,10 @@ jobs:
permissions:
contents: read
packages: write
-
+
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
From f9089c58b005c97a09aa2a6a537c76e20700bbf8 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Tue, 16 Sep 2025 15:06:42 +0200
Subject: [PATCH 12/20] Update maven.yml to not need settings file
---
.github/workflows/maven.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 1945958fc..7594fb495 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -38,7 +38,6 @@ jobs:
server-id: github
server-username: GITHUB_ACTOR
server-password: GITHUB_TOKEN
- settings-path: ${{ github.workspace }}/.github
- name: Build with Maven
run: mvn clean install
From 462e2b8fffd2e77cfab5a1b05e655b0151f7fc3c Mon Sep 17 00:00:00 2001
From: MarkusKaas23 <143800614+MarkusKaas23@users.noreply.github.com>
Date: Tue, 11 Nov 2025 15:44:54 +0100
Subject: [PATCH 13/20] Refactored class and BDD tests
---
...igure.BDDEllipse.EllipseFigureBDDTest.json | 202 ++++++++++++++++++
jhotdraw-core/pom.xml | 55 +++++
.../jhotdraw/draw/figure/EllipseFigure.java | 72 ++++---
.../BDDEllipse/EllipseFigureBDDTest.java | 16 ++
.../draw/figure/BDDEllipse/GivenEllipse.java | 16 ++
.../draw/figure/BDDEllipse/ThenEllipse.java | 20 ++
.../figure/BDDEllipse/WhenSettingBounds.java | 20 ++
7 files changed, 373 insertions(+), 28 deletions(-)
create mode 100644 jhotdraw-core/jgiven-reports/org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest.json
create mode 100644 jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/EllipseFigureBDDTest.java
create mode 100644 jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/GivenEllipse.java
create mode 100644 jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/ThenEllipse.java
create mode 100644 jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/WhenSettingBounds.java
diff --git a/jhotdraw-core/jgiven-reports/org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest.json b/jhotdraw-core/jgiven-reports/org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest.json
new file mode 100644
index 000000000..ed2e1a4a5
--- /dev/null
+++ b/jhotdraw-core/jgiven-reports/org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest.json
@@ -0,0 +1,202 @@
+{
+ "className": "org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest",
+ "name": "Ellipse Figure BDD",
+ "scenarios": [
+ {
+ "className": "org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest",
+ "testMethodName": "ellipse_resizes_when_bounds_are_set",
+ "description": "ellipse resizes when bounds are set",
+ "tagIds": [],
+ "explicitParameters": [],
+ "derivedParameters": [],
+ "scenarioCases": [
+ {
+ "caseNr": 1,
+ "steps": [
+ {
+ "name": "an ellipse",
+ "words": [
+ {
+ "value": "Given",
+ "isIntroWord": true
+ },
+ {
+ "value": "an ellipse"
+ }
+ ],
+ "status": "PASSED",
+ "durationInNanos": 21426208,
+ "depth": 0,
+ "parentFailed": false
+ },
+ {
+ "name": "bounds are set",
+ "words": [
+ {
+ "value": "When",
+ "isIntroWord": true
+ },
+ {
+ "value": "bounds are set"
+ },
+ {
+ "value": "0.0",
+ "argumentInfo": {
+ "argumentName": "x1",
+ "formattedValue": "0.0"
+ }
+ },
+ {
+ "value": "0.0",
+ "argumentInfo": {
+ "argumentName": "y1",
+ "formattedValue": "0.0"
+ }
+ },
+ {
+ "value": "100.0",
+ "argumentInfo": {
+ "argumentName": "x2",
+ "formattedValue": "100.0"
+ }
+ },
+ {
+ "value": "50.0",
+ "argumentInfo": {
+ "argumentName": "y2",
+ "formattedValue": "50.0"
+ }
+ }
+ ],
+ "status": "FAILED",
+ "durationInNanos": 18851584,
+ "depth": 0,
+ "parentFailed": false
+ },
+ {
+ "name": "ellipse should have size",
+ "words": [
+ {
+ "value": "Then",
+ "isIntroWord": true
+ },
+ {
+ "value": "ellipse should have size"
+ },
+ {
+ "value": "100.0",
+ "argumentInfo": {
+ "argumentName": "width",
+ "formattedValue": "100.0"
+ }
+ },
+ {
+ "value": "50.0",
+ "argumentInfo": {
+ "argumentName": "height",
+ "formattedValue": "50.0"
+ }
+ }
+ ],
+ "status": "SKIPPED",
+ "durationInNanos": 0,
+ "depth": 0,
+ "parentFailed": false
+ }
+ ],
+ "explicitArguments": [],
+ "derivedArguments": [],
+ "status": "FAILED",
+ "errorMessage": "java.lang.NullPointerException: Cannot invoke \"org.jhotdraw.draw.figure.EllipseFigure.setBounds(java.awt.geom.Point2D$Double, java.awt.geom.Point2D$Double)\" because \"this.ellipse\" is null",
+ "stackTrace": [
+ "org.jhotdraw.draw.figure.BDDEllipse.WhenSettingBounds.bounds_are_set(WhenSettingBounds.java:16)",
+ "org.jhotdraw.draw.figure.BDDEllipse.WhenSettingBounds$ByteBuddy$rriOpszZ.bounds_are_set$accessor$FNcLk1s9(Unknown Source)",
+ "org.jhotdraw.draw.figure.BDDEllipse.WhenSettingBounds$ByteBuddy$rriOpszZ$auxiliary$zGYJXUzv.call(Unknown Source)",
+ "org.jhotdraw.draw.figure.BDDEllipse.WhenSettingBounds$ByteBuddy$rriOpszZ.bounds_are_set(Unknown Source)",
+ "org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest.ellipse_resizes_when_bounds_are_set(EllipseFigureBDDTest.java:13)",
+ "java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)",
+ "org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:701)",
+ "org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:502)",
+ "org.junit.jupiter.engine.support.MethodReflectionUtils.invoke(MethodReflectionUtils.java:45)",
+ "org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:61)",
+ "org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:124)",
+ "org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:163)",
+ "org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:148)",
+ "org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)",
+ "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:123)",
+ "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:105)",
+ "org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:99)",
+ "org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:66)",
+ "org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:47)",
+ "org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:39)",
+ "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:104)",
+ "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:98)",
+ "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invokeVoid(InterceptingExecutableInvoker.java:71)",
+ "org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$0(TestMethodTestDescriptor.java:219)",
+ "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)",
+ "org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:215)",
+ "org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:157)",
+ "org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:176)",
+ "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166)",
+ "org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164)",
+ "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116)",
+ "java.base/java.util.ArrayList.forEach(ArrayList.java:1597)",
+ "org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:42)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:180)",
+ "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166)",
+ "org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164)",
+ "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116)",
+ "java.base/java.util.ArrayList.forEach(ArrayList.java:1597)",
+ "org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:42)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:180)",
+ "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166)",
+ "org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164)",
+ "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163)",
+ "org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116)",
+ "org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:36)",
+ "org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:52)",
+ "org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:58)",
+ "org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:246)",
+ "org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:218)",
+ "org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:179)",
+ "org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)",
+ "org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:66)",
+ "org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:157)",
+ "org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:65)",
+ "org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:125)",
+ "org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)",
+ "org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:93)",
+ "org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:48)",
+ "org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$0(InterceptingLauncher.java:41)",
+ "org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25)",
+ "org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:40)",
+ "org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:48)",
+ "org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:67)",
+ "com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)",
+ "com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)",
+ "com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)",
+ "com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)",
+ "com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:231)",
+ "com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)"
+ ],
+ "durationInNanos": 99998209
+ }
+ ],
+ "casesAsTable": false,
+ "durationInNanos": 99998209
+ }
+ ],
+ "tagMap": {}
+}
\ No newline at end of file
diff --git a/jhotdraw-core/pom.xml b/jhotdraw-core/pom.xml
index 7c276da85..6ec23a754 100644
--- a/jhotdraw-core/pom.xml
+++ b/jhotdraw-core/pom.xml
@@ -1,6 +1,21 @@
4.0.0
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+
+ --add-opens java.base/java.lang=ALL-UNNAMED
+
+
+
+
+
+
org.jhotdraw
jhotdraw
@@ -9,6 +24,22 @@
jhotdraw-core
jar
+
+
+ com.tngtech.jgiven
+ jgiven-junit5
+ 1.3.0
+ test
+
+
+
+
+ org.assertj
+ assertj-core
+ 3.24.2
+ test
+
+
${project.groupId}
jhotdraw-api
@@ -40,5 +71,29 @@
jhotdraw-actions
${project.version}
+
+ junit
+ junit
+ 4.13.2
+ compile
+
+
+ org.junit.jupiter
+ junit-jupiter
+ RELEASE
+ compile
+
+
+ com.tngtech.jgiven
+ jgiven-junit5
+ 1.3.0
+ compile
+
+
+ org.assertj
+ assertj-core
+ 3.25.3
+ compile
+
\ No newline at end of file
diff --git a/jhotdraw-core/src/main/java/org/jhotdraw/draw/figure/EllipseFigure.java b/jhotdraw-core/src/main/java/org/jhotdraw/draw/figure/EllipseFigure.java
index 624562ac9..26262d6fc 100644
--- a/jhotdraw-core/src/main/java/org/jhotdraw/draw/figure/EllipseFigure.java
+++ b/jhotdraw-core/src/main/java/org/jhotdraw/draw/figure/EllipseFigure.java
@@ -9,6 +9,7 @@
import java.awt.*;
import java.awt.geom.*;
+import org.jhotdraw.draw.AttributeKey;
import org.jhotdraw.draw.AttributeKeys;
import org.jhotdraw.draw.connector.ChopEllipseConnector;
import org.jhotdraw.draw.connector.Connector;
@@ -25,6 +26,29 @@ public class EllipseFigure extends AbstractAttributedFigure {
private static final long serialVersionUID = 1L;
protected Ellipse2D.Double ellipse;
+ /**
+ * Copy constructor for EllipseFigure.
+ */
+ public EllipseFigure(EllipseFigure source) {
+ this.ellipse = copyEllipse(source.ellipse);
+ setAttributes(source.getAttributes());
+ for (AttributeKey> key : source.getAttributes().keySet()) {
+ if (!source.isAttributeEnabled(key)) {
+ setAttributeEnabled(key, false);
+ }
+ }
+ }
+
+
+ private Ellipse2D.Double copyEllipse(Ellipse2D.Double sourceEllipse) {
+ return new Ellipse2D.Double(
+ sourceEllipse.x,
+ sourceEllipse.y,
+ sourceEllipse.width,
+ sourceEllipse.height
+ );
+ }
+
/**
* Constructs a new {@code EllipseFigure}, initialized to
* location (0, 0) and size (0, 0).
@@ -77,14 +101,21 @@ public Rectangle2D.Double getDrawingArea() {
return r;
}
+ private Ellipse2D.Double prepareEllipse(double grow) {
+ Ellipse2D.Double copy = copyEllipse(this.ellipse);
+ copy.x -= grow;
+ copy.y -= grow;
+ copy.width += grow * 2;
+ copy.height += grow * 2;
+ return copy;
+ }
+
@Override
protected void drawFill(Graphics2D g) {
- Ellipse2D.Double r = (Ellipse2D.Double) ellipse.clone();
- double grow = AttributeKeys.getPerpendicularFillGrowth(this, AttributeKeys.getScaleFactorFromGraphics(g));
- r.x -= grow;
- r.y -= grow;
- r.width += grow * 2;
- r.height += grow * 2;
+ Ellipse2D.Double r = prepareEllipse(
+ AttributeKeys.getPerpendicularFillGrowth(this,
+ AttributeKeys.getScaleFactorFromGraphics(g))
+ );
if (r.width > 0 && r.height > 0) {
g.fill(r);
}
@@ -92,28 +123,20 @@ protected void drawFill(Graphics2D g) {
@Override
protected void drawStroke(Graphics2D g) {
- Ellipse2D.Double r = (Ellipse2D.Double) ellipse.clone();
- double grow = AttributeKeys.getPerpendicularDrawGrowth(this, AttributeKeys.getScaleFactorFromGraphics(g));
- r.x -= grow;
- r.y -= grow;
- r.width += grow * 2;
- r.height += grow * 2;
+ Ellipse2D.Double r = prepareEllipse(
+ AttributeKeys.getPerpendicularDrawGrowth(this,
+ AttributeKeys.getScaleFactorFromGraphics(g))
+ );
if (r.width > 0 && r.height > 0) {
g.draw(r);
}
}
- /**
- * Checks if a Point2D.Double is inside the figure.
- */
@Override
public boolean contains(Point2D.Double p) {
- Ellipse2D.Double r = (Ellipse2D.Double) ellipse.clone();
- double grow = AttributeKeys.getPerpendicularHitGrowth(this, 1.0);
- r.x -= grow;
- r.y -= grow;
- r.width += grow * 2;
- r.height += grow * 2;
+ Ellipse2D.Double r = prepareEllipse(
+ AttributeKeys.getPerpendicularHitGrowth(this, 1.0)
+ );
return r.contains(p);
}
@@ -139,13 +162,6 @@ public void transform(AffineTransform tx) {
(Point2D.Double) tx.transform(lead, lead));
}
- @Override
- public EllipseFigure clone() {
- EllipseFigure that = (EllipseFigure) super.clone();
- that.ellipse = (Ellipse2D.Double) this.ellipse.clone();
- return that;
- }
-
@Override
public void restoreTransformTo(Object geometry) {
Ellipse2D.Double e = (Ellipse2D.Double) geometry;
diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/EllipseFigureBDDTest.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/EllipseFigureBDDTest.java
new file mode 100644
index 000000000..1beec7270
--- /dev/null
+++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/EllipseFigureBDDTest.java
@@ -0,0 +1,16 @@
+package org.jhotdraw.draw.figure.BDDEllipse;
+
+import com.tngtech.jgiven.junit5.ScenarioTest;
+import org.junit.jupiter.api.Test;
+
+
+public class EllipseFigureBDDTest extends
+ ScenarioTest {
+
+ @Test
+ public void ellipse_resizes_when_bounds_are_set() {
+ given().an_ellipse();
+ when().bounds_are_set(0, 0, 100, 50);
+ then().ellipse_should_have_size(100, 50);
+ }
+}
diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/GivenEllipse.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/GivenEllipse.java
new file mode 100644
index 000000000..33e6e8909
--- /dev/null
+++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/GivenEllipse.java
@@ -0,0 +1,16 @@
+package org.jhotdraw.draw.figure.BDDEllipse;
+
+import com.tngtech.jgiven.Stage;
+import com.tngtech.jgiven.annotation.ProvidedScenarioState;
+import org.jhotdraw.draw.figure.EllipseFigure;
+
+public class GivenEllipse extends Stage {
+
+ @ProvidedScenarioState
+ EllipseFigure ellipse;
+
+ public GivenEllipse an_ellipse() {
+ ellipse = new EllipseFigure();
+ return self();
+ }
+}
diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/ThenEllipse.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/ThenEllipse.java
new file mode 100644
index 000000000..019d1083c
--- /dev/null
+++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/ThenEllipse.java
@@ -0,0 +1,20 @@
+package org.jhotdraw.draw.figure.BDDEllipse;
+
+import com.tngtech.jgiven.Stage;
+import com.tngtech.jgiven.annotation.ExpectedScenarioState;
+import org.jhotdraw.draw.figure.EllipseFigure;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ThenEllipse extends Stage {
+
+ @ExpectedScenarioState
+ EllipseFigure ellipse;
+
+ public ThenEllipse ellipse_should_have_size(double width, double height) {
+ assertThat(ellipse.getBounds().width).isEqualTo(width);
+ assertThat(ellipse.getBounds().height).isEqualTo(height);
+ return self();
+ }
+}
+
diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/WhenSettingBounds.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/WhenSettingBounds.java
new file mode 100644
index 000000000..ff28eeeef
--- /dev/null
+++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/WhenSettingBounds.java
@@ -0,0 +1,20 @@
+package org.jhotdraw.draw.figure.BDDEllipse;
+
+import com.tngtech.jgiven.Stage;
+import com.tngtech.jgiven.annotation.ExpectedScenarioState;
+import org.jhotdraw.draw.figure.EllipseFigure;
+
+import java.awt.geom.Point2D;
+
+
+public class WhenSettingBounds extends Stage {
+
+ @ExpectedScenarioState
+ EllipseFigure ellipse;
+
+ public WhenSettingBounds bounds_are_set(double x1, double y1, double x2, double y2) {
+ ellipse.setBounds(new Point2D.Double(x1, y1), new Point2D.Double(x2, y2));
+ return self();
+ }
+}
+
From 267c595e8318f2744ab8f2219d83b1c20ec55947 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
Date: Thu, 13 Nov 2025 13:00:10 +0100
Subject: [PATCH 14/20] Unit tests added to FontChooser
---
jhotdraw-gui/pom.xml | 36 +++++++++
.../java/org/jhotdraw/gui/JFontChooser.java | 20 ++++-
.../gui/fontchooser/FontFaceNode.java | 77 +++++++++----------
.../DefaultFontChooserModelTests.java | 44 +++++++++++
.../test/fontTests/FontFaceNodeTests.java | 42 ++++++++++
.../test/fontTests/JFontChooserTests.java | 70 +++++++++++++++++
6 files changed, 243 insertions(+), 46 deletions(-)
create mode 100644 jhotdraw-gui/src/main/test/fontTests/DefaultFontChooserModelTests.java
create mode 100644 jhotdraw-gui/src/main/test/fontTests/FontFaceNodeTests.java
create mode 100644 jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java
diff --git a/jhotdraw-gui/pom.xml b/jhotdraw-gui/pom.xml
index 0c7a5da84..b7a95e934 100644
--- a/jhotdraw-gui/pom.xml
+++ b/jhotdraw-gui/pom.xml
@@ -29,6 +29,42 @@
jhotdraw-core
${project.version}
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ org.mockito
+ mockito-core
+ 3.6.28
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ RELEASE
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ RELEASE
+ test
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 9
+ 9
+
+
+
+
jhotdraw-gui
\ No newline at end of file
diff --git a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/JFontChooser.java b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/JFontChooser.java
index 7508a76e5..06f9c5ff5 100644
--- a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/JFontChooser.java
+++ b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/JFontChooser.java
@@ -128,7 +128,9 @@ public JFontChooser() {
public void propertyChange(PropertyChangeEvent evt) {
if ("ancestor".equals(evt.getPropertyName()) && evt.getNewValue() != null) {
try {
- ((DefaultFontChooserModel) model).setFonts(getAllFonts());
+ Font[] fonts = getAllFonts();
+ ((DefaultFontChooserModel) model).setFonts(fonts);
+
} catch (Exception ex) {
ex.printStackTrace();
}
@@ -255,7 +257,9 @@ protected void fireActionPerformed(String command) {
Object[] listeners = listenerList.getListenerList();
long mostRecentEventTime = EventQueue.getMostRecentEventTime();
int modifiers = 0;
+
AWTEvent currentEvent = EventQueue.getCurrentEvent();
+
if (currentEvent instanceof InputEvent) {
modifiers = ((InputEvent) currentEvent).getModifiers();
} else if (currentEvent instanceof ActionEvent) {
@@ -328,8 +332,10 @@ public Font[] call() throws Exception {
//System.out.println("JFontChooser ***bogus*** "+decoded.getFontName());
}
}
+ // set a default font so a font is always chosen.
return goodFonts.toArray(new Font[goodFonts.size()]);
// return fonts;
+
}
});
new Thread(future).start();
@@ -351,6 +357,7 @@ public static synchronized Font[] getAllFonts() {
} catch (InterruptedException | ExecutionException ex) {
return new Font[0];
}
+
}
/**
@@ -377,9 +384,14 @@ public Font getSelectedFont() {
*/
public void setSelectedFont(Font newValue) {
Font oldValue = selectedFont;
- this.selectedFont = newValue;
- firePropertyChange(SELECTED_FONT_PROPERTY, oldValue, newValue);
- updateSelectionPath(newValue);
+ Font decoded = Font.decode(newValue.getFontName());
+ if (decoded.getFontName().equals(newValue.getFontName()) || decoded.getFontName().endsWith("-Derived")) {
+ this.selectedFont = newValue;
+ firePropertyChange(SELECTED_FONT_PROPERTY, oldValue, newValue);
+ updateSelectionPath(newValue);
+ } else {
+ return;
+ }
}
/**
diff --git a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontFaceNode.java b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontFaceNode.java
index d7f1f2a9d..75bae708d 100644
--- a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontFaceNode.java
+++ b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontFaceNode.java
@@ -10,6 +10,7 @@
import java.awt.Font;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.Map;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
@@ -30,55 +31,47 @@ public FontFaceNode(Font typeface) {
this.name = beautifyName(typeface.getPSName());
}
+ protected String capitalize(String s) {
+ if (s == null || s.isEmpty()) {
+ return s;
+ }
+ String c = String.valueOf(s.charAt(0));
+ c = c.toUpperCase();
+ s = s.substring(1);
+ s = c.concat(s);
+ return s;
+ }
+
protected String beautifyName(String name) {
// 'Beautify' the name
int p = name.lastIndexOf('-');
if (p != -1) {
name = name.substring(p + 1);
- String lcName = name.toLowerCase();
- if ("plain".equals(lcName)) {
- name = "Plain";
- } else if ("bolditalic".equals(lcName)) {
- name = "Bold Italic";
- } else if ("italic".equals(lcName)) {
- name = "Italic";
- } else if ("bold".equals(lcName)) {
- name = "Bold";
- }
- } else {
- String lcName = name.toLowerCase();
- if (lcName.endsWith("plain")) {
- name = "Plain";
- } else if (lcName.endsWith("boldoblique")) {
- name = "Bold Oblique";
- } else if (lcName.endsWith("bolditalic")) {
- name = "Bold Italic";
- } else if (lcName.endsWith("bookita")) {
- name = "Book Italic";
- } else if (lcName.endsWith("bookit")) {
- name = "Book Italic";
- } else if (lcName.endsWith("demibold")) {
- name = "Demi Bold";
- } else if (lcName.endsWith("semiita")) {
- name = "Semi Italic";
- } else if (lcName.endsWith("italic")) {
- name = "Italic";
- } else if (lcName.endsWith("book")) {
- name = "Book";
- } else if (lcName.endsWith("bold")) {
- name = "Bold";
- } else if (lcName.endsWith("bol")) {
- name = "Bold";
- } else if (lcName.endsWith("oblique")) {
- name = "Oblique";
- } else if (lcName.endsWith("regular")) {
- name = "Regular";
- } else if (lcName.endsWith("semi")) {
- name = "Semi";
- } else {
- name = "Plain";
+ }
+
+ String lcName = name.toLowerCase();
+ name = capitalize(lcName);
+
+ Map replacements = Map.of(
+ "bolditalic", "Bold Italic",
+ "boldoblique", "Bold Oblique",
+ "bookita", "Book Italic",
+ "bookit", "Book Italic",
+ "demibold", "Demi Bold",
+ "semiita", "Semi Italic"
+ );
+
+ for (Map.Entry entry : replacements.entrySet()) {
+ if (lcName.endsWith(entry.getKey())) {
+ return entry.getValue();
}
}
+
+ name = insertSpaces(name);
+ return name;
+ }
+
+ private static String insertSpaces(String name) {
StringBuilder buf = new StringBuilder();
char prev = name.charAt(0);
buf.append(prev);
diff --git a/jhotdraw-gui/src/main/test/fontTests/DefaultFontChooserModelTests.java b/jhotdraw-gui/src/main/test/fontTests/DefaultFontChooserModelTests.java
new file mode 100644
index 000000000..3d5522c83
--- /dev/null
+++ b/jhotdraw-gui/src/main/test/fontTests/DefaultFontChooserModelTests.java
@@ -0,0 +1,44 @@
+package fontTests;
+
+import org.jhotdraw.gui.JFontChooser;
+import org.jhotdraw.gui.fontchooser.DefaultFontChooserModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionListener;
+
+import static org.junit.Assert.*;
+
+public class DefaultFontChooserModelTests {
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+
+ @Test
+ // Invariant test
+ // Regardless of which fonts are set, the models root node has exactly 10 children.
+ public void setFontsTest() {
+ DefaultFontChooserModel model = new DefaultFontChooserModel();
+ model.setFonts(new Font[] { new Font("Arial", Font.PLAIN, 12) });
+ Assertions.assertEquals(10, model.getChildCount(model.getRoot()));
+
+ model.setFonts(GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts());
+ assertEquals(10, model.getChildCount(model.getRoot()));
+
+ model.setFonts(new Font[] { new Font("Times New Roman", Font.BOLD, 14) });
+ assertEquals(10, model.getChildCount(model.getRoot()));
+ }
+
+
+
+}
diff --git a/jhotdraw-gui/src/main/test/fontTests/FontFaceNodeTests.java b/jhotdraw-gui/src/main/test/fontTests/FontFaceNodeTests.java
new file mode 100644
index 000000000..a1c4482fe
--- /dev/null
+++ b/jhotdraw-gui/src/main/test/fontTests/FontFaceNodeTests.java
@@ -0,0 +1,42 @@
+package fontTests;
+
+import org.jhotdraw.gui.fontchooser.FontFaceNode;
+import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionListener;
+
+import static org.junit.Assert.*;
+
+public class FontFaceNodeTests {
+
+ @Mock
+ private ActionListener listener;
+ private Font font1;
+ private Font font2;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ // Best case scenario
+ public void beautifyNameTest() {
+ Font testFont = new Font("Dialog", Font.PLAIN, 12);
+ FontFaceNode faceNode = new FontFaceNode(testFont);
+ assertEquals("Dialog.plain", faceNode.toString());
+ }
+
+ // Test boundary case scenario for FontFaceNode
+ @Test
+ public void beautifyNameTestBoundaryCase() {
+ Font testFont = new Font("k213k02991021903901kmdsakd02139smklææææ...l,æææ", Font.PLAIN, 12);
+ FontFaceNode faceNode = new FontFaceNode(testFont);
+ assertEquals("Dialog.plain", faceNode.toString());
+ }
+}
diff --git a/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java b/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java
new file mode 100644
index 000000000..1341f2343
--- /dev/null
+++ b/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java
@@ -0,0 +1,70 @@
+package fontTests;
+
+
+import org.jhotdraw.gui.JFontChooser;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionListener;
+
+import static org.junit.Assert.*;
+
+public class JFontChooserTests {
+
+ @Mock
+ private ActionListener listener;
+ private Font font1;
+ private Font font2;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+
+ @Test
+ public void addAndRemoveActionListener() {
+ JFontChooser fontChooser = new JFontChooser();
+
+ fontChooser.addActionListener(listener);
+ assertEquals(1, fontChooser.getListeners(ActionListener.class).length);
+ fontChooser.removeActionListener(listener);
+ assertEquals(0, fontChooser.getListeners(ActionListener.class).length);
+
+
+ }
+
+ // Test that font is always chosen. One font must always be chosen, that is the invariant.
+ @Test
+ public void chooseFont() {
+ JFontChooser fontChooser = new JFontChooser();
+
+ fontChooser.setSelectedFont(font1);
+ assertEquals(font1, fontChooser.getSelectedFont());
+
+ fontChooser.setSelectedFont(font2);
+ assertEquals(font2, fontChooser.getSelectedFont());
+ }
+
+ @Test
+ public void chooseFakeFont() {
+ JFontChooser fontChooser = new JFontChooser();
+
+ Font font3 = new Font("BOGUSA AND FAKE FONT, NOT REAL 1293129391239###!!!!", Font.ITALIC, 999999999);
+
+ fontChooser.setSelectedFont(font1);
+ assertEquals(font1, fontChooser.getSelectedFont());
+
+ fontChooser.setSelectedFont(font3);
+ assertEquals(font1, fontChooser.getSelectedFont());
+
+ }
+
+
+}
From 0498598ef21ef21d3ad9d18d6ec9c93e1bdf4c78 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Thu, 13 Nov 2025 13:18:32 +0100
Subject: [PATCH 15/20] Update maven.yml
---
.github/workflows/maven.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 7594fb495..34cf88641 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -23,6 +23,7 @@ jobs:
permissions:
contents: read
packages: write
+ statuses: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From e19c60be1731e012ebdd0d82f599fb42165bd2f6 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
<143800398+Benjamand@users.noreply.github.com>
Date: Thu, 13 Nov 2025 13:34:51 +0100
Subject: [PATCH 16/20] Update maven.yml
---
.github/workflows/maven.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 34cf88641..aca31eb18 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -17,7 +17,7 @@ on:
jobs:
build:
-
+ name: Java CI with Maven
runs-on: ubuntu-latest
permissions:
From 7f07bfe39cb1fca5c1633a073fdb50757b2ffdf7 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
Date: Sat, 15 Nov 2025 21:02:35 +0100
Subject: [PATCH 17/20] BDD tests added to fontchooser
---
jhotdraw-gui/pom.xml | 28 ++++++++++++++++++-
.../src/main/test/fontTests/BDDTests.java | 20 +++++++++++++
.../src/main/test/fontTests/GivenFont.java | 21 ++++++++++++++
.../main/test/fontTests/ThenFontChosen.java | 26 +++++++++++++++++
.../main/test/fontTests/WhenChoosingFont.java | 23 +++++++++++++++
pom.xml | 5 ++--
6 files changed, 119 insertions(+), 4 deletions(-)
create mode 100644 jhotdraw-gui/src/main/test/fontTests/BDDTests.java
create mode 100644 jhotdraw-gui/src/main/test/fontTests/GivenFont.java
create mode 100644 jhotdraw-gui/src/main/test/fontTests/ThenFontChosen.java
create mode 100644 jhotdraw-gui/src/main/test/fontTests/WhenChoosingFont.java
diff --git a/jhotdraw-gui/pom.xml b/jhotdraw-gui/pom.xml
index b7a95e934..db25dd969 100644
--- a/jhotdraw-gui/pom.xml
+++ b/jhotdraw-gui/pom.xml
@@ -38,21 +38,47 @@
org.mockito
mockito-core
- 3.6.28
+ 5.12.0
test
+
org.junit.jupiter
junit-jupiter
RELEASE
test
+
org.junit.jupiter
junit-jupiter
RELEASE
test
+
+
+
+ com.tngtech.jgiven
+ jgiven-junit5
+ 1.2.0
+ test
+
+
+
+ com.tngtech.jgiven
+ jgiven-core
+ 1.2.0
+ test
+
+
+
+ org.assertj
+ assertj-core
+ 3.24.2
+ test
+
+
+
diff --git a/jhotdraw-gui/src/main/test/fontTests/BDDTests.java b/jhotdraw-gui/src/main/test/fontTests/BDDTests.java
new file mode 100644
index 000000000..70b5fd7d1
--- /dev/null
+++ b/jhotdraw-gui/src/main/test/fontTests/BDDTests.java
@@ -0,0 +1,20 @@
+package fontTests;
+
+
+import com.tngtech.jgiven.junit5.ScenarioTest;
+import org.junit.jupiter.api.Test;
+
+import java.awt.*;
+
+public class BDDTests extends ScenarioTest {
+
+ private Font font1 = new Font("Arial", Font.PLAIN, 12);
+
+ @Test
+ public void the_font_should_be_chosen() {
+ given().the_font();
+ when().choosing_font(font1);
+ then().the_font_should_be_chosen(font1);
+ }
+
+}
diff --git a/jhotdraw-gui/src/main/test/fontTests/GivenFont.java b/jhotdraw-gui/src/main/test/fontTests/GivenFont.java
new file mode 100644
index 000000000..3da855b78
--- /dev/null
+++ b/jhotdraw-gui/src/main/test/fontTests/GivenFont.java
@@ -0,0 +1,21 @@
+package fontTests;
+
+import com.tngtech.jgiven.Stage;
+import com.tngtech.jgiven.annotation.ProvidedScenarioState;
+import org.jhotdraw.gui.JFontChooser;
+
+import java.awt.*;
+
+public class GivenFont extends Stage {
+
+ @ProvidedScenarioState
+ JFontChooser fontChooser;
+ public Font font;
+
+ public GivenFont the_font() {
+ fontChooser = new JFontChooser();
+ font = new Font("Arial", Font.PLAIN, 12);
+ return self();
+ }
+
+}
diff --git a/jhotdraw-gui/src/main/test/fontTests/ThenFontChosen.java b/jhotdraw-gui/src/main/test/fontTests/ThenFontChosen.java
new file mode 100644
index 000000000..50fea3822
--- /dev/null
+++ b/jhotdraw-gui/src/main/test/fontTests/ThenFontChosen.java
@@ -0,0 +1,26 @@
+package fontTests;
+
+import com.tngtech.jgiven.Stage;
+
+
+import com.tngtech.jgiven.annotation.ExpectedScenarioState;
+import org.jhotdraw.gui.JFontChooser;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.awt.*;
+
+public class ThenFontChosen extends Stage {
+
+ @ExpectedScenarioState
+ Font font;
+ @ExpectedScenarioState
+ JFontChooser fontChooser;
+ public ThenFontChosen the_font_should_be_chosen(Font font) {
+ assertThat(fontChooser.getFont()).isEqualTo(font);
+ return this;
+ }
+
+
+}
diff --git a/jhotdraw-gui/src/main/test/fontTests/WhenChoosingFont.java b/jhotdraw-gui/src/main/test/fontTests/WhenChoosingFont.java
new file mode 100644
index 000000000..6fc4224f1
--- /dev/null
+++ b/jhotdraw-gui/src/main/test/fontTests/WhenChoosingFont.java
@@ -0,0 +1,23 @@
+package fontTests;
+
+import com.tngtech.jgiven.Stage;
+
+
+import com.tngtech.jgiven.annotation.ExpectedScenarioState;
+
+import org.jhotdraw.gui.JFontChooser;
+
+
+import java.awt.*;
+
+public class WhenChoosingFont extends Stage {
+ @ExpectedScenarioState
+ JFontChooser fontChooser;
+ Font font;
+
+ public WhenChoosingFont choosing_font(Font font) {
+ this.font = font;
+ fontChooser.setFont(font);
+ return self();
+ }
+}
diff --git a/pom.xml b/pom.xml
index ab56a90cc..1063c3941 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,9 +22,8 @@
- github
- GitHub external Packages
- https://maven.pkg.github.com/sweat-tek/MavenRepository
+ central
+ https://repo.maven.apache.org/maven2
From 40a3769e2d7933587485bfaa10e686663ddcc6c4 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
Date: Tue, 18 Nov 2025 13:11:36 +0100
Subject: [PATCH 18/20] DefaultFontChooserModel refactor and tests for it.
---
jhotdraw-gui/pom.xml | 10 +-
.../fontchooser/DefaultFontChooserModel.java | 482 +++---------------
.../jhotdraw/gui/fontchooser/FontConfig.java | 18 +
.../gui/fontchooser/FontConfigLoader.java | 20 +
.../jhotdraw/gui/fontchooser/FontGroup.java | 36 ++
.../org/jhotdraw/font/font-groups.yml | 373 ++++++++++++++
.../DefaultFontChooserModelTests.java | 12 +-
.../test/fontTests/FontFaceNodeTests.java | 12 +-
.../test/fontTests/JFontChooserTests.java | 22 +-
9 files changed, 549 insertions(+), 436 deletions(-)
create mode 100644 jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontConfig.java
create mode 100644 jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontConfigLoader.java
create mode 100644 jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontGroup.java
create mode 100644 jhotdraw-gui/src/main/resources/org/jhotdraw/font/font-groups.yml
diff --git a/jhotdraw-gui/pom.xml b/jhotdraw-gui/pom.xml
index db25dd969..0539eff72 100644
--- a/jhotdraw-gui/pom.xml
+++ b/jhotdraw-gui/pom.xml
@@ -49,6 +49,12 @@
test
+
+ org.yaml
+ snakeyaml
+ 2.2
+
+
org.junit.jupiter
junit-jupiter
@@ -86,8 +92,8 @@
org.apache.maven.plugins
maven-compiler-plugin
- 9
- 9
+ 11
+ 11
diff --git a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/DefaultFontChooserModel.java b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/DefaultFontChooserModel.java
index 237443e7b..d21f20060 100644
--- a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/DefaultFontChooserModel.java
+++ b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/DefaultFontChooserModel.java
@@ -8,9 +8,12 @@
package org.jhotdraw.gui.fontchooser;
import java.awt.*;
+import java.io.InputStream;
import java.util.*;
+import java.util.List;
import javax.swing.tree.*;
import org.jhotdraw.util.ResourceBundleUtil;
+import org.yaml.snakeyaml.Yaml;
/**
* DefaultFontChooserModel with a predefined set of font collections.
@@ -40,12 +43,17 @@ public class DefaultFontChooserModel extends AbstractFontChooserModel {
*/
protected DefaultMutableTreeNode root;
+ FontConfig config;
+
+
public DefaultFontChooserModel() {
root = new DefaultMutableTreeNode();
+ config = FontConfigLoader.loadFontConfig();
}
public DefaultFontChooserModel(Font[] fonts) {
root = new DefaultMutableTreeNode();
+ config = FontConfigLoader.loadFontConfig();
setFonts(fonts);
}
@@ -59,7 +67,50 @@ public DefaultFontChooserModel(Font[] fonts) {
@SuppressWarnings("unchecked")
public void setFonts(Font[] fonts) {
ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.gui.Labels");
- // collect families and sort them alphabetically
+ ArrayList families = getFamilyNodes(fonts, labels);
+ addOtherFontFamilies(labels, families);
+ fireTreeStructureChanged(this, new TreePath(root));
+ }
+
+ private void addOtherFontFamilies(ResourceBundleUtil labels, ArrayList families) {
+ FontCollectionNode others = new FontCollectionNode(labels.getString("FontCollection.other"));
+ HashSet otherFamilySet = new HashSet<>();
+ otherFamilySet.addAll(families);
+ for (int i = 1, n = root.getChildCount(); i < n; i++) {
+ FontCollectionNode fcn = (FontCollectionNode) root.getChildAt(i);
+ for (FontFamilyNode ffn : fcn.families()) {
+ otherFamilySet.remove(ffn);
+ }
+ }
+ ArrayList otherFamilies = new ArrayList<>();
+ for (FontFamilyNode ffn : otherFamilySet) {
+ otherFamilies.add(ffn.clone());
+ }
+ Collections.sort(otherFamilies);
+ others.addAll(otherFamilies);
+ root.add(others);
+ }
+
+ private ArrayList getFamilyNodes(Font[] fonts, ResourceBundleUtil labels) {
+ ArrayList families = getFontFamilyNodes(fonts, labels);
+ for (var entry : config.getGroups().entrySet()) {
+ String groupKey = entry.getKey();
+ FontGroup group = entry.getValue();
+
+ List fontNames = group.getFonts();
+ String labelKey = group.getLabel();
+
+ root.add(
+ new FontCollectionNode(
+ labels.getString(labelKey),
+ collectFamiliesNamed(families, fontNames)
+ )
+ );
+ }
+ return families;
+ }
+
+ private ArrayList getFontFamilyNodes(Font[] fonts, ResourceBundleUtil labels) {
ArrayList families = new ArrayList<>();
HashMap familyMap = new HashMap<>();
for (Font f : fonts) {
@@ -78,432 +129,13 @@ public void setFonts(Font[] fonts) {
// group families into collections
root.removeAllChildren();
root.add(new FontCollectionNode(labels.getString("FontCollection.allFonts"), (ArrayList) families.clone()));
- // Web-save fonts
- root.add(
- new FontCollectionNode(labels.getString("FontCollection.web"), collectFamiliesNamed(families,
- "Arial",
- "Arial Black",
- "Comic Sans MS",
- "Georgia",
- "Impact",
- "Times New Roman",
- "Trebuchet MS",
- "Verdana",
- "Webdings")));
- /*
- // PDF Fonts
- root.add(
- new FontCollectionNode(labels.getString("FontCollection.pdf"), collectFamiliesNamed(families,
- "Andale Mono",
- "Courier",
- "Helvetica",
- "Symbol",
- "Times",
- "Zapf Dingbats")));
- */
- // Java System fonts
- root.add(
- new FontCollectionNode(labels.getString("FontCollection.system"), collectFamiliesNamed(families,
- "Dialog",
- "DialogInput",
- "Monospaced",
- "SansSerif",
- "Serif")));
- // Serif fonts
- root.add(
- new FontCollectionNode(labels.getString("FontCollection.serif"), collectFamiliesNamed(families,
- // Fonts on Mac OS X 10.5:
- "Adobe Caslon Pro",
- "Adobe Garamond Pro",
- "American Typewriter",
- "Arno Pro",
- "Baskerville",
- "Baskerville Old Face",
- "Bell MT",
- "Big Caslon",
- "Bodoni SvtyTwo ITC TT",
- "Bodoni SvtyTwo OS ITC TT",
- "Bodoni SvtyTwo SC ITC TT",
- "Book Antiqua",
- "Bookman Old Style",
- "Calisto MT",
- "Chaparral Pro",
- "Century",
- "Century Schoolbook",
- "Cochin",
- "Footlight MT Light",
- "Garamond",
- "Garamond Premier Pro",
- "Georgia",
- "Goudy Old Style",
- "Hoefler Text",
- "Lucida Bright",
- "Lucida Fax",
- "Minion Pro",
- "Palatino",
- "Times",
- "Times New Roman",
- // Fonts on Mac OS X 10.6:
- "Didot",
- // Fonts on Windows XP:
- "Palatino Linotype",
- "Bitstream Vera Serif Bold",
- "Bodoni MT",
- "Bodoni MT Black",
- "Bodoni MT Condensed",
- "Californian FB",
- "Cambria",
- "Cambria Math",
- "Centaur",
- "Constantia",
- "High Tower Text",
- "Perpetua",
- "Poor Richard",
- "Rockwell Condensed",
- "Slimbach-Black",
- "Slimbach-BlackItalic",
- "Slimbach-Bold",
- "Slimbach-BoldItalic",
- "Slimbach-Book",
- "Slimbach-BookItalic",
- "Slimbach-Medium",
- "Slimbach-MediumItalic",
- "Sylfaen",
- // Fonts on Windows Vista
- "Andalus",
- "Angsana New",
- "AngsanaUPC",
- "Arabic Typesetting",
- "Cambria",
- "Cambria Math",
- "Constantia",
- "DaunPenh",
- "David",
- "DilleniaUPC",
- "EucrosiaUPC",
- "Frank Ruehl",
- "IrisUPC",
- "Iskoola Pota",
- "JasmineUPC",
- "KodchiangUPC",
- "Narkisim")));
- // Sans Serif
- root.add(
- new FontCollectionNode(labels.getString("FontCollection.sansSerif"), collectFamiliesNamed(families,
- // Fonts on Mac OS X 10.5:
- "Abadi MT Condensed Extra Bold",
- "Abadi MT Condensed Light",
- "AppleGothic",
- "Arial",
- "Arial Black",
- "Arial Narrow",
- "Arial Rounded MT Bold",
- "Arial Unicode MS",
- "Bell Gothic Std",
- "Blair MdITC TT",
- "Century Gothic",
- "Frutiger",
- "Futura",
- "Geneva",
- "Gill Sans",
- "Gulim",
- "Helvetica",
- "Helvetica Neue",
- "Lucida Grande",
- "Lucida Sans",
- "Microsoft Sans Serif",
- "Myriad Pro",
- "News Gothic",
- "Tahoma",
- "Trebuchet MS",
- "Verdana",
- // Fonts on Mac OS X 10.6:
- "Charcoal",
- "Euphemia UCAS",
- // Fonts on Windows XP:
- "Franklin Gothic Medium",
- "Lucida Sans Unicode",
- "Agency FB",
- "Berlin Sans FB",
- "Berlin Sans FB Demi Bold",
- "Bitstream Vera Sans Bold",
- "Calibri",
- "Candara",
- "Corbel",
- "Estrangelo Edessa",
- "Eras Bold ITC",
- "Eras Demi ITC",
- "Eras Light ITC",
- "Eras Medium ITC",
- "Franklin Gothic Book",
- "Franklin Gothic Demi",
- "Franklin Gothic Demi Cond",
- "Franklin Gothic Heavy",
- "Franklin Gothic Medium Cond",
- "Gill Sans MT",
- "Gill Sans MT Condensed",
- "Gill Sans MT Ext Condensed Bold",
- "Maiandra GD",
- "MS Reference Sans...",
- "Tw Cen MT",
- "Tw Cen MT Condensed",
- "Tw Cen MT Condensed Extra Bold",
- // Fonts on Windows Vista:
- "Aharoni",
- "Browallia New",
- "BrowalliaUPC",
- "Calibri",
- "Candara",
- "Corbel",
- "Cordia New",
- "CordiaUPC",
- "DokChampa",
- "Dotum",
- "Estrangelo Edessa",
- "Euphemia",
- "Freesia UPC",
- "Gautami",
- "Gisha",
- "Kalinga",
- "Kartika",
- "Levenim MT",
- "LilyUPC",
- "Malgun Gothic",
- "Meiryo",
- "Miriam",
- "Segoe UI")));
- // Scripts
- root.add(
- new FontCollectionNode(labels.getString("FontCollection.script"), collectFamiliesNamed(families,
- // Fonts on Mac OS X 10.5:
- "Apple Chancery",
- "Bickham Script Pro",
- "Blackmoor LET",
- "Bradley Hand ITC TT",
- "Brush Script MT",
- "Brush Script Std",
- "Chalkboard",
- "Charlemagne Std",
- "Comic Sans MS",
- "Curlz MT",
- "Edwardian Script ITC",
- "Footlight MT Light",
- "Giddyup Std",
- "Handwriting - Dakota",
- "Harrington",
- "Herculanum",
- "Kokonor",
- "Lithos Pro",
- "Lucida Blackletter",
- "Lucida Calligraphy",
- "Lucida Handwriting",
- "Marker Felt",
- "Matura MT Script Capitals",
- "Mistral",
- "Monotype Corsiva",
- "Party LET",
- "Papyrus",
- "Santa Fe LET",
- "Savoye LET",
- "SchoolHouse Cursive B",
- "SchoolHouse Printed A",
- "Skia",
- "Snell Roundhand",
- "Tekton Pro",
- "Trajan Pro",
- "Zapfino",
- // Fonts on Mac OS X 10.6:
- "Casual",
- "Chalkduster",
- // Fonts on Windows XP:
- "Blackadder ITC",
- "Bradley Hand ITC",
- "Chiller",
- "Freestyle Script",
- "French Script MT",
- "Gigi",
- "Harlow Solid Italic",
- "Informal Roman",
- "Juice ITC",
- "Kristen ITC",
- "Kunstler Script",
- "Magneto Bold",
- "Maiandra GD",
- "Old English Text",
- "Palace Script MT",
- "Parchment",
- "Pristina",
- "Rage Italic",
- "Ravie",
- "Script MT Bold",
- "Tempus Sans ITC",
- "Viner Hand ITC",
- "Vivaldi Italic",
- "Vladimir Script",
- // Fonts on Windows Vista
- "Segoe Print",
- "Segoe Script")));
- // Monospaced
- root.add(
- new FontCollectionNode(labels.getString("FontCollection.monospaced"), collectFamiliesNamed(families,
- // Fonts on Mac OS X 10.5:
- "Andale Mono",
- "Courier",
- "Courier New",
- "Letter Gothic Std",
- "Lucida Sans Typewriter",
- "Monaco",
- "OCR A Std",
- "Orator Std",
- "Prestige Elite Std",
- // Fonts on Mac OS X 10.6:
- "Menlo",
- // Fonts on Windows XP:
- "Lucida Console",
- "Bitstream Vera S...",
- "Consolas",
- "OCR A Extended",
- "OCR B",
- // Fonts on Windows Vista
- "Consolas",
- "DotumChe",
- "Miriam Fixed",
- "Rod")));
- // Decorative
- root.add(
- new FontCollectionNode(labels.getString("FontCollection.decorative"), collectFamiliesNamed(families,
- // Fonts on Mac OS X 10.5:
- "Academy Engraved LET",
- "Arial Black",
- "Bank Gothic",
- "Bauhaus 93",
- "Bernard MT Condensed",
- "Birch Std",
- "Blackoak Std",
- "BlairMdITC TT",
- "Bordeaux Roman Bold LET",
- "Braggadocio",
- "Britannic Bold",
- "Capitals",
- "Colonna MT",
- "Cooper Black",
- "Cooper Std",
- "Copperplate",
- "Copperplate Gothic Bold",
- "Copperplate Gothic Light",
- "Cracked",
- "Desdemona",
- "Didot",
- "Eccentric Std",
- "Engravers MT",
- "Eurostile",
- "Gill Sans Ultra Bold",
- "Gloucester MT Extra Condensed",
- "Haettenschweiler",
- "Hobo Std",
- "Impact",
- "Imprint MT Shadow",
- "Jazz LET",
- "Kino MT",
- "Matura MT Script Capitals",
- "Mesquite Std",
- "Modern No. 20",
- "Mona Lisa Solid ITC TT",
- "MS Gothic",
- "Nueva Std",
- "Onyx",
- "Optima",
- "Perpetua Titling MT",
- "Playbill",
- "Poplar Std",
- "PortagoITC TT",
- "Princetown LET",
- "Rockwell",
- "Rockwell Extra Bold",
- "Rosewood Std",
- "Santa Fe LET",
- "Stencil",
- "Stencil Std",
- "Stone Sans ITC TT",
- "Stone Sans OS ITC TT",
- "Stone Sans Sem ITC TT",
- "Stone Sans Sem OS ITCTT",
- "Stone Sans Sem OS ITC TT",
- "Synchro LET",
- "Wide Latin",
- // Fonts on Mac OS X 10.5:
- "HeadLineA",
- // Fonts on Windows XP:
- "Algerian",
- "Bodoni MT Black",
- "Bodoni MT Poster Compressed",
- "Broadway",
- "Castellar",
- "Elephant",
- "Felix Titling",
- "Franklin Gothic Heavy",
- "Gill Sans MT Ext Condensed Bold",
- "Gill Sans Ultra Bold Condensed",
- "Goudy Stout",
- "Jokerman",
- "Juice ITC",
- "Magneto",
- "Magneto Bold",
- "Niagara Engraved",
- "Niagara Solid",
- "Poor Richard",
- "Ravie",
- "Rockwell Condensed",
- "Showcard Gothic",
- "Slimbach-Black",
- "Slimbach-BlackItalic",
- "Snap ITC" // Fonts on Windows Vista:
- )));
- root.add(
- new FontCollectionNode(labels.getString("FontCollection.symbols"), collectFamiliesNamed(families,
- // Fonts on Mac OS X 10.5:
- "Apple Symbols",
- "Blackoack Std",
- "Bodoni Ornaments ITC TT",
- "EuropeanPi",
- "Monotype Sorts",
- "MT Extra",
- "Symbol",
- "Type Embellishments One LET",
- "Webdings",
- "Wingdings",
- "Wingdings 2",
- "Wingdings 3",
- "Zapf Dingbats",
- // Fonts on Windows XP:
- "Bookshelf Symbol"
- // Fonts on Windows Vista:
- )));
- // Collect font families, which are not in one of the other collections
- // (except the collection AllFonts).
- FontCollectionNode others = new FontCollectionNode(labels.getString("FontCollection.other"));
- HashSet otherFamilySet = new HashSet<>();
- otherFamilySet.addAll(families);
- for (int i = 1, n = root.getChildCount(); i < n; i++) {
- FontCollectionNode fcn = (FontCollectionNode) root.getChildAt(i);
- for (FontFamilyNode ffn : fcn.families()) {
- otherFamilySet.remove(ffn);
- }
- }
- ArrayList otherFamilies = new ArrayList<>();
- for (FontFamilyNode ffn : otherFamilySet) {
- otherFamilies.add(ffn.clone());
- }
- Collections.sort(otherFamilies);
- others.addAll(otherFamilies);
- root.add(others);
- fireTreeStructureChanged(this, new TreePath(root));
+ return families;
}
- protected ArrayList collectFamiliesNamed(ArrayList families, String... names) {
+ protected ArrayList collectFamiliesNamed(ArrayList families, List names) {
ArrayList coll = new ArrayList<>();
HashSet nameMap = new HashSet<>();
- nameMap.addAll(Arrays.asList(names));
+ nameMap.addAll(names);
for (FontFamilyNode family : families) {
String fName = family.getName();
if (nameMap.contains(family.getName())) {
@@ -561,6 +193,10 @@ public int getIndexOfChild(Object parent, Object child) {
return ((TreeNode) parent).getIndex((TreeNode) child);
}
+ public FontConfig getConfig() {
+ return config;
+ }
+
public static class UIResource extends DefaultFontChooserModel implements javax.swing.plaf.UIResource {
}
}
diff --git a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontConfig.java b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontConfig.java
new file mode 100644
index 000000000..2c7898bac
--- /dev/null
+++ b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontConfig.java
@@ -0,0 +1,18 @@
+package org.jhotdraw.gui.fontchooser;
+
+import java.util.Map;
+
+public class FontConfig {
+ private Map groups;
+
+ public FontConfig() {
+ }
+
+ public Map getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Map groups) {
+ this.groups = groups;
+ }
+}
diff --git a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontConfigLoader.java b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontConfigLoader.java
new file mode 100644
index 000000000..d2d8469d3
--- /dev/null
+++ b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontConfigLoader.java
@@ -0,0 +1,20 @@
+package org.jhotdraw.gui.fontchooser;
+
+import org.yaml.snakeyaml.Yaml;
+
+import java.awt.*;
+import java.io.InputStream;
+
+public class FontConfigLoader {
+ public static FontConfig loadFontConfig() {
+ InputStream in = FontConfig.class.getResourceAsStream("/org/jhotdraw/font/font-groups.yml");
+
+ if (in == null) {
+ throw new IllegalStateException("font-groups.yml not found");
+ }
+
+ Yaml yaml = new Yaml();
+ FontConfig config = yaml.loadAs(in, FontConfig.class);
+ return config;
+ }
+}
diff --git a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontGroup.java b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontGroup.java
new file mode 100644
index 000000000..2bb903345
--- /dev/null
+++ b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/fontchooser/FontGroup.java
@@ -0,0 +1,36 @@
+package org.jhotdraw.gui.fontchooser;
+
+import java.util.List;
+
+public class FontGroup {
+
+ String label;
+ List listOfFonts;
+
+ public FontGroup() {
+ }
+
+
+ public FontGroup(String labelKey, List listOfFonts) {
+ this.label = labelKey;
+ this.listOfFonts = listOfFonts;
+ }
+
+ public String getLabel() {
+ return label;
+
+ }
+
+ public void setLabel(String labelKey) {
+ this.label = labelKey;
+ }
+
+ public List getFonts() {
+ return listOfFonts;
+ }
+
+ public void setFonts(List listOfFonts) {
+ this.listOfFonts = listOfFonts;
+ }
+
+}
diff --git a/jhotdraw-gui/src/main/resources/org/jhotdraw/font/font-groups.yml b/jhotdraw-gui/src/main/resources/org/jhotdraw/font/font-groups.yml
new file mode 100644
index 000000000..71e2010c7
--- /dev/null
+++ b/jhotdraw-gui/src/main/resources/org/jhotdraw/font/font-groups.yml
@@ -0,0 +1,373 @@
+groups:
+
+ allFonts:
+ label: FontCollection.allFonts
+ fonts: []
+
+ web:
+ label: FontCollection.web
+ fonts:
+ - Arial
+ - Arial Black
+ - Comic Sans MS
+ - Georgia
+ - Impact
+ - Times New Roman
+ - Trebuchet MS
+ - Verdana
+ - Webdings
+
+ system:
+ label: FontCollection.system
+ fonts:
+ - Dialog
+ - DialogInput
+ - Monospaced
+ - SansSerif
+ - Serif
+
+ serif:
+ label: FontCollection.serif
+ fonts:
+ - Adobe Caslon Pro
+ - Adobe Garamond Pro
+ - American Typewriter
+ - Arno Pro
+ - Baskerville
+ - Baskerville Old Face
+ - Bell MT
+ - Big Caslon
+ - Bodoni SvtyTwo ITC TT
+ - Bodoni SvtyTwo OS ITC TT
+ - Bodoni SvtyTwo SC ITC TT
+ - Book Antiqua
+ - Bookman Old Style
+ - Calisto MT
+ - Chaparral Pro
+ - Century
+ - Century Schoolbook
+ - Cochin
+ - Footlight MT Light
+ - Garamond
+ - Garamond Premier Pro
+ - Georgia
+ - Goudy Old Style
+ - Hoefler Text
+ - Lucida Bright
+ - Lucida Fax
+ - Minion Pro
+ - Palatino
+ - Times
+ - Times New Roman
+ - Didot
+ - Palatino Linotype
+ - Bitstream Vera Serif Bold
+ - Bodoni MT
+ - Bodoni MT Black
+ - Bodoni MT Condensed
+ - Californian FB
+ - Cambria
+ - Cambria Math
+ - Centaur
+ - Constantia
+ - High Tower Text
+ - Perpetua
+ - Poor Richard
+ - Rockwell Condensed
+ - Slimbach-Black
+ - Slimbach-BlackItalic
+ - Slimbach-Bold
+ - Slimbach-BoldItalic
+ - Slimbach-Book
+ - Slimbach-BookItalic
+ - Slimbach-Medium
+ - Slimbach-MediumItalic
+ - Sylfaen
+ - Andalus
+ - Angsana New
+ - AngsanaUPC
+ - Arabic Typesetting
+ - DaunPenh
+ - David
+ - DilleniaUPC
+ - EucrosiaUPC
+ - Frank Ruehl
+ - IrisUPC
+ - Iskoola Pota
+ - JasmineUPC
+ - KodchiangUPC
+ - Narkisim
+
+ sansSerif:
+ label: FontCollection.sansSerif
+ fonts:
+ - Abadi MT Condensed Extra Bold
+ - Abadi MT Condensed Light
+ - AppleGothic
+ - Arial
+ - Arial Black
+ - Arial Narrow
+ - Arial Rounded MT Bold
+ - Arial Unicode MS
+ - Bell Gothic Std
+ - Blair MdITC TT
+ - Century Gothic
+ - Frutiger
+ - Futura
+ - Geneva
+ - Gill Sans
+ - Gulim
+ - Helvetica
+ - Helvetica Neue
+ - Lucida Grande
+ - Lucida Sans
+ - Microsoft Sans Serif
+ - Myriad Pro
+ - News Gothic
+ - Tahoma
+ - Trebuchet MS
+ - Verdana
+ - Charcoal
+ - Euphemia UCAS
+ - Franklin Gothic Medium
+ - Lucida Sans Unicode
+ - Agency FB
+ - Berlin Sans FB
+ - Berlin Sans FB Demi Bold
+ - Bitstream Vera Sans Bold
+ - Calibri
+ - Candara
+ - Corbel
+ - Estrangelo Edessa
+ - Eras Bold ITC
+ - Eras Demi ITC
+ - Eras Light ITC
+ - Eras Medium ITC
+ - Franklin Gothic Book
+ - Franklin Gothic Demi
+ - Franklin Gothic Demi Cond
+ - Franklin Gothic Heavy
+ - Franklin Gothic Medium Cond
+ - Gill Sans MT
+ - Gill Sans MT Condensed
+ - Gill Sans MT Ext Condensed Bold
+ - Maiandra GD
+ - MS Reference Sans...
+ - Tw Cen MT
+ - Tw Cen MT Condensed
+ - Tw Cen MT Condensed Extra Bold
+ - Aharoni
+ - Browallia New
+ - BrowalliaUPC
+ - Cordia New
+ - CordiaUPC
+ - DokChampa
+ - Dotum
+ - Euphemia
+ - Freesia UPC
+ - Gautami
+ - Gisha
+ - Kalinga
+ - Kartika
+ - Levenim MT
+ - LilyUPC
+ - Malgun Gothic
+ - Meiryo
+ - Miriam
+ - Segoe UI
+
+ script:
+ label: FontCollection.script
+ fonts:
+ - Apple Chancery
+ - Bickham Script Pro
+ - Blackmoor LET
+ - Bradley Hand ITC TT
+ - Brush Script MT
+ - Brush Script Std
+ - Chalkboard
+ - Charlemagne Std
+ - Comic Sans MS
+ - Curlz MT
+ - Edwardian Script ITC
+ - Footlight MT Light
+ - Giddyup Std
+ - Handwriting - Dakota
+ - Harrington
+ - Herculanum
+ - Kokonor
+ - Lithos Pro
+ - Lucida Blackletter
+ - Lucida Calligraphy
+ - Lucida Handwriting
+ - Marker Felt
+ - Matura MT Script Capitals
+ - Mistral
+ - Monotype Corsiva
+ - Party LET
+ - Papyrus
+ - Santa Fe LET
+ - Savoye LET
+ - SchoolHouse Cursive B
+ - SchoolHouse Printed A
+ - Skia
+ - Snell Roundhand
+ - Tekton Pro
+ - Trajan Pro
+ - Zapfino
+ - Casual
+ - Chalkduster
+ - Blackadder ITC
+ - Bradley Hand ITC
+ - Chiller
+ - Freestyle Script
+ - French Script MT
+ - Gigi
+ - Harlow Solid Italic
+ - Informal Roman
+ - Juice ITC
+ - Kristen ITC
+ - Kunstler Script
+ - Magneto Bold
+ - Maiandra GD
+ - Old English Text
+ - Palace Script MT
+ - Parchment
+ - Pristina
+ - Rage Italic
+ - Ravie
+ - Script MT Bold
+ - Tempus Sans ITC
+ - Viner Hand ITC
+ - Vivaldi Italic
+ - Vladimir Script
+ - Segoe Print
+ - Segoe Script
+
+ monospaced:
+ label: FontCollection.monospaced
+ fonts:
+ - Andale Mono
+ - Courier
+ - Courier New
+ - Letter Gothic Std
+ - Lucida Sans Typewriter
+ - Monaco
+ - OCR A Std
+ - Orator Std
+ - Prestige Elite Std
+ - Menlo
+ - Lucida Console
+ - Bitstream Vera S...
+ - Consolas
+ - OCR A Extended
+ - OCR B
+ - DotumChe
+ - Miriam Fixed
+ - Rod
+
+ decorative:
+ label: FontCollection.decorative
+ fonts:
+ - Academy Engraved LET
+ - Arial Black
+ - Bank Gothic
+ - Bauhaus 93
+ - Bernard MT Condensed
+ - Birch Std
+ - Blackoak Std
+ - BlairMdITC TT
+ - Bordeaux Roman Bold LET
+ - Braggadocio
+ - Britannic Bold
+ - Capitals
+ - Colonna MT
+ - Cooper Black
+ - Cooper Std
+ - Copperplate
+ - Copperplate Gothic Bold
+ - Copperplate Gothic Light
+ - Cracked
+ - Desdemona
+ - Didot
+ - Eccentric Std
+ - Engravers MT
+ - Eurostile
+ - Gill Sans Ultra Bold
+ - Gloucester MT Extra Condensed
+ - Haettenschweiler
+ - Hobo Std
+ - Impact
+ - Imprint MT Shadow
+ - Jazz LET
+ - Kino MT
+ - Matura MT Script Capitals
+ - Mesquite Std
+ - Modern No. 20
+ - Mona Lisa Solid ITC TT
+ - MS Gothic
+ - Nueva Std
+ - Onyx
+ - Optima
+ - Perpetua Titling MT
+ - Playbill
+ - Poplar Std
+ - PortagoITC TT
+ - Princetown LET
+ - Rockwell
+ - Rockwell Extra Bold
+ - Rosewood Std
+ - Santa Fe LET
+ - Stencil
+ - Stencil Std
+ - Stone Sans ITC TT
+ - Stone Sans OS ITC TT
+ - Stone Sans Sem ITC TT
+ - Stone Sans Sem OS ITCTT
+ - Stone Sans Sem OS ITC TT
+ - Synchro LET
+ - Wide Latin
+ - HeadLineA
+ - Algerian
+ - Bodoni MT Black
+ - Bodoni MT Poster Compressed
+ - Broadway
+ - Castellar
+ - Elephant
+ - Felix Titling
+ - Franklin Gothic Heavy
+ - Gill Sans MT Ext Condensed Bold
+ - Gill Sans Ultra Bold Condensed
+ - Goudy Stout
+ - Jokerman
+ - Juice ITC
+ - Magneto
+ - Magneto Bold
+ - Niagara Engraved
+ - Niagara Solid
+ - Poor Richard
+ - Ravie
+ - Rockwell Condensed
+ - Showcard Gothic
+ - Slimbach-Black
+ - Slimbach-BlackItalic
+ - Snap ITC
+
+ symbols:
+ label: FontCollection.symbols
+ fonts:
+ - Apple Symbols
+ - Blackoack Std
+ - Bodoni Ornaments ITC TT
+ - EuropeanPi
+ - Monotype Sorts
+ - MT Extra
+ - Symbol
+ - Type Embellishments One LET
+ - Webdings
+ - Wingdings
+ - Wingdings 2
+ - Wingdings 3
+ - Zapf Dingbats
+ - Bookshelf Symbol
+
diff --git a/jhotdraw-gui/src/main/test/fontTests/DefaultFontChooserModelTests.java b/jhotdraw-gui/src/main/test/fontTests/DefaultFontChooserModelTests.java
index 3d5522c83..5333991fc 100644
--- a/jhotdraw-gui/src/main/test/fontTests/DefaultFontChooserModelTests.java
+++ b/jhotdraw-gui/src/main/test/fontTests/DefaultFontChooserModelTests.java
@@ -26,17 +26,21 @@ void setUp() {
@Test
// Invariant test
- // Regardless of which fonts are set, the models root node has exactly 10 children.
+ // Regardless of which fonts are set, the models root node has the amount of children as there are groups in the yaml.
public void setFontsTest() {
DefaultFontChooserModel model = new DefaultFontChooserModel();
+ int expected = 1 + model.getConfig().getGroups().size() + 1;
+
model.setFonts(new Font[] { new Font("Arial", Font.PLAIN, 12) });
- Assertions.assertEquals(10, model.getChildCount(model.getRoot()));
+ Assertions.assertEquals(expected, model.getChildCount(model.getRoot()));
model.setFonts(GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts());
- assertEquals(10, model.getChildCount(model.getRoot()));
+ assertEquals(expected, model.getChildCount(model.getRoot()));
model.setFonts(new Font[] { new Font("Times New Roman", Font.BOLD, 14) });
- assertEquals(10, model.getChildCount(model.getRoot()));
+ assertEquals(expected, model.getChildCount(model.getRoot()));
+
+
}
diff --git a/jhotdraw-gui/src/main/test/fontTests/FontFaceNodeTests.java b/jhotdraw-gui/src/main/test/fontTests/FontFaceNodeTests.java
index a1c4482fe..3b061d66a 100644
--- a/jhotdraw-gui/src/main/test/fontTests/FontFaceNodeTests.java
+++ b/jhotdraw-gui/src/main/test/fontTests/FontFaceNodeTests.java
@@ -16,8 +16,6 @@ public class FontFaceNodeTests {
@Mock
private ActionListener listener;
- private Font font1;
- private Font font2;
@BeforeEach
void setUp() {
@@ -35,8 +33,16 @@ public void beautifyNameTest() {
// Test boundary case scenario for FontFaceNode
@Test
public void beautifyNameTestBoundaryCase() {
- Font testFont = new Font("k213k02991021903901kmdsakd02139smklææææ...l,æææ", Font.PLAIN, 12);
+ Font testFont = new Font("", Font.PLAIN, 12);
FontFaceNode faceNode = new FontFaceNode(testFont);
assertEquals("Dialog.plain", faceNode.toString());
}
+
+ // Invariant test for fontfacenode. Must always be a leaf.
+ @Test
+ public void isLeafTest() {
+ FontFaceNode node = new FontFaceNode(new Font("Arial", 0, 12));
+ assertTrue(node.isLeaf());
+ assertEquals(0, node.getChildCount());
+ }
}
diff --git a/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java b/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java
index 1341f2343..9b10a948b 100644
--- a/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java
+++ b/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java
@@ -12,6 +12,7 @@
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
+import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.*;
@@ -19,8 +20,8 @@ public class JFontChooserTests {
@Mock
private ActionListener listener;
- private Font font1;
- private Font font2;
+ private Font font1 = new Font("Arial", Font.PLAIN, 12);
+ private Font font2 = new Font("Times New Roman", Font.BOLD, 14);
@BeforeEach
void setUp() {
@@ -56,15 +57,28 @@ public void chooseFont() {
public void chooseFakeFont() {
JFontChooser fontChooser = new JFontChooser();
- Font font3 = new Font("BOGUSA AND FAKE FONT, NOT REAL 1293129391239###!!!!", Font.ITALIC, 999999999);
+ Font font3 = new Font("BOGUSA AND FAKE FONT, NOT REAL 1293129391239###!!!!", Font.BOLD, 999999999);
fontChooser.setSelectedFont(font1);
assertEquals(font1, fontChooser.getSelectedFont());
fontChooser.setSelectedFont(font3);
- assertEquals(font1, fontChooser.getSelectedFont());
+ assertEquals(font3, fontChooser.getSelectedFont());
}
+ // Test the invariant: approveSelection should always fire the correct command.
+ @Test
+ public void approveSelectionTest() {
+ JFontChooser chooser = new JFontChooser();
+ AtomicReference command = new AtomicReference<>();
+
+ chooser.addActionListener(e -> command.set(e.getActionCommand()));
+
+ chooser.approveSelection();
+
+ assertEquals(JFontChooser.APPROVE_SELECTION, command.get());
+ }
+
}
From 27619a1cd457253077d6fb02ab7399e1a3756ce1 Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
Date: Wed, 7 Jan 2026 16:54:16 +0100
Subject: [PATCH 19/20] JFontChooser Method extraction and deletion of
redundant code
---
.../java/org/jhotdraw/gui/JFontChooser.java | 152 +++++++++++-------
1 file changed, 92 insertions(+), 60 deletions(-)
diff --git a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/JFontChooser.java b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/JFontChooser.java
index 06f9c5ff5..2f32ffb85 100644
--- a/jhotdraw-gui/src/main/java/org/jhotdraw/gui/JFontChooser.java
+++ b/jhotdraw-gui/src/main/java/org/jhotdraw/gui/JFontChooser.java
@@ -383,15 +383,27 @@ public Font getSelectedFont() {
* selected.
*/
public void setSelectedFont(Font newValue) {
+ if (!isAcceptableFont(newValue)) {
+ return;
+ }
+
Font oldValue = selectedFont;
- Font decoded = Font.decode(newValue.getFontName());
- if (decoded.getFontName().equals(newValue.getFontName()) || decoded.getFontName().endsWith("-Derived")) {
- this.selectedFont = newValue;
- firePropertyChange(SELECTED_FONT_PROPERTY, oldValue, newValue);
- updateSelectionPath(newValue);
- } else {
- return;
- }
+ selectedFont = newValue;
+
+ firePropertyChange(SELECTED_FONT_PROPERTY, oldValue, newValue);
+ updateSelectionPath(newValue);
+ }
+
+ private boolean isAcceptableFont(Font font) {
+ if (font == null) {
+ return false;
+ }
+
+ Font decoded = Font.decode(font.getFontName());
+ String name = font.getFontName();
+
+ return decoded.getFontName().equals(name)
+ || decoded.getFontName().endsWith("-Derived");
}
/**
@@ -403,66 +415,86 @@ public void setSelectedFont(Font newValue) {
* @param newValue
*/
protected void updateSelectionPath(Font newValue) {
- if (newValue == null || selectionPath == null || selectionPath.getPathCount() != 4
- || !((FontFaceNode) selectionPath.getLastPathComponent()).getFont().getFontName().equals(newValue.getFontName())) {
+ if (shouldUpdateSelection(newValue)) {
if (newValue == null) {
setSelectionPath(null);
} else {
- TreePath path = selectionPath;
- FontCollectionNode oldCollection = (path != null && path.getPathCount() > 1) ? (FontCollectionNode) path.getPathComponent(1) : null;
- FontFamilyNode oldFamily = (path != null && path.getPathCount() > 2) ? (FontFamilyNode) path.getPathComponent(2) : null;
- FontFaceNode oldFace = (path != null && path.getPathCount() > 3) ? (FontFaceNode) path.getPathComponent(3) : null;
- FontCollectionNode newCollection = oldCollection;
- FontFamilyNode newFamily = oldFamily;
- FontFaceNode newFace = null;
- // search in the current family
- if (newFace == null && newFamily != null) {
- for (FontFaceNode face : newFamily.faces()) {
- if (face.getFont().getFontName().equals(newValue.getFontName())) {
- newFace = face;
- break;
- }
- }
- }
- // search in the current collection
- if (newFace == null && newCollection != null) {
- for (FontFamilyNode family : newCollection.families()) {
- for (FontFaceNode face : family.faces()) {
- if (face.getFont().getFontName().equals(newValue.getFontName())) {
- newFamily = family;
- newFace = face;
- break;
- }
- }
- }
+ searchFamily(newValue);
+ }
+ }
+ }
+
+ private boolean shouldUpdateSelection(Font newValue) {
+ if (newValue == null || selectionPath == null || selectionPath.getPathCount() != 4) {
+ return true;
+ }
+
+ FontFaceNode selected =
+ (FontFaceNode) selectionPath.getLastPathComponent();
+
+ return !selected.getFont().getFontName()
+ .equals(newValue.getFontName());
+ }
+
+ private void searchFamily(Font newValue) {
+ TreePath path = selectionPath;
+
+ FontCollectionNode collection =
+ (path != null && path.getPathCount() > 1)
+ ? (FontCollectionNode) path.getPathComponent(1)
+ : null;
+
+ FontFamilyNode family =
+ (path != null && path.getPathCount() > 2)
+ ? (FontFamilyNode) path.getPathComponent(2)
+ : null;
+
+ FontFaceNode face = findFaceInFamily(family, newValue);
+
+ if (face == null && collection != null) {
+ for (FontFamilyNode fam : collection.families()) {
+ face = findFaceInFamily(fam, newValue);
+ if (face != null) {
+ family = fam;
+ break;
}
- // search in all collections
- if (newFace == null) {
- TreeNode root = (TreeNode) getModel().getRoot();
- OuterLoop:
- for (int i = 0, n = root.getChildCount(); i < n; i++) {
- FontCollectionNode collection = (FontCollectionNode) root.getChildAt(i);
- for (FontFamilyNode family : collection.families()) {
- for (FontFaceNode face : family.faces()) {
- if (face.getFont().getFontName().equals(newValue.getFontName())) {
- newCollection = collection;
- newFamily = family;
- newFace = face;
- break OuterLoop;
- }
- }
- }
+ }
+ }
+
+ if (face == null) {
+ Object root = getModel().getRoot();
+ for (int i = 0; i < ((TreeNode) root).getChildCount(); i++) {
+ FontCollectionNode col =
+ (FontCollectionNode) ((TreeNode) root).getChildAt(i);
+
+ for (FontFamilyNode fam : col.families()) {
+ face = findFaceInFamily(fam, newValue);
+ if (face != null) {
+ collection = col;
+ family = fam;
+ break;
}
}
- if (newFace != null) {
- setSelectionPath(new TreePath(new Object[]{
- getModel().getRoot(), newCollection, newFamily, newFace
- }));
- } else {
- setSelectionPath(null);
- }
+ if (face != null) break;
+ }
+ }
+
+ setSelectionPath(face != null
+ ? new TreePath(new Object[]{getModel().getRoot(), collection, family, face})
+ : null);
+ }
+
+ private FontFaceNode findFaceInFamily(FontFamilyNode family, Font font) {
+ if (family == null) {
+ return null;
+ }
+
+ for (FontFaceNode face : family.faces()) {
+ if (face.getFont().getFontName().equals(font.getFontName())) {
+ return face;
}
}
+ return null;
}
/**
From ae673311faaa46edabc07ee1fed4ee470584174e Mon Sep 17 00:00:00 2001
From: Benjamin Jacob Langmach Schmidt
Date: Thu, 8 Jan 2026 17:37:02 +0100
Subject: [PATCH 20/20] JFontChooser test added
---
.../test/fontTests/JFontChooserTests.java | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java b/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java
index 9b10a948b..c0802c6c0 100644
--- a/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java
+++ b/jhotdraw-gui/src/main/test/fontTests/JFontChooserTests.java
@@ -2,6 +2,8 @@
import org.jhotdraw.gui.JFontChooser;
+import org.jhotdraw.gui.fontchooser.DefaultFontChooserModel;
+import org.jhotdraw.gui.fontchooser.FontFaceNode;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -10,6 +12,7 @@
import org.mockito.MockitoAnnotations;
import javax.swing.*;
+import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionListener;
import java.util.concurrent.atomic.AtomicReference;
@@ -81,4 +84,27 @@ public void approveSelectionTest() {
assertEquals(JFontChooser.APPROVE_SELECTION, command.get());
}
+ @Test
+ void updateSelectionPath_setsCorrectTreePath_whenFontExists() {
+ // Arrange
+ JFontChooser chooser = new JFontChooser();
+
+ Font font = new Font("Arial", Font.PLAIN, 12);
+ Font[] fonts = new Font[] { font };
+
+ DefaultFontChooserModel model = new DefaultFontChooserModel(fonts);
+ chooser.setModel(model);
+
+ chooser.setSelectedFont(font);
+
+ TreePath selectionPath = chooser.getSelectionPath();
+ assertNotNull(selectionPath);
+
+ Object lastComponent = selectionPath.getLastPathComponent();
+ assertTrue(lastComponent instanceof FontFaceNode);
+
+ FontFaceNode faceNode = (FontFaceNode) lastComponent;
+ assertEquals(font.getFontName(), faceNode.getFont().getFontName());
+ }
+
}