From cb74a35bff625bc0c9c45ac7fb24efc609e059aa Mon Sep 17 00:00:00 2001 From: Christoph Empl Date: Mon, 10 Sep 2018 15:04:39 +0200 Subject: [PATCH] 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 --- .../support/AbstractStateMachine.java | 22 ++-- .../statemachine/StateMachineResetTests.java | 112 +++++++++++++++++- 2 files changed, 123 insertions(+), 11 deletions(-) diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java index 6f3e0590..cceb3f7a 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java @@ -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 extends StateMachineObjectSuppo for (State 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 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; diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineResetTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineResetTests.java index d1090103..d9163e1e 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineResetTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineResetTests.java @@ -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 machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, + StateMachine.class); + + DefaultStateMachineContext stateMachineContext = new DefaultStateMachineContext( + SubState.SUB_NEXT, null, null, null); + + machine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction>() { + + @Override + public void apply(StateMachineAccess 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 machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, + StateMachine.class); + + DefaultStateMachineContext stateMachineContext = new DefaultStateMachineContext( + SuperState.INITIAL, null, null, null); + + machine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction>() { + + @Override + public void apply(StateMachineAccess function) { + function.resetStateMachine(stateMachineContext); + } + }); + + machine.start(); + assertThat(machine.getState().getIds(), containsInAnyOrder(SuperState.INITIAL)); + } + @Configuration @EnableStateMachine static class Config1 extends EnumStateMachineConfigurerAdapter { @@ -756,4 +805,65 @@ public class StateMachineResetTests extends AbstractStateMachineTests { } -} + @Configuration + @EnableStateMachine + static class Config6 extends StateMachineConfigurerAdapter { + + @Override + public void configure(final StateMachineStateConfigurer 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 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; + }}