Explorar o código

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.

Paul Speed %!s(int64=6) %!d(string=hai) anos
pai
achega
36afe829c6

+ 51 - 2
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 com.jme3.util.SafeArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.List;
 import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
 
 
 /**
 /**
  * The <code>AppStateManager</code> holds a list of {@link AppState}s which
  * The <code>AppStateManager</code> holds a list of {@link AppState}s which
@@ -82,6 +84,12 @@ public class AppStateManager {
      *  cleanup.  
      *  cleanup.  
      */
      */
     private final SafeArrayList<AppState> terminating = new SafeArrayList<AppState>(AppState.class);
     private final SafeArrayList<AppState> terminating = new SafeArrayList<AppState>(AppState.class);
+
+    /**
+     *  Thread-safe index of every state that is currently attached and has
+     *  an ID.
+     */
+    private final ConcurrentMap<String, AppState> stateIndex = new ConcurrentHashMap<>();
  
  
     // All of the above lists need to be thread safe but access will be
     // All of the above lists need to be thread safe but access will be
     // synchronized separately.... but always on the states list.  This
     // 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
      * 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
      * @param state The state to attach
      * @return True if the state was successfully attached, false if the state
      * @return True if the state was successfully attached, false if the state
@@ -130,11 +139,16 @@ public class AppStateManager {
      */
      */
     public boolean attach(AppState state){
     public boolean attach(AppState state){
         synchronized (states){
         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)){
             if (!states.contains(state) && !initializing.contains(state)){
                 state.stateAttached(this);
                 state.stateAttached(this);
                 initializing.add(state);
                 initializing.add(state);
                 return true;
                 return true;
-            }else{
+            } else {
                 return false;
                 return false;
             }
             }
         }
         }
@@ -175,6 +189,12 @@ public class AppStateManager {
      */
      */
     public boolean detach(AppState state){
     public boolean detach(AppState state){
         synchronized (states){
         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)){
             if (states.contains(state)){
                 state.stateDetached(this);
                 state.stateDetached(this);
                 states.remove(state);
                 states.remove(state);
@@ -249,6 +269,35 @@ public class AppStateManager {
         return null;
         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 extends AppState> T getState( String id, Class<T> 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 extends AppState> T stateForId( String id, Class<T> stateClass ) {
+        T result = getState(id, stateClass);
+        if( result == null ) {
+            throw new IllegalArgumentException("State not found for:" + id);
+        }
+        return stateClass.cast(result);
+    } 
+
     protected void initializePending(){
     protected void initializePending(){
         AppState[] array = getInitializing();
         AppState[] array = getInitializing();
         if (array.length == 0)
         if (array.length == 0)

+ 25 - 9
jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java

@@ -64,7 +64,10 @@ public class TestAppStateLifeCycle extends SimpleApplication {
         rootNode.attachChild(geom);
         rootNode.attachChild(geom);
 
 
         System.out.println("Attaching test state.");
         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
     @Override
@@ -74,51 +77,64 @@ public class TestAppStateLifeCycle extends SimpleApplication {
             System.out.println("Detaching test state."); 
             System.out.println("Detaching test state."); 
             stateManager.detach(stateManager.getState(TestState.class));
             stateManager.detach(stateManager.getState(TestState.class));
             System.out.println("Done"); 
             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 class TestState extends AbstractAppState {
  
  
+        public TestState() {
+        }
+        
+        public TestState( String id ) {
+            super(id);
+        }
+ 
         @Override
         @Override
         public void initialize(AppStateManager stateManager, Application app) {
         public void initialize(AppStateManager stateManager, Application app) {
             super.initialize(stateManager, app);
             super.initialize(stateManager, app);
-            System.out.println("Initialized");
+            System.out.println("Initialized, id:" + getId());
         }
         }
  
  
         @Override
         @Override
         public void stateAttached(AppStateManager stateManager) {
         public void stateAttached(AppStateManager stateManager) {
             super.stateAttached(stateManager);
             super.stateAttached(stateManager);
-            System.out.println("Attached");
+            System.out.println("Attached, id:" + getId());
         }
         }
  
  
         @Override
         @Override
         public void update(float tpf) {
         public void update(float tpf) {
             super.update(tpf);
             super.update(tpf);
-            System.out.println("update");
+            System.out.println("update, id:" + getId());
         }
         }
 
 
         @Override
         @Override
         public void render(RenderManager rm) {
         public void render(RenderManager rm) {
             super.render(rm);
             super.render(rm);
-            System.out.println("render");
+            System.out.println("render, id:" + getId());
         }
         }
 
 
         @Override
         @Override
         public void postRender() {
         public void postRender() {
             super.postRender();
             super.postRender();
-            System.out.println("postRender");
+            System.out.println("postRender, id:" + getId());
         }
         }
 
 
         @Override
         @Override
         public void stateDetached(AppStateManager stateManager) {
         public void stateDetached(AppStateManager stateManager) {
             super.stateDetached(stateManager);
             super.stateDetached(stateManager);
-            System.out.println("Detached");
+            System.out.println("Detached, id:" + getId());
         }
         }
  
  
         @Override
         @Override
         public void cleanup() {
         public void cleanup() {
             super.cleanup();
             super.cleanup();
-            System.out.println("Cleanup"); 
+            System.out.println("Cleanup, id:" + getId()); 
         }
         }
 
 
     }    
     }