From 6cd671e5ed22d4372f35322fbe59ac6b654b8e4f Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 7 Apr 2024 21:07:59 +0200 Subject: [PATCH 1/3] feat: make WorldConfigurator subscribable * interested parties can subscribe to receive change notifications for all or specific properties available * change notifications include information about the changed property as well as the old and new values * no longer interested parties can subscribe from all or specific properties * subscriptions to all properties will remain intact in case a no longer interested party cancels their subscription for specific properties --- .../generation/FacetedWorldConfigurator.java | 74 +++++++++++++++++++ .../world/generator/WorldConfigurator.java | 4 +- .../generator/WorldConfiguratorAdapter.java | 21 ++++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java b/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java index 1cc2d7b9a59..81a3f69c5ec 100644 --- a/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java +++ b/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java @@ -8,9 +8,14 @@ import org.terasology.engine.world.generator.WorldConfigurator; import org.terasology.gestalt.entitysystem.component.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; public class FacetedWorldConfigurator implements WorldConfigurator { @@ -19,6 +24,7 @@ public class FacetedWorldConfigurator implements WorldConfigurator { private final Map properties = Maps.newHashMap(); private final List providers; + private HashMap> listeners = new HashMap<>(); public FacetedWorldConfigurator(List providersList) { for (ConfigurableFacetProvider provider : providersList) { @@ -40,11 +46,79 @@ public void setProperty(String key, Component comp) { for (ConfigurableFacetProvider facetProvider : providers) { if (key.equals(facetProvider.getConfigurationName())) { facetProvider.setConfiguration(comp); + Component oldComp = properties.get(key); properties.put(key, comp); + + PropertyChangeEvent event = new PropertyChangeEvent(this, key, oldComp, comp); + Set allPropListeners = listeners.get("*"); + if (allPropListeners != null) { + allPropListeners.forEach(listener -> { + listener.propertyChange(event); + }); + } + Set specPropListeners = listeners.get(key); + if (specPropListeners != null) { + specPropListeners.forEach(listener -> { + listener.propertyChange(event); + }); + } + return; } } logger.warn("No property {} found", key); } + + /** + * Subscribes a new listener to receive change notifications for all properties. + * @param changeListener + */ + @Override + public void subscribe(PropertyChangeListener changeListener) { + Set ls = listeners.computeIfAbsent("*", k -> new HashSet<>()); + ls.add(changeListener); + } + + /** + * Unsubscribes an existing listener from receiving change notifications for all properties. + * If the listener was subscribed to a specific property separately + * via {@link FacetedWorldConfigurator#unsubscribe(String, PropertyChangeListener)}, + * it will continue to receive change notifications for that property. + * @param changeListener + */ + @Override + public void unsubscribe(PropertyChangeListener changeListener) { + listeners.computeIfPresent("*", (k, v) -> { + v.remove(changeListener); + return v; + }); + } + + /** + * Subscribes a new listener to receive change notifications for the specified property. + * @param propertyName + * @param changeListener + */ + @Override + public void subscribe(String propertyName, PropertyChangeListener changeListener) { + Set ls = listeners.computeIfAbsent(propertyName, k -> new HashSet<>()); + ls.add(changeListener); + } + + /** + * Unsubscribes an existing listener from receiving change notifications for the specified property. + * If the listener was subscribed to all properties separately + * via {@link FacetedWorldConfigurator#unsubscribe(PropertyChangeListener)}, + * it will continue to receive change notifications for all properties including the one it is being unsubscribed from now. + * @param propertyName + * @param changeListener + */ + @Override + public void unsubscribe(String propertyName, PropertyChangeListener changeListener) { + listeners.computeIfPresent(propertyName, (k, v) -> { + v.remove(changeListener); + return v; + }); + } } diff --git a/engine/src/main/java/org/terasology/engine/world/generator/WorldConfigurator.java b/engine/src/main/java/org/terasology/engine/world/generator/WorldConfigurator.java index d9d4653357f..5010facf47b 100644 --- a/engine/src/main/java/org/terasology/engine/world/generator/WorldConfigurator.java +++ b/engine/src/main/java/org/terasology/engine/world/generator/WorldConfigurator.java @@ -2,14 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.world.generator; +import org.terasology.engine.utilities.subscribables.Subscribable; import org.terasology.gestalt.entitysystem.component.Component; import java.util.Map; /** * Allows for configuration of world generators. + * Interested parties can subscribe to all or only specific properties and receive change notifications. */ -public interface WorldConfigurator { +public interface WorldConfigurator extends Subscribable { /** * The values are supposed to be annotated with {@link org.terasology.nui.properties.Property} diff --git a/engine/src/main/java/org/terasology/engine/world/generator/WorldConfiguratorAdapter.java b/engine/src/main/java/org/terasology/engine/world/generator/WorldConfiguratorAdapter.java index 240a56a9879..8f29daed998 100644 --- a/engine/src/main/java/org/terasology/engine/world/generator/WorldConfiguratorAdapter.java +++ b/engine/src/main/java/org/terasology/engine/world/generator/WorldConfiguratorAdapter.java @@ -4,6 +4,7 @@ import org.terasology.gestalt.entitysystem.component.Component; +import java.beans.PropertyChangeListener; import java.util.Collections; import java.util.Map; @@ -21,4 +22,24 @@ public Map getProperties() { public void setProperty(String key, Component comp) { // simply ignore } + + @Override + public void subscribe(PropertyChangeListener changeListener) { + // simply ignore + } + + @Override + public void unsubscribe(PropertyChangeListener changeListener) { + // simply ignore + } + + @Override + public void subscribe(String propertyName, PropertyChangeListener changeListener) { + // simply ignore + } + + @Override + public void unsubscribe(String propertyName, PropertyChangeListener changeListener) { + // simply ignore + } } From bf6da195a322d134fe27cf22635b00db928952f4 Mon Sep 17 00:00:00 2001 From: jdrueckert Date: Wed, 10 Apr 2024 17:56:31 +0200 Subject: [PATCH 2/3] chore: accept HashMap type usage --- .../engine/world/generation/FacetedWorldConfigurator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java b/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java index 81a3f69c5ec..55d2c96e4e6 100644 --- a/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java +++ b/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java @@ -24,7 +24,7 @@ public class FacetedWorldConfigurator implements WorldConfigurator { private final Map properties = Maps.newHashMap(); private final List providers; - private HashMap> listeners = new HashMap<>(); + private HashMap> listeners = new HashMap<>(); // OK public FacetedWorldConfigurator(List providersList) { for (ConfigurableFacetProvider provider : providersList) { From a964e5b9919e51e77c119a813d58a737f964c564 Mon Sep 17 00:00:00 2001 From: jdrueckert Date: Wed, 10 Apr 2024 18:26:55 +0200 Subject: [PATCH 3/3] chore: fix checkstyle warning --- .../engine/world/generation/FacetedWorldConfigurator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java b/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java index 55d2c96e4e6..5972217e14d 100644 --- a/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java +++ b/engine/src/main/java/org/terasology/engine/world/generation/FacetedWorldConfigurator.java @@ -24,7 +24,7 @@ public class FacetedWorldConfigurator implements WorldConfigurator { private final Map properties = Maps.newHashMap(); private final List providers; - private HashMap> listeners = new HashMap<>(); // OK + private Map> listeners = new HashMap<>(); public FacetedWorldConfigurator(List providersList) { for (ConfigurableFacetProvider provider : providersList) {