--- /dev/null
+package com.indexdata.mkjsf.pazpar2.state;\r
+\r
+import java.io.Serializable;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.apache.log4j.Logger;\r
+\r
+import com.indexdata.mkjsf.pazpar2.commands.Pazpar2Command;\r
+import com.indexdata.mkjsf.utils.Utils;\r
+\r
+public class StateManager implements Serializable {\r
+ \r
+ private static final long serialVersionUID = 8152558351351730035L;\r
+\r
+ Map<String, Pazpar2State> states = new HashMap<String, Pazpar2State>();\r
+ String currentKey = "";\r
+ private static List<String> allCommands = new ArrayList<String>(Arrays.asList("init","ping","settings","search","stat","show","record","termlist","bytarget",\r
+ /* SP extras */ "auth","categories"));\r
+ Map<String,Boolean> pendingStateChanges = new HashMap<String,Boolean>();\r
+ private static Logger logger = Logger.getLogger(StateManager.class);\r
+ private List<StateListener> listeners = new ArrayList<StateListener>(); \r
+ \r
+ public StateManager () {\r
+ logger.info("Initializing a Pazpar2 state manager [" + Utils.objectId(this) + "]");\r
+ Pazpar2State initialState = new Pazpar2State();\r
+ states.put(initialState.getKey(), initialState);\r
+ currentKey = initialState.getKey();\r
+ for (String command : allCommands) {\r
+ pendingStateChanges.put(command, new Boolean(false));\r
+ } \r
+ }\r
+ \r
+ public void addStateListener(StateListener listener) {\r
+ listeners.add(listener);\r
+ }\r
+ \r
+ public void removeStateListener (StateListener listener) {\r
+ listeners.remove(listener);\r
+ }\r
+ \r
+ private void updateListeners (String command) {\r
+ for (StateListener lsnr : listeners) {\r
+ lsnr.stateUpdated(command);\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Registers a Pazpar2 command for execution.\r
+ * \r
+ * The state manager will update current state and flag that\r
+ * a request change was made but that it was not yet carried \r
+ * out against Pazpar2.\r
+ * \r
+ * Any command that is created or modified must be checked in\r
+ * like this to come into effect.\r
+ * \r
+ * @param command\r
+ */\r
+ public void checkIn(Pazpar2Command command) {\r
+ if (getCurrentState().stateMutating(command)) {\r
+ logger.info("State changed by: " + command.getCommandName());\r
+ Pazpar2State state = new Pazpar2State(getCurrentState(),command);\r
+ states.put(state.getKey(), state);\r
+ currentKey = state.getKey();\r
+ hasPendingStateChange(command.getCommandName(),new Boolean(true)); \r
+ logger.info("Updating " + listeners.size() + " listener(s) with state change from " + command);\r
+ updateListeners(command.getCommandName()); \r
+ } else {\r
+ logger.debug("Command " + command.getCommandName() + " not found to change the state [" + command.getEncodedQueryString() + "]");\r
+ }\r
+ }\r
+ \r
+ public Pazpar2Command getCommand (String commandName) {\r
+ return getCurrentState().getCommand(commandName);\r
+ }\r
+ \r
+ public Pazpar2State getCurrentState () {\r
+ return states.get(currentKey);\r
+ }\r
+ \r
+ /**\r
+ * Changes the current state key. Invoked from the UI to have the state \r
+ * manager switch to another state than the current one. \r
+ * \r
+ * @See The state field in pz2watch.xhtml<br/> \r
+ * The state listeners windowlocationhashListener() and StateListener()\r
+ * in listeners.js<br/>\r
+ * The method {@link com.indexdata.mkjsf.pazpar2.Pz2Service#handleQueryStateChanges}<br/>\r
+ * The class {@link com.indexdata.mkjsf.pazpar2.state.Pazpar2State}<br/> \r
+ * ... for a complete picture of browser history handling.\r
+ * \r
+ * @param key\r
+ */\r
+ public void setCurrentStateKey(String key) { \r
+ if (currentKey.equals(key)) {\r
+ logger.info("Ignoring request from UI to set state key, already has that key [" + key + "]");\r
+ } else {\r
+ logger.info("Request from UI to change state key from: [" + currentKey + "] to ["+key+"]");\r
+ if (states.get(key)==null) {\r
+ logger.error("Have no state registered for the key ["+ key +"].");\r
+ if (key == null || key.length()==0) {\r
+ logger.debug("Recived an empty key, retaining currentKey [" + currentKey + "]");\r
+ key = currentKey;\r
+ } else {\r
+ if (states.get(currentKey) != null) {\r
+ if (key.equals("#1")) {\r
+ logger.debug("Initial key created [" + key + "], but already got a state registered for the current key [" + currentKey + "]. Retaining current key.");\r
+ key = currentKey;\r
+ } else {\r
+ logger.info("Current search state cached under both new key [" + key + "] and current key [" + currentKey + "]");\r
+ states.put(key,states.get(currentKey));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ if (states.get(key).getCommand("search").equals(states.get(currentKey).getCommand("search"))) {\r
+ logger.debug("No search change detected as a consequence of processing the key ["+key+"]");\r
+ } else {\r
+ hasPendingStateChange("search",true);\r
+ }\r
+ if (states.get(key).getCommand("record").equals(states.get(currentKey).getCommand("record"))) {\r
+ logger.debug("No record change detected as a consequence of processing the key ["+key+"]");\r
+ } else {\r
+ hasPendingStateChange("record",true);\r
+ }\r
+ currentKey = key; \r
+ }\r
+ }\r
+\r
+ /**\r
+ * Sets a pending-state-change flag for the given command and notifies\r
+ * registered listeners. \r
+ * \r
+ * It is up to the listener to reset the flag as needed.\r
+ * \r
+ * @param command\r
+ * @param bool\r
+ */\r
+ public void hasPendingStateChange(String command, boolean bool) {\r
+ pendingStateChanges.put(command, new Boolean(bool));\r
+ }\r
+ \r
+ /**\r
+ * \r
+ * @param command\r
+ * @return true if there is a non-executed command change in this state\r
+ */\r
+ public boolean hasPendingStateChange (String command) {\r
+ return pendingStateChanges.get(command).booleanValue();\r
+ }\r
+\r
+}\r