Merge branch 'master' of ssh://git.indexdata.com/home/git/private/mkjsf.git into...
[mkjsf-moved-to-github.git] / src / main / java / com / indexdata / mkjsf / pazpar2 / commands / Pazpar2Command.java
diff --git a/src/main/java/com/indexdata/mkjsf/pazpar2/commands/Pazpar2Command.java b/src/main/java/com/indexdata/mkjsf/pazpar2/commands/Pazpar2Command.java
new file mode 100644 (file)
index 0000000..a4fa51f
--- /dev/null
@@ -0,0 +1,346 @@
+package com.indexdata.mkjsf.pazpar2.commands;\r
+\r
+import java.io.Serializable;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.StringTokenizer;\r
+\r
+import org.apache.log4j.Logger;\r
+\r
+import com.indexdata.mkjsf.pazpar2.ClientCommandResponse;\r
+import com.indexdata.mkjsf.pazpar2.HttpResponseWrapper;\r
+import com.indexdata.mkjsf.pazpar2.Pz2Service;\r
+import com.indexdata.mkjsf.pazpar2.SearchClient;\r
+import com.indexdata.mkjsf.pazpar2.commands.sp.ServiceProxyCommand;\r
+import com.indexdata.mkjsf.pazpar2.data.ResponseDataObject;\r
+import com.indexdata.mkjsf.pazpar2.data.ResponseParser;\r
+import com.indexdata.mkjsf.pazpar2.data.Responses;\r
+\r
+/**\r
+ * Represents a generic Pazpar2 or Service Proxy command with all its current parameters, and has\r
+ * methods for executing the command against the currently selected Pazpar2 service</p>\r
+ * <p>Being an abstract class it only has generic methods for getting and setting parameters. \r
+ * Implementing classes are supposed to create named getters and setters for convenient access\r
+ * to parameters from the UI.</p> \r
+ * <p>Parameters can be set with or without notifying the state manager.<p>\r
+ * \r
+ * <p><i>Note: Internally the application has to be able to set parameters without state changes \r
+ * - for instance to avoid eternal feedback when copying parameter from one state to the next. A \r
+ * setting from the UI should spawn a new search state however.</i></p>   \r
+ * \r
+ * @author Niels Erik\r
+ *\r
+ */\r
+public abstract class Pazpar2Command implements Serializable  {\r
+  \r
+  private static Logger logger = Logger.getLogger(Pazpar2Command.class);\r
+  private static final long serialVersionUID = -6825491856480675917L;   \r
+  protected String name = "";\r
+  protected Map<String,CommandParameter> parameters = new HashMap<String,CommandParameter>();  \r
+  \r
+  public Pazpar2Command () {    \r
+  }\r
+  \r
+  public void setCommandName(String name) {\r
+    this.name = name;\r
+  }\r
+          \r
+  public Pazpar2Command (String name) {\r
+    this.name = name;    \r
+  }\r
+      \r
+  /**\r
+   * Commands must implement this method to provide an completely detached, deep clone of \r
+   * themselves.\r
+   * \r
+   * The clone is needed by the state manager to transfer commands with current setting \r
+   * from one state to the next.\r
+   * \r
+   * Whenever a non-standard attribute is added to a command class, the copy method must \r
+   * be updated to ensure that the new attribute is brought over as well. \r
+   *   \r
+   * @return a Pazpar2 command of the given type\r
+   */\r
+  public abstract Pazpar2Command copy ();\r
+          \r
+  public String getCommandName() {\r
+    return name;\r
+  }\r
+  \r
+  /**\r
+   * Executes the command with the currently selected parameters against \r
+   * the currently selected Pazpar2 service\r
+   * \r
+   * @return Response data object based on the Pazpar2 service response. \r
+   */\r
+  public ResponseDataObject run() {    \r
+    return run(Pz2Service.get().getSearchClient(),\r
+               Pz2Service.get().getPzresp());\r
+  }\r
+  \r
+  /**\r
+   * Executes the commands with the currently selected parameters, while adding\r
+   * the parameters provided\r
+   * @param parameters A list of parameters on the form [key=value]\r
+   * \r
+   * @return Response data object based on the Pazpar2 service response\r
+   */\r
+  public ResponseDataObject runWith(String... parameters) {\r
+    for (String parameter : parameters) {\r
+      StringTokenizer tokenizer = new StringTokenizer(parameter,"=");\r
+      String name = (String) tokenizer.nextElement();\r
+      String value = (String) tokenizer.nextElement();\r
+      CommandParameter commandParameter = new CommandParameter(name,"=",value);\r
+      setParameterInState(commandParameter);\r
+    }\r
+    return run();\r
+  }\r
+  \r
+  /**\r
+   * Executes the command in a thread.  \r
+   * \r
+   * Note: Client and Responses must be provided because at this point \r
+   * CDI beans cannot be retrieved from within a thread.\r
+   * \r
+   * @param client\r
+   * @param pzresp\r
+   * @return\r
+   */\r
+  public ResponseDataObject run(SearchClient client,Responses pzresp) {\r
+    logger.debug("Running " + getCommandName() + " using " + client);    \r
+    HttpResponseWrapper httpResponse = client.executeCommand(this);\r
+    logger.debug("Parsing response for " + getCommandName());\r
+    ResponseDataObject responseObject = ResponseParser.getParser().getDataObject((ClientCommandResponse) httpResponse);\r
+    logger.trace("Storing response for " + getCommandName());\r
+    pzresp.put(getCommandName(), responseObject);\r
+    return responseObject;    \r
+  }\r
+  \r
+   \r
+  /**\r
+   * Sets a parameter on this command and notifies the state manager\r
+   * about the change\r
+   * \r
+   * @param parameter \r
+   */\r
+  public void setParameter (CommandParameter parameter) {\r
+    Pazpar2Command copy = this.copy();\r
+    logger.trace(name + " command: setting parameter [" + parameter.getName() + "=" + parameter.getValueWithExpressions() + "]");\r
+    copy.parameters.put(parameter.getName(),parameter);\r
+    checkInState(copy);\r
+  }\r
+  \r
+  /**\r
+   * Sets multiple parameters on the command and notifies the state\r
+   * manager -- once -- about the change\r
+   * \r
+   * @param params \r
+   */\r
+  public void setParameters (CommandParameter... params) {\r
+    Pazpar2Command copy = this.copy();\r
+    for (CommandParameter param : params) {\r
+      logger.trace(name + " command: setting parameter [" + param.getName() + "=" + param.getValueWithExpressions() + "]");\r
+      copy.parameters.put(param.getName(),param);\r
+    }\r
+    checkInState(copy);\r
+  }\r
+  \r
+  /**\r
+   * Sets multiple parameters on this command without notifying the state manager. \r
+   * Typically used when one parameter setting should automatically trigger\r
+   * other parameters to be reset to defaults etc. Intended to avoid \r
+   * useless proliferation of states  \r
+   * \r
+   * @param params\r
+   */\r
+  public void setParametersInState (CommandParameter... params) {    \r
+    for (CommandParameter param : params) {\r
+      logger.trace(name + " command: setting parameter [" + param.getName() + "=" + param.getValueWithExpressions() + "] silently");\r
+      parameters.put(param.getName(),param);\r
+    }    \r
+  }\r
+  \r
+  /**\r
+   * Sets a parameter on this command without notifying the state manager. \r
+   * Typically used when one parameter setting should automatically trigger\r
+   * other parameters to be reset to defaults etc. Intended to avoid \r
+   * useless proliferation of states  \r
+   * \r
+   * @param parameter\r
+   */    \r
+  public void setParameterInState (CommandParameter parameter) {\r
+    logger.trace(name + " command: setting parameter [" + parameter.getName() + "=" + parameter.getValueWithExpressions() + "] silently");\r
+    parameters.put(parameter.getName(),parameter);    \r
+  }\r
+  \r
+  \r
+  /**\r
+   * Retrieves a command parameter by parameter name\r
+   * \r
+   * @param name of the parameter\r
+   * @return CommandParameter\r
+   */\r
+  public CommandParameter getParameter (String name) {\r
+    return parameters.get(name);\r
+  }\r
+  \r
+  /**\r
+   * Removes a parameter completely and notifies the state manager\r
+   * about the change\r
+   * \r
+   * @param name of the parameter to remove\r
+   */\r
+  public void removeParameter (String name) {\r
+    Pazpar2Command copy = this.copy();\r
+    copy.parameters.remove(name);\r
+    checkInState(copy);\r
+  }\r
+  \r
+  /**\r
+   * Removes multiple parameters completely and notifies the state manager\r
+   * -- once -- about the change\r
+   * \r
+   * @param name of the parameter to remove\r
+   */  \r
+  public void removeParameters() {\r
+    Pazpar2Command copy = this.copy();\r
+    copy.parameters = new HashMap<String,CommandParameter>();\r
+    checkInState(copy);\r
+  }\r
+  \r
+  \r
+  /**\r
+   * Removes all parameters without notifying the state manager. For instance\r
+   * used in case of change of Pazpar2 service or renewed login to a service.\r
+   *  \r
+   */\r
+  public void removeParametersInState() {\r
+    parameters = new HashMap<String,CommandParameter>();    \r
+  }\r
+  \r
+  /**\r
+   * Adds an expression to an ordered list of expressions on a given parameter\r
+   * and notifies the state manager of the change\r
+   * \r
+   * @param parameterName name of the parameter to add the expression to\r
+   * @param expression\r
+   */\r
+  public void addExpression(String parameterName, Expression expression) {\r
+    Pazpar2Command copy = this.copy();\r
+    copy.getParameter(parameterName).addExpression(expression);\r
+    checkInState(copy);\r
+  }\r
+  \r
+  public void removeExpression(String parameterName, Expression expression) {\r
+    Pazpar2Command copy = this.copy();\r
+    copy.getParameter(parameterName).removeExpression(expression);\r
+    checkInState(copy);    \r
+  }\r
+  \r
+  public void removeExpressionsAfter(String parameterName, Expression expression,String... expressionFields) {\r
+    Pazpar2Command copy = this.copy();\r
+    copy.getParameter(parameterName).removeExpressionsAfter(expression,expressionFields);\r
+    checkInState(copy);    \r
+  }\r
+  \r
+  public void removeExpressions(String parameterName, String... expressionFields) {\r
+    Pazpar2Command copy = this.copy();    \r
+    copy.getParameter(parameterName).removeExpressions(expressionFields);    \r
+    if (!getParameter(parameterName).hasValue() && !getParameter(parameterName).hasExpressions()) {\r
+      copy.parameters.remove(parameterName);\r
+    }\r
+    checkInState(copy);    \r
+  }\r
+  \r
+  public boolean hasParameters () {\r
+    return (parameters.keySet().size()>0);\r
+  }\r
+  \r
+  public boolean hasParameterValue(String parameterName) {\r
+    return (parameters.get(parameterName) != null && (parameters.get(parameterName).hasValue()));\r
+  }\r
+    \r
+  public String getEncodedQueryString () {\r
+    StringBuilder queryString = new StringBuilder("command="+name);\r
+    for (CommandParameter parameter : parameters.values()) {\r
+      if (parameter.hasValue() || parameter.hasExpressions()) {\r
+        queryString.append("&"+parameter.getEncodedQueryString());\r
+      }\r
+    }\r
+    return queryString.toString();\r
+  } \r
+    \r
+  public String getValueWithExpressions() {    \r
+    StringBuilder value = new StringBuilder("");\r
+    for (CommandParameter parameter : parameters.values()) {\r
+      if (parameter.hasValue() || parameter.hasExpressions()) {\r
+        value.append("&" + parameter.getName() + parameter.operator + parameter.getValueWithExpressions());\r
+      }\r
+   }\r
+    return value.toString();\r
+  }\r
+  \r
+  @Override\r
+  public boolean equals (Object otherCommand) {\r
+    logger.trace("Comparing commands ["+this.toString()+"] and ["+otherCommand.toString() +"]");\r
+    return\r
+        ((otherCommand instanceof Pazpar2Command)\r
+         && this.getValueWithExpressions().equals(((Pazpar2Command) otherCommand).getValueWithExpressions()));\r
+  }\r
+  \r
+  @Override\r
+  public int hashCode () {\r
+    return getValueWithExpressions().hashCode();\r
+  }\r
+  \r
+  public String toString () {\r
+    return parameters.toString();\r
+  }\r
+\r
+  public String getParameterValue(String parameterName) {\r
+    return getParameter(parameterName)==null ? "" : getParameter(parameterName).getValueWithExpressions();    \r
+  }\r
+\r
+  /*\r
+  public String getUrlEncodedParameterValue(String parameterName) {\r
+    return getParameter(parameterName).getEncodedQueryString();\r
+  }\r
+  */\r
+  \r
+  public void setSession (String sessionId) {\r
+    setParameter(new CommandParameter("session","=",sessionId));\r
+  }\r
+  \r
+  public String getSession() {\r
+    return getParameterValue("session");\r
+  } \r
+  \r
+  /**\r
+   * Notifies the state manager that this command changed a parameter\r
+   * \r
+   * @param command\r
+   */\r
+  protected void checkInState(Pazpar2Command command) {\r
+    Pz2Service.get().getStateMgr().checkIn(command);\r
+  }\r
+    \r
+  /**\r
+   * Implementing classes must provide their Service Proxy \r
+   * extension command if any extension parameters exists, \r
+   * or -- just to be polite -- 'this' if there is no\r
+   * Service Proxy extension to the given command.\r
+   * @return\r
+   */\r
+  public abstract ServiceProxyCommand getSp();\r
+     \r
+  /**\r
+   * Here implementing commands publish whether they only \r
+   * apply to the Service Proxy or can be executed \r
+   * against straight Pazpar2 as well. This is convenient for a \r
+   * UI that switches between service types either \r
+   * deployment time or run time.\r
+   *   \r
+   * @return false if the command applies to straight Pazpar2\r
+   */\r
+  public abstract boolean spOnly();  \r
+}\r