// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by 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. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.utils.fsm; import java.util.ArrayList; import java.util.Formatter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Specifically, it implements the Moore machine. * so someone else can add/modify states easily without regression. * business logic anyways. * * @param <S> state * @param <E> event */ public class StateMachine2<S, E, V extends StateObject<S>> { private final HashMap<S, StateEntry> _states = new HashMap<S, StateEntry>(); private final StateEntry _initialStateEntry; private List<StateListener<S,E,V>> _listeners = new ArrayList<StateListener<S,E,V>>(); public StateMachine2() { _initialStateEntry = new StateEntry(null); } public void addTransition(S currentState, E event, S toState) { StateEntry entry = null; if (currentState == null) { entry = _initialStateEntry; } else { entry = _states.get(currentState); if (entry == null) { entry = new StateEntry(currentState); _states.put(currentState, entry); } } entry.addTransition(event, toState); entry = _states.get(toState); if (entry == null) { entry = new StateEntry(toState); _states.put(toState, entry); } entry.addFromTransition(event, currentState); } public Set<E> getPossibleEvents(S s) { StateEntry entry = _states.get(s); return entry.nextStates.keySet(); } public S getNextState(S s, E e) throws NoTransitionException { StateEntry entry = null; if (s == null) { entry = _initialStateEntry; } else { entry = _states.get(s); assert entry != null : "Cannot retrieve transitions for state " + s; } S ns = entry.nextStates.get(e); if (ns == null) { throw new NoTransitionException("Unable to transition to a new state from " + s + " via " + e); } return ns; } public List<S> getFromStates(S s, E e) { StateEntry entry = _states.get(s); if (entry == null) { return new ArrayList<S>(); } return entry.prevStates.get(e); } public boolean transitTo(V vo, E e, Object opaque, StateDao<S,E,V> dao) throws NoTransitionException { S currentState = vo.getState(); S nextState = getNextState(currentState, e); boolean transitionStatus = true; if (nextState == null) { transitionStatus = false; } for (StateListener<S,E, V> listener : _listeners) { listener.preStateTransitionEvent(currentState, e, nextState, vo, transitionStatus, opaque); } transitionStatus = dao.updateState(currentState, e, nextState, vo, opaque); if (!transitionStatus) { return false; } for (StateListener<S,E, V> listener : _listeners) { listener.postStateTransitionEvent(currentState, e, nextState, vo, transitionStatus, opaque); } return true; } public boolean registerListener(StateListener<S,E,V> listener) { synchronized (_listeners) { return _listeners.add(listener); } } @Override public String toString() { StringBuilder str = new StringBuilder(1024); _initialStateEntry.buildString(str); for (StateEntry entry : _states.values()) { entry.buildString(str); } return str.toString(); } private class StateEntry { public S state; public HashMap<E, S> nextStates; public HashMap<E, List<S>> prevStates; public StateEntry(S state) { this.state = state; nextStates = new HashMap<E, S>(); prevStates = new HashMap<E, List<S>>(); } public void addTransition(E e, S s) { assert !nextStates.containsKey(e) : "State " + getStateStr() + " already contains a transition to state " + nextStates.get(e).toString() + " via event " + e.toString() + ". Please revisit the rule you're adding to state " + s.toString(); nextStates.put(e, s); } public void addFromTransition(E e, S s) { List<S> l = prevStates.get(e); if (l == null) { l = new ArrayList<S>(); prevStates.put(e, l); } assert !l.contains(s) : "Already contains the from transition " + e.toString() + " from state " + s.toString() + " to " + getStateStr(); l.add(s); } protected String getStateStr() { return state == null ? "Initial" : state.toString(); } public void buildString(StringBuilder str) { str.append("State: ").append(getStateStr()).append("\n"); for (Map.Entry<E, S> nextState : nextStates.entrySet()) { str.append(" --> Event: "); Formatter format = new Formatter(); str.append(format.format("%-30s", nextState.getKey().toString())); str.append("----> State: "); str.append(nextState.getValue().toString()); str.append("\n"); } } } }