1 /* 2 * This file is part of Waarp Project (named also Waarp or GG). 3 * 4 * Copyright (c) 2019, Waarp SAS, and individual contributors by the @author 5 * tags. See the COPYRIGHT.txt in the distribution for a full listing of 6 * individual contributors. 7 * 8 * All Waarp Project is free software: you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or (at your 11 * option) any later version. 12 * 13 * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY 14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 15 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * Waarp . If not, see <http://www.gnu.org/licenses/>. 19 */ 20 package org.waarp.common.state; 21 22 import org.waarp.common.exception.IllegalFiniteStateException; 23 import org.waarp.common.logging.WaarpLogger; 24 import org.waarp.common.logging.WaarpLoggerFactory; 25 26 import java.util.EnumSet; 27 import java.util.concurrent.ConcurrentHashMap; 28 import java.util.concurrent.ConcurrentMap; 29 30 /** 31 * This is the base class for the basic support of Finite State Machine in 32 * GoldenGate. One need to implement 33 * an Enum class to use with it. <br> 34 * <br> 35 * Note: the type EnumSet< ? > is in fact of type EnumSet< EnumState > 36 * 37 * @param <E> 38 */ 39 public class MachineState<E extends Enum<E>> { 40 /** 41 * Internal Logger 42 */ 43 private static final WaarpLogger logger = 44 WaarpLoggerFactory.getLogger(MachineState.class); 45 46 private ConcurrentMap<E, EnumSet<E>> statemap; 47 private E currentState; 48 49 /** 50 * Initialize with an initialState 51 * 52 * @param initialState initial MachineState 53 * @param map the association of state and set of acceptable 54 * following 55 * states 56 */ 57 public MachineState(final E initialState, 58 final ConcurrentMap<E, EnumSet<E>> map) { 59 statemap = map; 60 currentState = initialState; 61 } 62 63 /** 64 * Initialize with an initialState but no association (Machine State is 65 * empty) 66 * 67 * @param initialState initial MachineState 68 */ 69 public MachineState(final E initialState) { 70 statemap = new ConcurrentHashMap<E, EnumSet<E>>(); 71 currentState = initialState; 72 } 73 74 /** 75 * Add a new association from one state to a set of acceptable following 76 * states (can replace an existing 77 * association) 78 * 79 * @param state 80 * @param set the new association 81 * 82 * @return the previous association if any 83 */ 84 public final EnumSet<E> addNewAssociation(final E state, 85 final EnumSet<E> set) { 86 return statemap.put(state, set); 87 } 88 89 /** 90 * Add a new association from one state to a set of acceptable following 91 * states (can replace an existing 92 * association) 93 * 94 * @param elt 95 * 96 * @return the previous association if any 97 */ 98 public final EnumSet<E> addNewAssociation(final Transition<E> elt) { 99 return statemap.put(elt.getState(), elt.getSet()); 100 } 101 102 /** 103 * Remove an association from one state to any acceptable following states 104 * 105 * @param state the state to remove any acceptable following states 106 * 107 * @return the previous association if any 108 */ 109 public final EnumSet<E> removeAssociation(final E state) { 110 return statemap.remove(state); 111 } 112 113 /** 114 * Return the current application state. 115 * 116 * @return the current State 117 */ 118 public final E getCurrent() { 119 return currentState; 120 } 121 122 /** 123 * Sets the current application state. 124 * 125 * @param desiredState 126 * 127 * @return the requested state, if it was reachable 128 * 129 * @throws IllegalFiniteStateException if the state is not allowed 130 */ 131 public final E setCurrent(final E desiredState) 132 throws IllegalFiniteStateException { 133 if (!isReachable(desiredState)) { 134 logger.debug("State {} not reachable from: {}", desiredState, 135 currentState); 136 throw new IllegalFiniteStateException( 137 desiredState + " not allowed from " + currentState); 138 } 139 return setAsFinal(desiredState); 140 } 141 142 /** 143 * Sets the current application state, but no exception if not compatible. 144 * 145 * @param desiredState 146 * 147 * @return the requested state, even if it was not reachable 148 */ 149 public final E setDryCurrent(final E desiredState) { 150 return setAsFinal(desiredState); 151 } 152 153 /** 154 * Determine if the given state is allowed to be next. 155 * 156 * @param desiredState desired MachineState 157 * 158 * @return True if the desiredState is valid from currentState 159 */ 160 private boolean isReachable(final E desiredState) { 161 if (currentState == null || statemap == null) { 162 return false; 163 } 164 final EnumSet<?> set = statemap.get(currentState); 165 if (set != null) { 166 return set.contains(desiredState); 167 } 168 return false; 169 } 170 171 /** 172 * Finalizes the new requested state 173 * 174 * @param desiredState 175 * 176 * @return the requested state 177 */ 178 private E setAsFinal(final E desiredState) { 179 logger.debug("New State: {} from {}", desiredState, currentState); 180 currentState = desiredState; 181 return currentState; 182 } 183 184 /** 185 * Release the Machine State 186 */ 187 public final void release() { 188 currentState = null; 189 statemap = null; 190 } 191 }