From 8405dd0ad7e731413193b64add91016fe078cd8b Mon Sep 17 00:00:00 2001
From: DemchaAV
One vestigial holdover keeps {@code SystemECS} and {@code EntityManager} - * referenced from live code: the canonical - * {@code engine.measurement.TextMeasurementSystem} still - * {@code extends SystemECS} with a no-op {@code process(EntityManager)}. - * Decoupling that base — so {@code engine.core} becomes genuinely unreferenced by - * the canonical pipeline — is a tracked follow-up.
- * *The genuinely shared engine packages are elsewhere and are not * deprecated: {@code engine.components} (value types), {@code engine.measurement} * (text-measurement contracts), {@code engine.font}, and @@ -28,9 +21,8 @@ * @deprecated Legacy ECS engine, superseded by the canonical * {@code com.demcha.compose.document.layout} pipeline. No public entry point * runs it and it is not on the canonical hot path; it is retained only for the - * legacy engine regression tests (aside from the vestigial {@code SystemECS} - * base of {@code TextMeasurementSystem}, a tracked cleanup) — a candidate for - * removal. Do not extend it or spend optimization effort here. + * legacy engine regression tests — a candidate for removal. Do not extend it or + * spend optimization effort here. */ @Deprecated package com.demcha.compose.engine.core; diff --git a/src/main/java/com/demcha/compose/engine/layout/LayoutSystem.java b/src/main/java/com/demcha/compose/engine/layout/LayoutSystem.java index e667fcd18..e1fd80599 100644 --- a/src/main/java/com/demcha/compose/engine/layout/LayoutSystem.java +++ b/src/main/java/com/demcha/compose/engine/layout/LayoutSystem.java @@ -388,7 +388,7 @@ private Entity alignBlockText(Entity blockTextBox, EntityManager entityManager) if (Double.isNaN(lineWidth)) { if (measurementSystem == null) { measurementSystem = entityManager.getSystems() - .getSystem(TextMeasurementSystem.class) + .textMeasurement() .orElseThrow(() -> new IllegalStateException("TextMeasurementSystem is required to align block text.")); } lineWidth = line.width(measurementSystem, style); diff --git a/src/main/java/com/demcha/compose/engine/measurement/TextMeasurementSystem.java b/src/main/java/com/demcha/compose/engine/measurement/TextMeasurementSystem.java index a374cf97f..6026f641d 100644 --- a/src/main/java/com/demcha/compose/engine/measurement/TextMeasurementSystem.java +++ b/src/main/java/com/demcha/compose/engine/measurement/TextMeasurementSystem.java @@ -2,8 +2,6 @@ import com.demcha.compose.engine.components.content.text.TextStyle; import com.demcha.compose.engine.components.geometry.ContentSize; -import com.demcha.compose.engine.core.EntityManager; -import com.demcha.compose.engine.core.SystemECS; /** * Engine-level text measurement contract used by builders and layout helpers. @@ -12,7 +10,7 @@ * without forcing engine code to reach through the active rendering system. *
*/ -public interface TextMeasurementSystem extends SystemECS { +public interface TextMeasurementSystem { /** * Vertical metrics for one resolved text style. @@ -90,19 +88,4 @@ default void clearCaches() { default double lineHeight(TextStyle style) { return lineMetrics(style).lineHeight(); } - - /** - * A no-op implementation of the standard ECS system processing method. - *- * Measurement systems act as service providers that expose metrics to builders - * and layout calculations on demand. They do not actively mutate or participate - * in the runtime processing of entities within the main game loop / pipeline. - * - * @param entityManager The active entity manager. Ignored by this system. - */ - @Override - default void process(EntityManager entityManager) { - // Measurement systems expose services to builders/layout helpers and do - // not participate in the runtime processing pipeline. - } } diff --git a/src/test/java/com/demcha/compose/engine/core/SystemRegistryTest.java b/src/test/java/com/demcha/compose/engine/core/SystemRegistryTest.java new file mode 100644 index 000000000..34b794cf4 --- /dev/null +++ b/src/test/java/com/demcha/compose/engine/core/SystemRegistryTest.java @@ -0,0 +1,64 @@ +package com.demcha.compose.engine.core; + +import com.demcha.compose.engine.components.content.text.TextStyle; +import com.demcha.compose.engine.components.geometry.ContentSize; +import com.demcha.compose.engine.measurement.TextMeasurementSystem; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +/** + * Unit coverage for the dedicated text-measurement service slot on + * {@link SystemRegistry}. This seam is what lets {@code TextMeasurementSystem} + * stay decoupled from {@link SystemECS}: the measurement system is a service + * provider exposed on demand, not a {@code process()}-driven ECS system, so it is + * held out-of-band from the {@code systems} map. + */ +class SystemRegistryTest { + + @Test + void textMeasurementIsEmptyWhenNoneRegistered() { + assertThat(new SystemRegistry().textMeasurement()).isEmpty(); + } + + @Test + void registerTextMeasurementExposesTheSameInstance() { + SystemRegistry registry = new SystemRegistry(); + TextMeasurementSystem service = new StubMeasurement(); + + registry.registerTextMeasurement(service); + + assertThat(registry.textMeasurement()).containsSame(service); + } + + @Test + void registerTextMeasurementRejectsNull() { + assertThatNullPointerException() + .isThrownBy(() -> new SystemRegistry().registerTextMeasurement(null)) + .withMessageContaining("textMeasurement"); + } + + @Test + void measurementServiceIsNotEnrolledAsAProcessDrivenSystem() { + SystemRegistry registry = new SystemRegistry(); + + registry.registerTextMeasurement(new StubMeasurement()); + + // It must stay out of the SystemECS map so the processSystems() loop never + // calls process() on it — that is the whole point of the dedicated slot. + assertThat(registry.getStream().toList()).isEmpty(); + } + + private static final class StubMeasurement implements TextMeasurementSystem { + @Override + public ContentSize measure(TextStyle style, String text) { + return new ContentSize(0.0, 0.0); + } + + @Override + public LineMetrics lineMetrics(TextStyle style) { + return new LineMetrics(0.0, 0.0, 0.0); + } + } +} diff --git a/src/test/java/com/demcha/compose/testsupport/EngineComposerHarness.java b/src/test/java/com/demcha/compose/testsupport/EngineComposerHarness.java index 6200b5f3c..d8fe48b2e 100644 --- a/src/test/java/com/demcha/compose/testsupport/EngineComposerHarness.java +++ b/src/test/java/com/demcha/compose/testsupport/EngineComposerHarness.java @@ -349,7 +349,7 @@ public void close() throws IOException { } private void setupSystems() { - entityManager.getSystems().addSystem(new FontLibraryTextMeasurementSystem(entityManager.getFonts(), PdfFont.class)); + entityManager.getSystems().registerTextMeasurement(new FontLibraryTextMeasurementSystem(entityManager.getFonts(), PdfFont.class)); entityManager.getSystems().addSystem(layoutSystem); entityManager.getSystems().addSystem(renderingSystem); } diff --git a/src/test/java/com/demcha/compose/testsupport/engine/assembly/BlockTextBuilder.java b/src/test/java/com/demcha/compose/testsupport/engine/assembly/BlockTextBuilder.java index 8b924f285..fe08b8195 100644 --- a/src/test/java/com/demcha/compose/testsupport/engine/assembly/BlockTextBuilder.java +++ b/src/test/java/com/demcha/compose/testsupport/engine/assembly/BlockTextBuilder.java @@ -584,7 +584,7 @@ private boolean isStickyToNext(String text) { private TextMeasurementSystem textMeasurementSystem() { return entityManager.getSystems() - .getSystem(TextMeasurementSystem.class) + .textMeasurement() .orElseThrow(() -> new IllegalStateException("TextMeasurementSystem is required to build block text.")); } diff --git a/src/test/java/com/demcha/compose/testsupport/engine/assembly/BlockTextBuilderTest.java b/src/test/java/com/demcha/compose/testsupport/engine/assembly/BlockTextBuilderTest.java index 6e05e0c77..3effe5158 100644 --- a/src/test/java/com/demcha/compose/testsupport/engine/assembly/BlockTextBuilderTest.java +++ b/src/test/java/com/demcha/compose/testsupport/engine/assembly/BlockTextBuilderTest.java @@ -31,7 +31,7 @@ class BlockTextBuilderTest { @BeforeEach void setUp() { entityManager = new EntityManager(); - entityManager.getSystems().addSystem(new FontLibraryTextMeasurementSystem(entityManager.getFonts(), PdfFont.class)); + entityManager.getSystems().registerTextMeasurement(new FontLibraryTextMeasurementSystem(entityManager.getFonts(), PdfFont.class)); entityManager.setMarkdown(true); } diff --git a/src/test/java/com/demcha/compose/testsupport/engine/assembly/TableBuilder.java b/src/test/java/com/demcha/compose/testsupport/engine/assembly/TableBuilder.java index 206295664..f6cc8e284 100644 --- a/src/test/java/com/demcha/compose/testsupport/engine/assembly/TableBuilder.java +++ b/src/test/java/com/demcha/compose/testsupport/engine/assembly/TableBuilder.java @@ -398,7 +398,7 @@ private ContentSize measureText(String text, TableCellLayoutStyle style) { private TextMeasurementSystem textMeasurementSystem() { return entityManager.getSystems() - .getSystem(TextMeasurementSystem.class) + .textMeasurement() .orElseThrow(() -> new IllegalStateException("TextMeasurementSystem is required to measure table text.")); } diff --git a/src/test/java/com/demcha/compose/testsupport/engine/assembly/TableBuilderTest.java b/src/test/java/com/demcha/compose/testsupport/engine/assembly/TableBuilderTest.java index 33d0efbec..258a4d0a1 100644 --- a/src/test/java/com/demcha/compose/testsupport/engine/assembly/TableBuilderTest.java +++ b/src/test/java/com/demcha/compose/testsupport/engine/assembly/TableBuilderTest.java @@ -285,7 +285,7 @@ void shouldIncludeCellLineSpacingInMultilineCellHeight() throws Exception { @Test void shouldBuildWithoutLayoutSystemWhenTextMeasurementSystemIsRegistered() { EntityManager entityManager = new EntityManager(); - entityManager.getSystems().addSystem(new FontLibraryTextMeasurementSystem(entityManager.getFonts(), PdfFont.class)); + entityManager.getSystems().registerTextMeasurement(new FontLibraryTextMeasurementSystem(entityManager.getFonts(), PdfFont.class)); Entity table = new TableBuilder(entityManager) .entityName("MeasurementOnlyTable") diff --git a/src/test/java/com/demcha/compose/testsupport/engine/assembly/TextBuilderTest.java b/src/test/java/com/demcha/compose/testsupport/engine/assembly/TextBuilderTest.java index 49de9cd7e..10ad24f44 100644 --- a/src/test/java/com/demcha/compose/testsupport/engine/assembly/TextBuilderTest.java +++ b/src/test/java/com/demcha/compose/testsupport/engine/assembly/TextBuilderTest.java @@ -21,7 +21,7 @@ class TextBuilderTest { @BeforeEach void setUp() { entityManager = new EntityManager(); - entityManager.getSystems().addSystem(new FontLibraryTextMeasurementSystem(entityManager.getFonts(), PdfFont.class)); + entityManager.getSystems().registerTextMeasurement(new FontLibraryTextMeasurementSystem(entityManager.getFonts(), PdfFont.class)); } @Test @@ -79,7 +79,7 @@ void shouldAutosizeWithoutLayoutSystemWhenMeasurementSystemIsRegistered() { void shouldExposeFullPdfLineMetricsThroughMeasurementSystem() { TextStyle style = TextStyle.DEFAULT_STYLE; var measurementSystem = entityManager.getSystems() - .getSystem(com.demcha.compose.engine.measurement.TextMeasurementSystem.class) + .textMeasurement() .orElseThrow(); PdfFont font = (PdfFont) entityManager.getFonts().getFont(style.fontName(), PdfFont.class).orElseThrow();