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.
fix-openal-soft-deadlink
Paul Speed 5 years ago
parent 3dd2755c20
commit 36afe829c6
  1. 51
      jme3-core/src/main/java/com/jme3/app/state/AppStateManager.java
  2. 30
      jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.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
@ -83,6 +85,12 @@ public class AppStateManager {
*/ */
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
// is to avoid deadlocking that may occur and the most common use case // is to avoid deadlocking that may occur and the most common use case
@ -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,6 +139,11 @@ 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);
@ -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)

@ -65,6 +65,9 @@ public class TestAppStateLifeCycle extends SimpleApplication {
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
@ -75,50 +78,63 @@ public class TestAppStateLifeCycle extends SimpleApplication {
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());
} }
} }

Loading…
Cancel
Save