Refactors state management
authorNiels Erik G. Nielsen <nielserik@indexdata.com>
Wed, 10 Apr 2013 17:05:59 +0000 (13:05 -0400)
committerNiels Erik G. Nielsen <nielserik@indexdata.com>
Wed, 10 Apr 2013 17:05:59 +0000 (13:05 -0400)
.. with the intend to decouple the complexities of setting up
commands and parameters from the maintenance of state (before
the state manager was involved in every setting of parameters,
in order to detect and record the change of state. Now it
just recieves a copy of the complete command after the fact, and
doesn't need to know about command parameters.

src/META-INF/resources/pz2utils/fieldlisteners.js
src/main/java/com/indexdata/pz2utils4jsf/pazpar2/CommandParameter.java
src/main/java/com/indexdata/pz2utils4jsf/pazpar2/Pz2Session.java
src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/Pazpar2State.java [new file with mode: 0644]
src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/QueryState.java [deleted file]
src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/QueryStates.java [deleted file]
src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/StateManager.java [new file with mode: 0644]

index d9cec5d..b0a3e6a 100644 (file)
@@ -82,7 +82,7 @@
       var stateKeyDoc = StringtoXML(field.textContent || field.text);\r
       var stateKeyValue = stateKeyDoc.childNodes[0].getAttribute("value");\r
       //console.log('Application hash update detected. New value: ' + stateKeyValue);\r
-      if (stateKeyValue != window.location.hash) {\r
+      if (stateKeyValue !== window.location.hash) {\r
         window.location.hash = stateKeyValue;\r
         //console.log("Browsers hash updated accordingly.");\r
       } else {\r
index d589160..212175b 100644 (file)
@@ -9,7 +9,6 @@ import java.util.Map;
 import org.apache.log4j.Logger;\r
 \r
 import com.indexdata.pz2utils4jsf.pazpar2.CommandParameter;\r
-import com.indexdata.pz2utils4jsf.pazpar2.Expression;\r
 \r
 public class CommandParameter implements Serializable {\r
 \r
index 66063c3..4d8f8c2 100644 (file)
@@ -27,7 +27,7 @@ import com.indexdata.pz2utils4jsf.pazpar2.data.ShowResponse;
 import com.indexdata.pz2utils4jsf.pazpar2.data.StatResponse;\r
 import com.indexdata.pz2utils4jsf.pazpar2.data.TermListsResponse;\r
 import com.indexdata.pz2utils4jsf.pazpar2.data.TermResponse;\r
-import com.indexdata.pz2utils4jsf.pazpar2.state.QueryStates;\r
+import com.indexdata.pz2utils4jsf.pazpar2.state.StateManager;\r
 import com.indexdata.pz2utils4jsf.utils.Utils;\r
 \r
 @Named @SessionScoped  \r
@@ -37,7 +37,7 @@ public class Pz2Session implements Pz2Interface {
   private static Logger logger = Logger.getLogger(Pz2Session.class);\r
   \r
   protected Map<String,Pazpar2ResponseData> dataObjects = new ConcurrentHashMap<String,Pazpar2ResponseData>();\r
-  protected QueryStates queryStates = new QueryStates();\r
+  protected StateManager stateManager = new StateManager();\r
   protected ErrorHelper errorHelper = null;\r
   \r
   protected List<ErrorInterface> configurationErrors = null;\r
@@ -76,7 +76,7 @@ public class Pz2Session implements Pz2Interface {
   }\r
 \r
   public void doSearch() { \r
-    queryStates.hasPendingStateChange("search",false);\r
+    stateManager.hasPendingStateChange("search",false);\r
     resetDataObjects();\r
     removeCommand("record");\r
     setCommandParameter("show",new CommandParameter("start","=",0));    \r
@@ -155,8 +155,10 @@ public class Pz2Session implements Pz2Interface {
   }\r
   \r
   public void setFacet (String facetKey, String term) {           \r
-    if (term != null && term.length()>0) {\r
-      queryStates.getCurrentState().setCommandParameterExpression("search","query",new Expression(facetKey,"=",term),queryStates);\r
+    if (term != null && term.length()>0) {   \r
+      Pazpar2Command command = getCommand("search");\r
+      command.getParameter("query").addExpression(new Expression(facetKey,"=",term));\r
+      stateManager.checkIn(command);\r
       doSearch();\r
     }            \r
   }\r
@@ -170,7 +172,9 @@ public class Pz2Session implements Pz2Interface {
   }\r
       \r
   public void removeFacet(String facetKey, String term) {\r
-    queryStates.getCurrentState().removeCommandParameterExpression("search","query",new Expression(facetKey,"=",term),queryStates);\r
+    Pazpar2Command command = getCommand("search");\r
+    command.getParameter("query").removeExpression(new Expression(facetKey,"=",term));\r
+    stateManager.checkIn(command);\r
     doSearch();\r
   }\r
   \r
@@ -290,11 +294,11 @@ public class Pz2Session implements Pz2Interface {
   \r
   \r
   public String getCurrentStateKey () {    \r
-    return queryStates.getCurrentStateKey();\r
+    return stateManager.getCurrentState().getKey();\r
   }\r
       \r
   public void setCurrentStateKey(String key) {       \r
-    queryStates.setCurrentStateKey(key);\r
+    stateManager.setCurrentStateKey(key);\r
   }\r
   \r
   public boolean hasConfigurationErrors () {\r
@@ -379,13 +383,13 @@ public class Pz2Session implements Pz2Interface {
   }\r
   \r
   protected void handleQueryStateChanges (String commands) {\r
-    if (queryStates.hasPendingStateChange("search") && hasQuery()) { \r
+    if (stateManager.hasPendingStateChange("search") && hasQuery()) { \r
       logger.debug("Found pending search change. Doing search before updating " + commands);      \r
       doSearch();\r
     } \r
-    if (queryStates.hasPendingStateChange("record") && ! commands.equals("record")) {        \r
+    if (stateManager.hasPendingStateChange("record") && ! commands.equals("record")) {        \r
       logger.debug("Found pending record ID change. Doing record before updating " + commands);\r
-      queryStates.hasPendingStateChange("record",false);\r
+      stateManager.hasPendingStateChange("record",false);\r
       if (getCommand("record").hasParameters()) {\r
         update("record");\r
       } else {\r
@@ -404,22 +408,40 @@ public class Pz2Session implements Pz2Interface {
     }\r
   }\r
 \r
+  /**\r
+   * Returns a Pazpar2 command 'detached' from the current Pazpar2 state.\r
+   * \r
+   * 'Detached' is meant to imply that this is a copy of a command in the \r
+   * current state, detached so as to NOT change the current state if \r
+   * modified. It can be viewed and executed, however. \r
+   * \r
+   * In order to modify the command with effect for subsequent searches,\r
+   * it must be checked back into the StateManager, which will\r
+   * then create a new current Pazpar2 state as needed.\r
+   *  \r
+   * @param name\r
+   * @return\r
+   */\r
   protected Pazpar2Command getCommand(String name) {\r
-    return queryStates.getCurrentState().getCommand(name);\r
+    return stateManager.checkOut(name);\r
   }\r
   \r
   protected void setCommandParameter(String commandName, CommandParameter parameter) {\r
     logger.debug("Setting parameter for " + commandName + ": " + parameter);\r
-    queryStates.getCurrentState().setCommandParameter(commandName, parameter, queryStates);    \r
+    Pazpar2Command command = getCommand(commandName);\r
+    command.setParameter(parameter);\r
+    stateManager.checkIn(command);    \r
   }\r
   \r
   \r
   protected void removeCommandParameter(String commandName, String parameterName) {\r
-    queryStates.getCurrentState().removeCommandParameter(commandName,parameterName,queryStates);    \r
+    Pazpar2Command command = getCommand(commandName);\r
+    command.removeParameter(parameterName);\r
+    stateManager.checkIn(command);    \r
   }\r
   \r
   protected void removeCommand (String commandName) {\r
-    queryStates.getCurrentState().removeCommand(commandName, queryStates);\r
+    stateManager.checkIn(new Pazpar2Command(commandName));\r
   }\r
     \r
   protected String getCommandParameterValue (String commandName, String parameterName, String defaultValue) {    \r
@@ -472,7 +494,7 @@ public class Pz2Session implements Pz2Interface {
     dataObjects.put("record", new RecordResponse());\r
     dataObjects.put("search", new SearchResponse());\r
   }\r
-\r
+  \r
   @Override\r
   public void setFilter(String filterExpression) {\r
     logger.debug("Setting filter to " + filterExpression);\r
diff --git a/src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/Pazpar2State.java b/src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/Pazpar2State.java
new file mode 100644 (file)
index 0000000..fba685e
--- /dev/null
@@ -0,0 +1,73 @@
+package com.indexdata.pz2utils4jsf.pazpar2.state;\r
+\r
+import java.util.Arrays;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import com.indexdata.pz2utils4jsf.pazpar2.Pazpar2Command;\r
+\r
+\r
+public class Pazpar2State {\r
+\r
+  String key = null;\r
+  Map<String,Pazpar2Command> commands = new HashMap<String,Pazpar2Command>();;\r
+\r
+  public Pazpar2State () {    \r
+    for (String command : Arrays.asList("init","ping","settings","search","stat","show","record","termlist","bytarget")) {\r
+      commands.put(command, new Pazpar2Command(command));\r
+    }\r
+    key = "#initial";\r
+  }\r
+  \r
+  public Pazpar2State (Pazpar2State previousState, Pazpar2Command newCommand) {\r
+    for (String commandName : previousState.commands.keySet()) {\r
+      this.commands.put(commandName, previousState.commands.get(commandName).copy());\r
+    }\r
+    this.commands.put(newCommand.getName(),newCommand);\r
+    this.key = getKey();           \r
+  }\r
+    \r
+  /**\r
+   * Generates a state key that can be used by the browser to pick\r
+   * up this state again at a later point in time.\r
+   * \r
+   * @return\r
+   */\r
+  public String getKey() {\r
+    if (key == null) {\r
+      StringBuilder querystatebuilder = new StringBuilder("#");\r
+      for (Pazpar2Command command : commands.values()) {\r
+        if (command.hasParameters()) {\r
+          querystatebuilder.append("||"+command.getName()+"::");\r
+          querystatebuilder.append(command.getValueWithExpressions());\r
+        }      \r
+      }            \r
+      key = querystatebuilder.toString();\r
+      return key;\r
+    } else {      \r
+      return key;\r
+    }\r
+  }\r
+  \r
+  /**\r
+   * Checks if the provided command represents a state change\r
+   * \r
+   * @param command\r
+   * @return true if the command causes a change of state\r
+   */\r
+  public boolean stateMutating (Pazpar2Command command) {\r
+    if (command == null) {\r
+      return true;\r
+    } else if (commands.get(command.getName()) == null) {\r
+      return true;\r
+    } else if ((command.equals(commands.get(command.getName())))) {\r
+      return false;      \r
+    } else {\r
+      return true;\r
+    }\r
+  } \r
+  \r
+  public Pazpar2Command getCommand(String name) {\r
+    return commands.get(name);\r
+  }\r
+}\r
diff --git a/src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/QueryState.java b/src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/QueryState.java
deleted file mode 100644 (file)
index 89c8888..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-package com.indexdata.pz2utils4jsf.pazpar2.state;\r
-\r
-import java.io.Serializable;\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-\r
-import org.apache.log4j.Logger;\r
-\r
-import com.indexdata.pz2utils4jsf.pazpar2.CommandParameter;\r
-import com.indexdata.pz2utils4jsf.pazpar2.Expression;\r
-import com.indexdata.pz2utils4jsf.pazpar2.Pazpar2Command;\r
-import com.indexdata.pz2utils4jsf.pazpar2.state.QueryState;\r
-import com.indexdata.pz2utils4jsf.pazpar2.state.QueryStates;\r
-\r
-public class QueryState implements Serializable {\r
-  \r
-  private static final long serialVersionUID = -1465676392610954168L;\r
-  private static Logger logger = Logger.getLogger(QueryState.class);\r
-  private Map<String,Pazpar2Command> pz2commands = new HashMap<String,Pazpar2Command>();\r
-  private String key = null;  \r
-  \r
-  public QueryState () {\r
-    for (String command : Arrays.asList("init","ping","settings","search","stat","show","record","termlist","bytarget")) {\r
-      pz2commands.put(command, new Pazpar2Command(command));\r
-    }\r
-  }    \r
-  \r
-  private QueryState copy() {    \r
-    QueryState newState = new QueryState();\r
-    for (String commandName : pz2commands.keySet()) {\r
-      newState.setCommand(pz2commands.get(commandName).copy());\r
-    }\r
-    return newState;\r
-  }\r
-    \r
-  public void setCommandParameter(String commandName, CommandParameter parameter, QueryStates queryStates) {\r
-    CommandParameter current = getCommand(commandName).getParameter(parameter.getName());\r
-    if (current != null && current.equals(parameter)) {\r
-      logger.debug("Recieved parameter but already have " + parameter.getValueWithExpressions() + " in this state. No state change.");\r
-    } else {\r
-      logger.debug("New command parameter received: " + parameter.getValueWithExpressions() + ". Initiating new state.");\r
-      QueryState newState = this.copy();\r
-      newState._setCommandParameter(commandName,parameter);\r
-      logger.debug("Old state: " + this);\r
-      logger.debug("New state: " + newState);        \r
-      queryStates.setCurrentState(newState);\r
-    }\r
-  }\r
-  \r
-  public void removeCommandParameter(String commandName, String parameterName, QueryStates queryStates) {\r
-    QueryState newState = this.copy();\r
-    newState._removeCommandParameter(commandName, parameterName);\r
-    queryStates.setCurrentState(newState);\r
-  }\r
-  \r
-  public void setCommandParameterExpression (String commandName, String parameterName, Expression expression, QueryStates queryStates) {\r
-    QueryState newState = this.copy();\r
-    newState._setCommandParameterExpression(commandName, parameterName, expression);\r
-    queryStates.setCurrentState(newState);\r
-  }\r
-  \r
-  public void _setCommandParameterExpression (String commandName, String parameterName, Expression expression) {\r
-    getCommand(commandName).getParameter(parameterName).addExpression(expression);\r
-  }\r
-  \r
-  public void removeCommandParameterExpression (String commandName, String parameterName, Expression expression, QueryStates queryStates) {\r
-    QueryState newState = this.copy();\r
-    newState._removeCommandParameterExpression(commandName, parameterName, expression);\r
-    queryStates.setCurrentState(newState);\r
-  }\r
-  \r
-  public void _removeCommandParameterExpression (String commandName, String parameterName, Expression expression) {\r
-    getCommand(commandName).getParameter(parameterName).removeExpression(expression);\r
-    \r
-  }\r
-\r
-  public void _setCommandParameter(String commandName, CommandParameter parameter) {\r
-    getCommand(commandName).setParameter(parameter);\r
-  }\r
-  \r
-  public void _removeCommandParameter(String commandName, String parameterName) {\r
-    getCommand(commandName).removeParameter(parameterName);\r
-  }\r
-  \r
-  public void _removeCommand(String commandName) {\r
-    getCommand(commandName).removeParameters();    \r
-  }\r
-\r
-  \r
-  public void removeCommand (String commandName, QueryStates queryStates) {\r
-    QueryState newState = this.copy();\r
-    newState._removeCommand(commandName);\r
-    queryStates.setCurrentState(newState);\r
-    \r
-  }\r
-\r
-  public Pazpar2Command getCommand(String name) {\r
-    return pz2commands.get(name);\r
-  }\r
-  \r
-  private void setCommand(Pazpar2Command command) {\r
-    pz2commands.put(command.getName(), command);\r
-  }\r
-  \r
-  public String getKey() {\r
-    if (key == null) {\r
-      StringBuilder querystatebuilder = new StringBuilder("#");\r
-      for (Pazpar2Command command : pz2commands.values()) {\r
-        if (command.hasParameters()) {\r
-          querystatebuilder.append("||"+command.getName()+"::");\r
-          querystatebuilder.append(command.getValueWithExpressions());\r
-        }      \r
-      }            \r
-      key = querystatebuilder.toString();\r
-      return key;\r
-    } else {      \r
-      return key;\r
-    }\r
-  }\r
-    \r
-  public String toString () {\r
-    return pz2commands.toString();\r
-  }\r
-    \r
-  public boolean equals (Object otherQueryState) {\r
-    if (otherQueryState instanceof QueryState) {\r
-      return this.toString().equals(otherQueryState.toString());              \r
-    } else {\r
-      return false;\r
-    }\r
-  }\r
-\r
-}\r
diff --git a/src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/QueryStates.java b/src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/QueryStates.java
deleted file mode 100644 (file)
index ea1264d..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.indexdata.pz2utils4jsf.pazpar2.state;\r
-\r
-import java.io.Serializable;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-\r
-import org.apache.log4j.Logger;\r
-\r
-import com.indexdata.pz2utils4jsf.pazpar2.Pazpar2Command;\r
-import com.indexdata.pz2utils4jsf.pazpar2.state.QueryState;\r
-import com.indexdata.pz2utils4jsf.pazpar2.state.QueryStates;\r
-\r
-public class QueryStates implements Serializable {\r
-  \r
-  private static final long serialVersionUID = 6131720167974584659L;\r
-  private static Logger logger = Logger.getLogger(QueryStates.class);\r
-    \r
-  Map<String, QueryState> queryStates = new HashMap<String, QueryState>();\r
-  String currentStateKey = "";\r
-  Map<String,Boolean> pendingStateChanges = new HashMap<String,Boolean>();  \r
-\r
-  public QueryStates () {\r
-    queryStates.put("#initial", new QueryState());\r
-    currentStateKey = "#initial";\r
-    for (String command : Pazpar2Command.allCommands) {\r
-      pendingStateChanges.put(command, new Boolean(false));\r
-    }\r
-  }\r
-  \r
-  public String getCurrentStateKey() {\r
-    return currentStateKey;\r
-  }\r
-\r
-  public void setCurrentStateKey(String key) {\r
-    \r
-    if (currentStateKey.equals(key)) {\r
-      logger.debug("setCurrentStateKey: no key change detected");\r
-    } else {\r
-      logger.debug("State key change. Was: [" + currentStateKey + "]. Will be ["+key+"]");\r
-      if (queryStates.get(key).getCommand("search").equals(getCurrentState().getCommand("search"))) {\r
-        logger.debug("No search change detected");\r
-      } else {\r
-        hasPendingStateChange("search",true);\r
-      }\r
-      if (queryStates.get(key).getCommand("record").equals(getCurrentState().getCommand("record"))) {\r
-        logger.debug("No record change detected");\r
-      } else {\r
-        hasPendingStateChange("record",true);\r
-      }\r
-      currentStateKey = key;\r
-    }\r
-  }\r
-    \r
-  public QueryState getCurrentState() {\r
-    if (queryStates.get(currentStateKey) == null) {      \r
-      return new QueryState();\r
-    } else {            \r
-      return queryStates.get(currentStateKey);\r
-    }\r
-  }\r
-  \r
-  public void setCurrentState(QueryState queryState) {\r
-    logger.debug("Setting current state: " + queryState.getKey());\r
-    queryStates.put(queryState.getKey(), queryState);\r
-    setCurrentStateKey(queryState.getKey());\r
-  }\r
-    \r
-  public void hasPendingStateChange(String command, boolean bool) {\r
-    pendingStateChanges.put(command, new Boolean(bool));\r
-  }\r
-  \r
-  public boolean hasPendingStateChange (String command) {\r
-    return pendingStateChanges.get(command).booleanValue();\r
-  }\r
-  \r
-}\r
diff --git a/src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/StateManager.java b/src/main/java/com/indexdata/pz2utils4jsf/pazpar2/state/StateManager.java
new file mode 100644 (file)
index 0000000..638effd
--- /dev/null
@@ -0,0 +1,86 @@
+package com.indexdata.pz2utils4jsf.pazpar2.state;\r
+\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import org.apache.log4j.Logger;\r
+\r
+import com.indexdata.pz2utils4jsf.pazpar2.Pazpar2Command;\r
+\r
+public class StateManager {\r
+  \r
+  Map<String, Pazpar2State> states = new HashMap<String, Pazpar2State>();\r
+  String currentKey = "";\r
+  Map<String,Boolean> pendingStateChanges = new HashMap<String,Boolean>();\r
+  private static Logger logger = Logger.getLogger(StateManager.class);\r
+  \r
+  public StateManager () {\r
+    Pazpar2State initialState = new Pazpar2State();\r
+    states.put(initialState.getKey(), initialState);\r
+    currentKey = initialState.getKey();\r
+    for (String command : Pazpar2Command.allCommands) {\r
+      pendingStateChanges.put(command, new Boolean(false));\r
+    }\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
+      Pazpar2State state = new Pazpar2State(getCurrentState(),command);\r
+      states.put(state.getKey(), state);\r
+      currentKey = state.getKey();\r
+      hasPendingStateChange(command.getName(),new Boolean(true));\r
+    } else {\r
+      logger.debug("Command " + command.getName() + " not found to change the state [" + command.getEncodedQueryString() + "]");\r
+    }\r
+  }\r
+  \r
+  public Pazpar2Command checkOut (String commandName) {\r
+    return getCurrentState().getCommand(commandName).copy();\r
+  }\r
+  \r
+  public Pazpar2State getCurrentState () {\r
+    return states.get(currentKey);\r
+  }\r
+  \r
+  public void setCurrentStateKey(String key) {    \r
+    if (currentKey.equals(key)) {\r
+      logger.debug("setCurrentStateKey: no key change detected");\r
+    } else {\r
+      logger.debug("State key change. Was: [" + currentKey + "]. Will be ["+key+"]");\r
+      if (states.get(key).getCommand("search").equals(states.get(currentKey).getCommand("search"))) {\r
+        logger.debug("No search change detected");\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");\r
+      } else {\r
+        hasPendingStateChange("record",true);\r
+      }\r
+      currentKey = key;\r
+    }\r
+  }\r
+\r
+  \r
+  public void hasPendingStateChange(String command, boolean bool) {\r
+    pendingStateChanges.put(command, new Boolean(bool));\r
+  }\r
+  \r
+  public boolean hasPendingStateChange (String command) {\r
+    return pendingStateChanges.get(command).booleanValue();\r
+  }\r
+\r
+}\r