Fixed enum comparison in AbstractStatemachine#resetStateMachine

- Add a check for enum class as well so that
  restore doesn't pick a wrong instance.
- Fixes #554
This commit is contained in:
Christoph Empl
2018-09-10 15:04:39 +02:00
committed by Janne Valkealahti
parent 1a797532ca
commit cb74a35bff
2 changed files with 123 additions and 11 deletions

View File

@@ -15,6 +15,15 @@
*/
package org.springframework.statemachine.support;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
@@ -60,15 +69,6 @@ import org.springframework.statemachine.trigger.Trigger;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
/**
* Base implementation of a {@link StateMachine} loosely modelled from UML state
* machine.
@@ -655,7 +655,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
for (State<S, E> ss : s.getStates()) {
boolean enumMatch = false;
if (state instanceof Enum && ss.getId() instanceof Enum
if (state instanceof Enum && ss.getId() instanceof Enum && state.getClass() == ss.getId().getClass()
&& ((Enum) ss.getId()).ordinal() == ((Enum) state).ordinal()) {
enumMatch = true;
}
@@ -717,9 +717,11 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
S state2 = child.getState();
boolean enumMatch2 = false;
if (state2 instanceof Enum && ss.getId() instanceof Enum
&& state.getClass() == ss.getId().getClass()
&& ((Enum) ss.getId()).ordinal() == ((Enum) state2).ordinal()) {
enumMatch2 = true;
}
if (state2 != null && (ss.getIds().contains(state2) || enumMatch2) ) {
currentState = s;
lastState = currentState;

View File

@@ -38,6 +38,7 @@ import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
@@ -377,6 +378,54 @@ public class StateMachineResetTests extends AbstractStateMachineTests {
assertThat(listener.count2, is(1));
}
@Test
public void testResetFunkyEnumTypes1() throws Exception {
context.register(Config6.class);
context.refresh();
@SuppressWarnings("unchecked")
StateMachine<MyState, MyEvent> machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE,
StateMachine.class);
DefaultStateMachineContext<MyState, MyEvent> stateMachineContext = new DefaultStateMachineContext<MyState, MyEvent>(
SubState.SUB_NEXT, null, null, null);
machine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction<StateMachineAccess<MyState, MyEvent>>() {
@Override
public void apply(StateMachineAccess<MyState, MyEvent> function) {
function.resetStateMachine(stateMachineContext);
}
});
machine.start();
assertThat(machine.getState().getIds(), containsInAnyOrder(SuperState.PARENT, SubState.SUB_NEXT));
}
@Test
public void testResetFunkyEnumTypes2() throws Exception {
context.register(Config6.class);
context.refresh();
@SuppressWarnings("unchecked")
StateMachine<MyState, MyEvent> machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE,
StateMachine.class);
DefaultStateMachineContext<MyState, MyEvent> stateMachineContext = new DefaultStateMachineContext<MyState, MyEvent>(
SuperState.INITIAL, null, null, null);
machine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction<StateMachineAccess<MyState, MyEvent>>() {
@Override
public void apply(StateMachineAccess<MyState, MyEvent> function) {
function.resetStateMachine(stateMachineContext);
}
});
machine.start();
assertThat(machine.getState().getIds(), containsInAnyOrder(SuperState.INITIAL));
}
@Configuration
@EnableStateMachine
static class Config1 extends EnumStateMachineConfigurerAdapter<States, Events> {
@@ -756,4 +805,65 @@ public class StateMachineResetTests extends AbstractStateMachineTests {
}
}
@Configuration
@EnableStateMachine
static class Config6 extends StateMachineConfigurerAdapter<MyState, MyEvent> {
@Override
public void configure(final StateMachineStateConfigurer<MyState, MyEvent> states) throws Exception {
states
.withStates()
.end(SuperState.END)
.state(SuperState.PARENT)
.initial(SuperState.INITIAL)
.and()
.withStates()
.parent(SuperState.PARENT)
.initial(SubState.SUB_INITIAL)
.state(SubState.SUB_NEXT)
.end(SubState.SUB_END);
}
@Override
public void configure(final StateMachineTransitionConfigurer<MyState, MyEvent> transitions) throws Exception {
transitions
.withExternal()
.source(SuperState.INITIAL)
.target(SuperState.PARENT)
.event(MyEvent.GO)
.and()
.withExternal()
.source(SuperState.PARENT)
.target(SuperState.END)
.event(MyEvent.GO)
.and()
.withExternal()
.source(SubState.SUB_INITIAL)
.target(SubState.SUB_NEXT)
.event(MyEvent.GO)
.and()
.withExternal()
.source(SubState.SUB_NEXT)
.target(SubState.SUB_END)
.event(MyEvent.GO);
}
}
public enum SubState implements MyState {
SUB_INITIAL,
SUB_NEXT,
SUB_END;
}
public enum SuperState implements MyState {
INITIAL,
PARENT,
END;
}
public interface MyState {
}
public enum MyEvent {
GO;
}}