/*
* Copyright (C) 2012 Google Inc.
*
* 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 interactivespaces.util.statemachine.simplegoal;
import interactivespaces.util.statemachine.simplegoal.SimpleGoalStateTransition.TransitionResult;
import com.google.common.collect.Lists;
import org.apache.commons.logging.Log;
import java.util.Queue;
/**
* Run an activity through a set of state transitions.
*
* <p>
* This class is not threadsafe!
*
* @param <S>
* type for the state
* @param <C>
* type for the state control
*
* @author Keith M. Hughes
*/
public class SimpleGoalStateTransitioner<S, C> {
/**
* Control.
*/
private C control;
/**
* The transitions which need to take place.
*/
private Queue<SimpleGoalStateTransition<S, C>> transitions = Lists.newLinkedList();
/**
* Whether or not there was an error.
*/
private boolean errored;
/**
* Log for errors.
*/
private Log log;
/**
* Construct a new transitioner.
*
* @param control
* the control for the item being transitioned
* @param log
* the logger to use
*/
public SimpleGoalStateTransitioner(C control, Log log) {
this.control = control;
this.log = log;
}
/**
* Add new transitions to the transitioner.
*
* @param newTransitions
* the new transitions to add
*
* @return the transitioner
*/
public SimpleGoalStateTransitioner<S, C> addTransitions(SimpleGoalStateTransition<S, C>... newTransitions) {
if (newTransitions != null) {
for (SimpleGoalStateTransition<S, C> transition : newTransitions) {
transitions.add(transition);
}
}
return this;
}
/**
* Transition to a new state.
*
* @param currentState
* the state that was transitioned to
*
* @return the result of the transition
*/
public SimpleGoalStateTransitionResult transition(S currentState) {
if (errored) {
return SimpleGoalStateTransitionResult.CANT;
}
SimpleGoalStateTransition<S, C> nextTransition = transitions.peek();
if (nextTransition == null) {
return SimpleGoalStateTransitionResult.DONE;
}
TransitionResult canTransition = nextTransition.canTransition(currentState);
if (canTransition == TransitionResult.WAIT) {
// Don't consume the transition yet. WAIT means we are on our way.
return SimpleGoalStateTransitionResult.WORKING;
} else if (canTransition == TransitionResult.OK) {
transitions.poll();
try {
nextTransition.onTransition(currentState, control);
} catch (Throwable e) {
errored = true;
log.error("Error during goal transition", e);
return SimpleGoalStateTransitionResult.ERROR;
}
} else {
log.warn("Goal state machine cannot transition");
return SimpleGoalStateTransitionResult.CANT;
}
if (transitions.isEmpty()) {
return SimpleGoalStateTransitionResult.DONE;
} else {
return SimpleGoalStateTransitionResult.WORKING;
}
}
/**
* Result of a transition attempt.
*
* @author Keith M. Hughes
*/
public enum SimpleGoalStateTransitionResult {
/**
* Still working on the transitions.
*/
WORKING,
/**
* The transitions are done.
*/
DONE,
/**
* Can't transition. may be because it errored before, or the transition is illegal.
*/
CANT,
/**
* An error occurred during transition.
*/
ERROR
}
}