MachineState.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, 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 org.waarp.common.exception.IllegalFiniteStateException;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import java.util.EnumSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 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 >
*
* @param <E>
*/
public class MachineState<E extends Enum<E>> {
/**
* Internal Logger
*/
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(MachineState.class);
private ConcurrentMap<E, EnumSet<E>> statemap;
private E currentState;
/**
* Initialize with an initialState
*
* @param initialState initial MachineState
* @param map the association of state and set of acceptable
* following
* states
*/
public MachineState(final E initialState,
final ConcurrentMap<E, EnumSet<E>> map) {
statemap = map;
currentState = initialState;
}
/**
* Initialize with an initialState but no association (Machine State is
* empty)
*
* @param initialState initial MachineState
*/
public MachineState(final E initialState) {
statemap = new ConcurrentHashMap<E, EnumSet<E>>();
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 final EnumSet<E> addNewAssociation(final E state,
final EnumSet<E> 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 final EnumSet<E> addNewAssociation(final Transition<E> 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 final EnumSet<E> removeAssociation(final E state) {
return statemap.remove(state);
}
/**
* Return the current application state.
*
* @return the current State
*/
public final E 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 final E setCurrent(final E desiredState)
throws IllegalFiniteStateException {
if (!isReachable(desiredState)) {
logger.debug("State {} not reachable from: {}", desiredState,
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 final E setDryCurrent(final E 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(final E desiredState) {
if (currentState == null || statemap == null) {
return false;
}
final 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 E setAsFinal(final E desiredState) {
logger.debug("New State: {} from {}", desiredState, currentState);
currentState = desiredState;
return currentState;
}
/**
* Release the Machine State
*/
public final void release() {
currentState = null;
statemap = null;
}
}