/**
* This file is part of Waarp Project.
*
* Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the
* COPYRIGHT.txt in the distribution for a full listing of individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Waarp . If not, see
* <http://www.gnu.org/licenses/>.
*/
package org.waarp.common.state;
import java.util.EnumSet;
import java.util.concurrent.ConcurrentHashMap;
import org.waarp.common.exception.IllegalFiniteStateException;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
/**
* This is the base class for the basic support of Finite State Machine in GoldenGate. One need to
* implement an Enum class to use with it. <br>
* <br>
* Note: the type EnumSet< ? > is in fact of type EnumSet< EnumState >
*
* @author Frederic Bregier
* @param <EnumState>
*
*/
public class MachineState<EnumState> {
/**
* Internal Logger
*/
private static final WaarpLogger logger = WaarpLoggerFactory
.getLogger(MachineState.class);
private ConcurrentHashMap<EnumState, EnumSet<?>> statemap;
private EnumState currentState;
/**
* Initialize with an initialState
*
* @param initialState
* initial MachineState
* @param map
* the association of state and set of acceptable following states
*/
public MachineState(EnumState initialState, ConcurrentHashMap<EnumState, EnumSet<?>> map) {
statemap = map;
currentState = initialState;
}
/**
* Initialize with an initialState but no association (Machine State is empty)
*
* @param initialState
* initial MachineState
*/
public MachineState(EnumState initialState) {
statemap = new ConcurrentHashMap<EnumState, EnumSet<?>>();
currentState = initialState;
}
/**
* Add a new association from one state to a set of acceptable following states (can replace an
* existing association)
*
* @param state
* @param set
* the new association
* @return the previous association if any
*/
public EnumSet<?> addNewAssociation(EnumState state, EnumSet<?> set) {
return statemap.put(state, set);
}
/**
* Add a new association from one state to a set of acceptable following states (can replace an
* existing association)
*
* @param elt
* @return the previous association if any
*/
public EnumSet<?> addNewAssociation(Transition<EnumState> elt) {
return statemap.put(elt.getState(), elt.getSet());
}
/**
* Remove an association from one state to any acceptable following states
*
* @param state
* the state to remove any acceptable following states
* @return the previous association if any
*/
public EnumSet<?> removeAssociation(EnumState state) {
return statemap.remove(state);
}
/**
* Return the current application state.
*
* @return the current State
*/
public EnumState getCurrent() {
return currentState;
}
/**
* Sets the current application state.
*
* @param desiredState
* @return the requested state, if it was reachable
* @throws IllegalFiniteStateException
* if the state is not allowed
*/
public EnumState setCurrent(EnumState desiredState) throws IllegalFiniteStateException {
if (!isReachable(desiredState)) {
logger.debug("State " + desiredState + " not reachable from: " + currentState);
throw new IllegalFiniteStateException(desiredState + " not allowed from "
+ currentState);
}
return setAsFinal(desiredState);
}
/**
* Sets the current application state, but no exception if not compatible.
*
* @param desiredState
* @return the requested state, even if it was not reachable
*/
public EnumState setDryCurrent(EnumState desiredState) {
return setAsFinal(desiredState);
}
/**
* Determine if the given state is allowed to be next.
*
* @param desiredState
* desired MachineState
* @return True if the desiredState is valid from currentState
*/
private boolean isReachable(EnumState desiredState) {
if (currentState == null || statemap == null) {
return false;
}
EnumSet<?> set = statemap.get(currentState);
if (set != null) {
return set.contains(desiredState);
}
return false;
}
/**
* Finalizes the new requested state
*
* @param desiredState
* @return the requested state
*/
private EnumState setAsFinal(EnumState desiredState) {
logger.debug("New State: " + desiredState + " from " + currentState);
currentState = desiredState;
return currentState;
}
/**
* Release the Machine State
*/
public void release() {
this.currentState = null;
this.statemap = null;
}
}