uk.org.retep.util.state.StateEngine Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2007, Peter T Mount
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the retep.org.uk nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package uk.org.retep.util.state;
import java.util.HashMap;
import java.util.Map;
/**
* A state engine used to validate a state change between one state and another
* usually Enum's.
*
* @author peter
*/
public class StateEngine
{
private Map validStateMap;
private Map> sequenceMap;
private String stateName;
/** Creates a new instance of StateEngine */
public StateEngine()
{
validStateMap = new HashMap();
sequenceMap = new HashMap>();
}
private String getStateName()
{
return stateName == null ? "Unknown" : stateName;
}
public void setStateChange( T state, T... validStates )
{
validStateMap.put( state, validStates );
// Lazy initialise stateName t the first state set, as we cannot find the
// class name from the generics
if( stateName==null )
{
stateName = state.getClass().getSimpleName();
}
}
public T getNextState( T state )
throws UnknownStateException
{
T[] validStateChanges = getValidStates( state );
if( validStateChanges.length==0 )
{
return null;
}
return validStateChanges[0];
}
public void setStateSequence( T from, T to, T... sequence )
throws UnknownStateException
{
// Assert that from, to an the contents of sequence have all been defined
assertState( from );
assertState( to );
for( T t: sequence )
{
assertState( t );
}
Map map = sequenceMap.get( from );
if( map==null )
{
map = new HashMap();
sequenceMap.put( from, map );
}
map.put( to, sequence );
}
public T[] getStateChangeSequence( T from, T to )
throws
UnknownStateException,
InvalidStateChangeException
{
assertState( from );
assertState( to );
T[] sequence = null;
Map map = sequenceMap.get( from );
if( map==null )
{
throw new InvalidStateChangeException( from, to );
}
sequence = map.get( to );
if( sequence==null )
{
throw new InvalidStateChangeException( from, to );
}
return sequence;
}
public T[] getValidStates( T state )
throws UnknownStateException
{
T[] validStates = validStateMap.get( state );
// Throw IllegalStateException if the state is unknown
if( validStates==null )
{
throw new UnknownStateException( state );
}
return validStates;
}
/**
* Is it possible to change this state into the required state?
* @param newState DependencyState required
* @return true if it's valid, false if not
*/
public boolean isStateChangeValid( final T oldState, final T newState )
throws UnknownStateException
{
if( oldState==null )
{
throw new NullPointerException( "Provided old state is null" );
}
if( newState==null )
{
throw new NullPointerException( "Provided new state is null" );
}
for( T state: getValidStates( oldState ) )
{
if( newState.equals( state ) )
{
return true;
}
}
return false;
}
/**
* Is it possible to change this state into the required state?
* This will simply return if it is valid or throw InvalidStateChangeException
* if the state is invalid.
* @param newState DependencyState required
* @throws InvalidStateChangeException if the state change is invalid
*/
public void assertStateChange( final T oldState, final T newState )
throws
InvalidStateChangeException,
UnknownStateException
{
if( !isStateChangeValid( oldState, newState ) )
{
throw new InvalidStateChangeException( oldState, newState );
}
}
public void assertState( final T state )
throws UnknownStateException
{
if( !validStateMap.containsKey( state ) )
{
throw new UnknownStateException( state );
}
}
}