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());
}
}