From 36afe829c63e70add1d350a6d1d7c73f80b4928e Mon Sep 17 00:00:00 2001 From: Paul Speed Date: Sun, 15 Sep 2019 20:27:46 -0400 Subject: [PATCH] Part 2 of the small AppState refactoring: added methods to AppStateManager for retrieving an AppState by ID. Also modified attach() to throw an IllegalArgumentException if the ID is already registered. Updated TestAppStateLifeCycle to add a small ID demonstration. --- .../com/jme3/app/state/AppStateManager.java | 53 ++++++++++++++++++- .../jme3test/app/TestAppStateLifeCycle.java | 34 ++++++++---- 2 files changed, 76 insertions(+), 11 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java b/jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java index b15b55598..96a7295d3 100644 --- a/jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java +++ b/jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java @@ -37,6 +37,8 @@ import com.jme3.renderer.RenderManager; import com.jme3.util.SafeArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; /** * The AppStateManager holds a list of {@link AppState}s which @@ -82,6 +84,12 @@ public class AppStateManager { * cleanup. */ private final SafeArrayList terminating = new SafeArrayList(AppState.class); + + /** + * Thread-safe index of every state that is currently attached and has + * an ID. + */ + private final ConcurrentMap stateIndex = new ConcurrentHashMap<>(); // All of the above lists need to be thread safe but access will be // synchronized separately.... but always on the states list. This @@ -122,7 +130,8 @@ public class AppStateManager { /** * Attach a state to the AppStateManager, the same state cannot be attached - * twice. + * twice. Throws an IllegalArgumentException if the state has an ID and that + * ID has already been associated with another AppState. * * @param state The state to attach * @return True if the state was successfully attached, false if the state @@ -130,11 +139,16 @@ public class AppStateManager { */ public boolean attach(AppState state){ synchronized (states){ + if( state.getId() != null && stateIndex.putIfAbsent(state.getId(), state) != null ) { + throw new IllegalArgumentException("ID:" + state.getId() + + " is already being used by another state:" + + stateIndex.get(state.getId())); + } if (!states.contains(state) && !initializing.contains(state)){ state.stateAttached(this); initializing.add(state); return true; - }else{ + } else { return false; } } @@ -175,6 +189,12 @@ public class AppStateManager { */ public boolean detach(AppState state){ synchronized (states){ + + // Remove it from the index if it exists. + // Note: we remove it directly from the values() in case + // the state has changed its ID since registered. + stateIndex.values().remove(state); + if (states.contains(state)){ state.stateDetached(this); states.remove(state); @@ -249,6 +269,35 @@ public class AppStateManager { return null; } + /** + * Returns the state associated with the specified ID at the time it was + * attached or null if not state was attached with that ID. + */ + public T getState( String id, Class stateClass ) { + return stateClass.cast(stateIndex.get(id)); + } + + /** + * Returns true if there is currently a state associated with the specified + * ID. + */ + public boolean hasState( String id ) { + return stateIndex.containsKey(id); + } + + /** + * Returns the state associated with the specified ID at the time it + * was attached or throws an IllegalArgumentException if the ID was + * not found. + */ + public T stateForId( String id, Class stateClass ) { + T result = getState(id, stateClass); + if( result == null ) { + throw new IllegalArgumentException("State not found for:" + id); + } + return stateClass.cast(result); + } + protected void initializePending(){ AppState[] array = getInitializing(); if (array.length == 0) diff --git a/jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java b/jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java index b6517c9d2..3bbc316b4 100644 --- a/jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java +++ b/jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java @@ -64,7 +64,10 @@ public class TestAppStateLifeCycle extends SimpleApplication { rootNode.attachChild(geom); System.out.println("Attaching test state."); - stateManager.attach(new TestState()); + stateManager.attach(new TestState()); + + System.out.println("Attaching test state with an ID."); + stateManager.attach(new TestState("Test ID")); } @Override @@ -74,51 +77,64 @@ public class TestAppStateLifeCycle extends SimpleApplication { System.out.println("Detaching test state."); stateManager.detach(stateManager.getState(TestState.class)); System.out.println("Done"); - } + } + + if( stateManager.hasState("Test ID") ) { + System.out.println("Detaching test state with an ID."); + stateManager.detach(stateManager.getState("Test ID", TestState.class)); + System.out.println("Done"); + } } public class TestState extends AbstractAppState { + public TestState() { + } + + public TestState( String id ) { + super(id); + } + @Override public void initialize(AppStateManager stateManager, Application app) { super.initialize(stateManager, app); - System.out.println("Initialized"); + System.out.println("Initialized, id:" + getId()); } @Override public void stateAttached(AppStateManager stateManager) { super.stateAttached(stateManager); - System.out.println("Attached"); + System.out.println("Attached, id:" + getId()); } @Override public void update(float tpf) { super.update(tpf); - System.out.println("update"); + System.out.println("update, id:" + getId()); } @Override public void render(RenderManager rm) { super.render(rm); - System.out.println("render"); + System.out.println("render, id:" + getId()); } @Override public void postRender() { super.postRender(); - System.out.println("postRender"); + System.out.println("postRender, id:" + getId()); } @Override public void stateDetached(AppStateManager stateManager) { super.stateDetached(stateManager); - System.out.println("Detached"); + System.out.println("Detached, id:" + getId()); } @Override public void cleanup() { super.cleanup(); - System.out.println("Cleanup"); + System.out.println("Cleanup, id:" + getId()); } }