/* * Copyright 2013 Google Inc. * * 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 org.bitcoinj.protocols.channels; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import java.util.Locale; import static com.google.common.base.Preconditions.checkNotNull; /** * A class representing a state machine, with limited transitions between states. * @param <State> An enum of states to use */ public class StateMachine<State extends Enum<State>> { private State currentState; private final Multimap<State, State> transitions; public StateMachine(State startState, Multimap<State, State> transitions) { currentState = checkNotNull(startState); this.transitions = checkNotNull(transitions); } /** * Checks that the machine is in the given state. Throws if it isn't. * @param requiredState */ public synchronized void checkState(State requiredState) throws IllegalStateException { if (requiredState != currentState) { throw new IllegalStateException(String.format(Locale.US, "Expected state %s, but in state %s", requiredState, currentState)); } } /** * Checks that the machine is in one of the given states. Throws if it isn't. * @param requiredStates */ public synchronized void checkState(State... requiredStates) throws IllegalStateException { for (State requiredState : requiredStates) { if (requiredState.equals(currentState)) { return; } } throw new IllegalStateException(String.format(Locale.US, "Expected states %s, but in state %s", Lists.newArrayList(requiredStates), currentState)); } /** * Transitions to a new state, provided that the required transition exists * @param newState * @throws IllegalStateException If no state transition exists from oldState to newState */ public synchronized void transition(State newState) throws IllegalStateException { if (transitions.containsEntry(currentState, newState)) { currentState = newState; } else { throw new IllegalStateException(String.format(Locale.US, "Attempted invalid transition from %s to %s", currentState, newState)); } } public synchronized State getState() { return currentState; } @Override public String toString() { return new StringBuilder().append('[').append(getState()).append(']').toString(); } }