Should not define initial state as state

- Tweak DefaultStateConfigurer so that we don't need
  to define initial state as additional state. Also
  fixed some other issue which caused a bit of a mess
  when i.e multiple states were introduces twice.
- Fixes #29
This commit is contained in:
Janne Valkealahti
2015-05-06 10:59:06 +01:00
parent 1186ae4a71
commit 8e42ca0688
4 changed files with 212 additions and 3 deletions

View File

@@ -61,14 +61,26 @@ public class StateData<S, E> {
public Collection<E> getDeferred() {
return deferred;
}
public void setDeferred(Collection<E> deferred) {
this.deferred = deferred;
}
public Collection<? extends Action<S, E>> getEntryActions() {
return entryActions;
}
public void setEntryActions(Collection<? extends Action<S, E>> entryActions) {
this.entryActions = entryActions;
}
public Collection<? extends Action<S, E>> getExitActions() {
return exitActions;
}
public void setExitActions(Collection<? extends Action<S, E>> exitActions) {
this.exitActions = exitActions;
}
public Object getParent() {
return parent;

View File

@@ -18,6 +18,8 @@ package org.springframework.statemachine.config.configurers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -45,7 +47,7 @@ public class DefaultStateConfigurer<S, E>
private final Object region = UUID.randomUUID().toString();
private final Collection<StateData<S, E>> incomplete = new ArrayList<StateData<S, E>>();
private final Map<S, StateData<S, E>> incomplete = new HashMap<S, StateData<S, E>>();
private S initialState;
@@ -68,7 +70,7 @@ public class DefaultStateConfigurer<S, E>
// before passing state datas to builder, update structure
// for missing parent, initial and end state infos.
Collection<StateData<S, E>> stateDatas = new ArrayList<StateData<S, E>>();
for (StateData<S, E> s : incomplete) {
for (StateData<S, E> s : incomplete.values()) {
s.setParent(parent);
stateDatas.add(s);
if (s.getState() == initialState) {
@@ -99,6 +101,7 @@ public class DefaultStateConfigurer<S, E>
@Override
public StateConfigurer<S, E> initial(S initial) {
this.initialState = initial;
state(initial);
return this;
}
@@ -195,7 +198,26 @@ public class DefaultStateConfigurer<S, E>
private void addIncomplete(Object parent, S state, Collection<E> deferred,
Collection<? extends Action<S, E>> entryActions, Collection<? extends Action<S, E>> exitActions) {
incomplete.add(new StateData<S, E>(parent, region, state, deferred, entryActions, exitActions));
StateData<S, E> stateData = incomplete.get(state);
if (stateData == null) {
stateData = new StateData<S, E>(parent, region, state, deferred, entryActions, exitActions);
incomplete.put(state, stateData);
}
if (stateData.getParent() == null) {
stateData.setParent(parent);
}
if (stateData.getRegion() == null) {
stateData.setRegion(region);
}
if (stateData.getDeferred() == null) {
stateData.setDeferred(deferred);
}
if (stateData.getEntryActions() == null) {
stateData.setEntryActions(entryActions);
}
if (stateData.getExitActions() == null) {
stateData.setExitActions(exitActions);
}
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.config.configurers;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.springframework.statemachine.AbstractStateMachineTests.TestEntryAction;
import org.springframework.statemachine.AbstractStateMachineTests.TestEvents;
import org.springframework.statemachine.AbstractStateMachineTests.TestExitAction;
import org.springframework.statemachine.AbstractStateMachineTests.TestStates;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.StateData;
import org.springframework.statemachine.config.builders.StateMachineStateBuilder;
public class DefaultStateConfigurerTests {
@Test
public void testInitialWithoutState() throws Exception {
DefaultStateConfigurer<TestStates, TestEvents> configurer = new DefaultStateConfigurer<TestStates, TestEvents>();
TestStateMachineStateBuilder builder = new TestStateMachineStateBuilder();
configurer.initial(TestStates.SI);
configurer.configure(builder);
assertThat(builder.data, notNullValue());
assertThat(builder.data.size(), is(1));
assertThat(builder.data.iterator().next().getState(), is(TestStates.SI));
}
@Test
public void testInitialWithState() throws Exception {
DefaultStateConfigurer<TestStates, TestEvents> configurer = new DefaultStateConfigurer<TestStates, TestEvents>();
TestStateMachineStateBuilder builder = new TestStateMachineStateBuilder();
configurer.initial(TestStates.SI);
configurer.state(TestStates.SI);
configurer.configure(builder);
assertThat(builder.data, notNullValue());
assertThat(builder.data.size(), is(1));
assertThat(builder.data.iterator().next().getState(), is(TestStates.SI));
}
@Test
public void testSameStateShouldResultOneState() throws Exception {
DefaultStateConfigurer<TestStates, TestEvents> configurer = new DefaultStateConfigurer<TestStates, TestEvents>();
TestStateMachineStateBuilder builder = new TestStateMachineStateBuilder();
configurer.state(TestStates.SI);
configurer.state(TestStates.SI);
configurer.configure(builder);
assertThat(builder.data, notNullValue());
assertThat(builder.data.size(), is(1));
assertThat(builder.data.iterator().next().getState(), is(TestStates.SI));
}
@Test
public void testParentSet() throws Exception {
DefaultStateConfigurer<TestStates, TestEvents> configurer = new DefaultStateConfigurer<TestStates, TestEvents>();
TestStateMachineStateBuilder builder = new TestStateMachineStateBuilder();
configurer.parent(TestStates.SI);
configurer.state(TestStates.S1);
configurer.configure(builder);
assertThat(builder.data, notNullValue());
assertThat(builder.data.size(), is(1));
assertThat(builder.data.iterator().next().getState(), is(TestStates.S1));
assertThat((TestStates)builder.data.iterator().next().getParent(), is(TestStates.SI));
}
@Test
public void testActionsInitialFirst() throws Exception {
@SuppressWarnings("unchecked")
Collection<Action<TestStates, TestEvents>> exitActions = Arrays.asList(testExitAction());
DefaultStateConfigurer<TestStates, TestEvents> configurer = new DefaultStateConfigurer<TestStates, TestEvents>();
TestStateMachineStateBuilder builder = new TestStateMachineStateBuilder();
configurer.initial(TestStates.S1);
configurer.state(TestStates.S1, null, exitActions);
configurer.configure(builder);
assertThat(builder.data, notNullValue());
assertThat(builder.data.size(), is(1));
assertThat(builder.data.iterator().next().getState(), is(TestStates.S1));
assertThat(builder.data.iterator().next().getEntryActions(), nullValue());
assertThat(builder.data.iterator().next().getExitActions(), notNullValue());
}
@Test
public void testActionsJustState() throws Exception {
@SuppressWarnings("unchecked")
Collection<Action<TestStates, TestEvents>> entryActions = Arrays.asList(testEntryAction());
DefaultStateConfigurer<TestStates, TestEvents> configurer = new DefaultStateConfigurer<TestStates, TestEvents>();
TestStateMachineStateBuilder builder = new TestStateMachineStateBuilder();
configurer.state(TestStates.S2, entryActions, null);
configurer.configure(builder);
assertThat(builder.data, notNullValue());
assertThat(builder.data.size(), is(1));
assertThat(builder.data.iterator().next().getState(), is(TestStates.S2));
assertThat(builder.data.iterator().next().getExitActions(), nullValue());
assertThat(builder.data.iterator().next().getEntryActions(), notNullValue());
}
private static class TestStateMachineStateBuilder extends StateMachineStateBuilder<TestStates, TestEvents> {
Collection<StateData<TestStates, TestEvents>> data;
@Override
public void addStateData(Collection<StateData<TestStates, TestEvents>> stateDatas) {
this.data = stateDatas;
}
}
private Action<TestStates, TestEvents> testEntryAction() {
return new TestEntryAction();
}
private Action<TestStates, TestEvents> testExitAction() {
return new TestExitAction();
}
}

View File

@@ -58,6 +58,20 @@ public class InitialStateTests extends AbstractStateMachineTests {
context.refresh();
}
@SuppressWarnings({ "unchecked" })
@Test
public void testInitialNoNeedAsState() throws Exception {
context.register(BaseConfig.class, Config3.class);
context.refresh();
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
EnumStateMachine<TestStates,TestEvents> machine =
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
machine.start();
assertThat(machine.getState().getIds(), contains(TestStates.SI));
machine.sendEvent(TestEvents.E1);
assertThat(machine.getState().getIds(), contains(TestStates.S1));
}
@Configuration
@EnableStateMachine
public static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
@@ -111,6 +125,29 @@ public class InitialStateTests extends AbstractStateMachineTests {
}
@Configuration
@EnableStateMachine
public static class Config3 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
@Override
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
states
.withStates()
.initial(TestStates.SI)
.state(TestStates.S1);
}
@Override
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
transitions
.withExternal()
.source(TestStates.SI)
.target(TestStates.S1)
.event(TestEvents.E1);
}
}
@Override
protected AnnotationConfigApplicationContext buildContext() {
return new AnnotationConfigApplicationContext();