EventCalculator.java
/***************************************************************************
Copyright 2013 Emily Estes
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 net.metanotion.util;
import java.util.HashMap;
import java.util.Map;
/** A "next state calculator" helper for state machines.
A set of instances of event handlers are associated with the calculator. These handlers should be
stateless. A handler is associated with the java.lang.Class of the type of events it handles.
The interfaces and parent objects associated with that java.lang.Class are walked via reflection
to determine the full set of classes the handler works with.
@param <S> The type of state objects this state transition calculator instance operates on.
*/
public final class EventCalculator<S extends State> {
private final Map<Class,EventHandler<S>> eventHandlers = new HashMap<>();
private EventHandler<S> recurseEventClass(final Class klazz) {
EventHandler<S> handler = eventHandlers.get(klazz);
if(handler != null) { return handler; }
for(final Class c: klazz.getInterfaces()) {
handler = eventHandlers.get(c);
if(handler != null) { return handler; }
handler = recurseEventClass(c);
if(handler != null) { return handler; }
}
return null;
}
/** Calculate the next state from a given state/event pair.
@param event The instance of the event triggering the state transition.
@param state The current state.
@return The next state.
*/
public S nextState(final Object event, final S state) {
final EventHandler<S> handler = recurseEventClass(event.getClass());
if(handler == null) { throw new RuntimeException("Event class " + event.getClass() + " has no handler"); }
return handler.nextState(event, state);
}
/** Add an event handler to the calculator to handle a type of event.
@param event The type of the events this handler understands.
@param handler The handler to add.
@return The EventCalculator object.
*/
public EventCalculator<S> addHandler(Class event, EventHandler<S> handler) {
eventHandlers.put(event, handler);
return this;
}
}