Bugfixes to compound filter and limit settings
[mkjsf-moved-to-github.git] / src / main / java / com / indexdata / mkjsf / pazpar2 / commands / Pazpar2Command.java
1 package com.indexdata.mkjsf.pazpar2.commands;\r
2 \r
3 import java.io.Serializable;\r
4 import java.util.HashMap;\r
5 import java.util.Map;\r
6 import java.util.StringTokenizer;\r
7 \r
8 import org.apache.log4j.Logger;\r
9 \r
10 import com.indexdata.mkjsf.pazpar2.ClientCommandResponse;\r
11 import com.indexdata.mkjsf.pazpar2.HttpResponseWrapper;\r
12 import com.indexdata.mkjsf.pazpar2.Pz2Service;\r
13 import com.indexdata.mkjsf.pazpar2.SearchClient;\r
14 import com.indexdata.mkjsf.pazpar2.commands.sp.ServiceProxyCommand;\r
15 import com.indexdata.mkjsf.pazpar2.data.ResponseDataObject;\r
16 import com.indexdata.mkjsf.pazpar2.data.ResponseParser;\r
17 import com.indexdata.mkjsf.pazpar2.data.Responses;\r
18 \r
19 /**\r
20  * Represents a generic Pazpar2 or Service Proxy command with all its current parameters, and has\r
21  * methods for executing the command against the currently selected Pazpar2 service</p>\r
22  * <p>Being an abstract class it only has generic methods for getting and setting parameters. \r
23  * Implementing classes are supposed to create named getters and setters for convenient access\r
24  * to parameters from the UI.</p> \r
25  * <p>Parameters can be set with or without notifying the state manager.<p>\r
26  * \r
27  * <p><i>Note: Internally the application has to be able to set parameters without state changes \r
28  * - for instance to avoid eternal feedback when copying parameter from one state to the next. A \r
29  * setting from the UI should spawn a new search state however.</i></p>   \r
30  * \r
31  * @author Niels Erik\r
32  *\r
33  */\r
34 public abstract class Pazpar2Command implements Serializable  {\r
35   \r
36   private static Logger logger = Logger.getLogger(Pazpar2Command.class);\r
37   private static final long serialVersionUID = -6825491856480675917L;   \r
38   protected String name = "";\r
39   protected Map<String,CommandParameter> parameters = new HashMap<String,CommandParameter>();  \r
40   \r
41   public Pazpar2Command () {    \r
42   }\r
43   \r
44   public void setCommandName(String name) {\r
45     this.name = name;\r
46   }\r
47           \r
48   public Pazpar2Command (String name) {\r
49     this.name = name;    \r
50   }\r
51       \r
52   /**\r
53    * Commands must implement this method to provide a completely detached, deep clone of \r
54    * themselves.\r
55    * \r
56    * The clone is needed by the state manager to transfer commands with current setting \r
57    * from one state to the next.\r
58    * \r
59    * Whenever a non-standard attribute is added to a command class, the copy method must \r
60    * be updated to ensure that the new attribute is brought over as well. \r
61    *   \r
62    * @return a Pazpar2 command of the given type\r
63    */\r
64   public abstract Pazpar2Command copy ();\r
65           \r
66   public String getCommandName() {\r
67     return name;\r
68   }\r
69   \r
70   /**\r
71    * Executes the command with the currently selected parameters against \r
72    * the currently selected Pazpar2 service\r
73    * \r
74    * @return Response data object based on the Pazpar2 service response. \r
75    */\r
76   public ResponseDataObject run() {    \r
77     return run(Pz2Service.get().getSearchClient(),\r
78                Pz2Service.get().getPzresp());\r
79   }\r
80   \r
81   /**\r
82    * Executes the commands with the currently selected parameters, while adding\r
83    * the parameters provided in the vararg\r
84    * @param parameters A list of parameters on the form [key=value]\r
85    * \r
86    * @return Response data object based on the Pazpar2 service response\r
87    */\r
88   public ResponseDataObject runWith(String... parameters) {\r
89     for (String parameter : parameters) {\r
90       StringTokenizer tokenizer = new StringTokenizer(parameter,"=");\r
91       String name = (String) tokenizer.nextElement();\r
92       String value = (String) tokenizer.nextElement();\r
93       CommandParameter commandParameter = new CommandParameter(name,"=",value);\r
94       setParameterInState(commandParameter);\r
95     }\r
96     return run();\r
97   }\r
98 \r
99   /**\r
100    * Executes the commands with the currently selected parameters, while adding\r
101    * the parameters provided in the 'delimiter'-separated String.\r
102    * \r
103    * Note: This is for Glassfish/JBoss support. With Tomcat7 the method \r
104    *       runWith(String... parameters) can be used directly from EL \r
105    *       with a vararg \r
106    *  \r
107    * @param parameters A list of parameters separated by 'delimiter'\r
108    * @param delimiter The separator character of the String 'parameters' \r
109    * \r
110    * @return Response data object based on the Pazpar2 service response\r
111    */\r
112   public ResponseDataObject runWith2(String parameters, String delimiter) {    \r
113     StringTokenizer params = new StringTokenizer(parameters,delimiter);\r
114     String[] vararg = new String[params.countTokens()];\r
115     int i=0;\r
116     while (params.hasMoreTokens()) {\r
117       vararg[i++] = params.nextToken();\r
118     }\r
119     return runWith(vararg);\r
120   }\r
121     \r
122   /**\r
123    * Executes the command in a thread.  \r
124    * \r
125    * Note: Client and Responses must be provided because at this point \r
126    * CDI beans cannot be retrieved from within a thread.\r
127    * \r
128    * @param client\r
129    * @param pzresp\r
130    * @return\r
131    */\r
132   public ResponseDataObject run(SearchClient client,Responses pzresp) {\r
133     logger.debug("Running " + getCommandName() + " using " + client);    \r
134     HttpResponseWrapper httpResponse = client.executeCommand(this);\r
135     logger.debug("Parsing response for " + getCommandName());\r
136     ResponseDataObject responseObject = ResponseParser.getParser().getDataObject((ClientCommandResponse) httpResponse);\r
137     logger.trace("Storing response for " + getCommandName());\r
138     pzresp.put(getCommandName(), responseObject);\r
139     return responseObject;    \r
140   }\r
141   \r
142    \r
143   /**\r
144    * Sets a parameter on this command and notifies the state manager\r
145    * about the change\r
146    * \r
147    * @param parameter \r
148    */\r
149   public void setParameter (CommandParameter parameter) {\r
150     Pazpar2Command copy = this.copy();\r
151     logger.trace(name + " command: setting parameter [" + parameter.getName() + "=" + parameter.getValueWithExpressions() + "]");\r
152     copy.parameters.put(parameter.getName(),parameter);\r
153     checkInState(copy);\r
154   }\r
155   \r
156   /**\r
157    * Sets multiple parameters on the command and notifies the state\r
158    * manager -- once -- about the change\r
159    * \r
160    * @param params \r
161    */\r
162   public void setParameters (CommandParameter... params) {\r
163     Pazpar2Command copy = this.copy();\r
164     for (CommandParameter param : params) {\r
165       logger.trace(name + " command: setting parameter [" + param.getName() + "=" + param.getValueWithExpressions() + "]");\r
166       copy.parameters.put(param.getName(),param);\r
167     }\r
168     checkInState(copy);\r
169   }\r
170   \r
171   /**\r
172    * Sets multiple parameters on this command without notifying the state manager. \r
173    * Typically used when one parameter setting should automatically trigger\r
174    * other parameters to be reset to defaults etc. Intended to avoid \r
175    * useless proliferation of states  \r
176    * \r
177    * @param params\r
178    */\r
179   public void setParametersInState (CommandParameter... params) {    \r
180     for (CommandParameter param : params) {\r
181       logger.trace(name + " command: setting parameter [" + param.getName() + "=" + param.getValueWithExpressions() + "] silently");\r
182       parameters.put(param.getName(),param);\r
183     }    \r
184   }\r
185   \r
186   /**\r
187    * Sets a parameter on this command without notifying the state manager. \r
188    * Typically used when one parameter setting should automatically trigger\r
189    * other parameters to be reset to defaults etc. Intended to avoid \r
190    * useless proliferation of states  \r
191    * \r
192    * @param parameter\r
193    */    \r
194   public void setParameterInState (CommandParameter parameter) {\r
195     logger.trace(name + " command: setting parameter [" + parameter.getName() + "=" + parameter.getValueWithExpressions() + "] silently");\r
196     parameters.put(parameter.getName(),parameter);    \r
197   }\r
198   \r
199   \r
200   /**\r
201    * Retrieves a command parameter by parameter name\r
202    * \r
203    * @param name of the parameter\r
204    * @return CommandParameter\r
205    */\r
206   public CommandParameter getParameter (String name) {\r
207     return parameters.get(name);\r
208   }\r
209   \r
210   /**\r
211    * Removes a parameter completely and notifies the state manager\r
212    * about the change\r
213    * \r
214    * @param name of the parameter to remove\r
215    */\r
216   public void removeParameter (String name) {\r
217     Pazpar2Command copy = this.copy();\r
218     copy.parameters.remove(name);\r
219     checkInState(copy);\r
220   }\r
221   \r
222   public void removeParameterInState (String name) {\r
223     parameters.remove(name);\r
224   }\r
225   \r
226   /**\r
227    * Removes multiple parameters completely and notifies the state manager\r
228    * -- once -- about the change\r
229    * \r
230    * @param name of the parameter to remove\r
231    */  \r
232   public void removeParameters() {\r
233     Pazpar2Command copy = this.copy();\r
234     copy.parameters = new HashMap<String,CommandParameter>();\r
235     checkInState(copy);\r
236   }\r
237   \r
238   \r
239   /**\r
240    * Removes all parameters without notifying the state manager. For instance\r
241    * used in case of change of Pazpar2 service or renewed login to a service.\r
242    *  \r
243    */\r
244   public void removeParametersInState() {\r
245     parameters = new HashMap<String,CommandParameter>();    \r
246   }\r
247   \r
248   /**\r
249    * Adds an expression to an ordered list of expressions on a given parameter\r
250    * and notifies the state manager of the change\r
251    * \r
252    * @param parameterName name of the parameter to add the expression to\r
253    * @param expression\r
254    */\r
255   public void addExpression(String parameterName, Expression expression) {\r
256     Pazpar2Command copy = this.copy();\r
257     copy.getParameter(parameterName).addExpression(expression);\r
258     checkInState(copy);\r
259   }\r
260   \r
261   public void addExpressionInState (String parameterName, Expression expression) {\r
262     getParameter(parameterName).addExpression(expression);\r
263   }\r
264   \r
265   public void removeExpression(String parameterName, Expression expression) {\r
266     Pazpar2Command copy = this.copy();\r
267     copy.getParameter(parameterName).removeExpression(expression);\r
268     checkInState(copy);    \r
269   }\r
270   \r
271   public void removeExpressionsAfter(String parameterName, Expression expression,String... expressionFields) {\r
272     Pazpar2Command copy = this.copy();\r
273     copy.getParameter(parameterName).removeExpressionsAfter(expression,expressionFields);\r
274     checkInState(copy);    \r
275   }\r
276   \r
277   public void removeExpressions(String parameterName, String... expressionFields) {\r
278     if (this.getParameter(parameterName) != null) {\r
279       Pazpar2Command copy = this.copy();    \r
280       copy.getParameter(parameterName).removeExpressions(expressionFields);    \r
281       if (!getParameter(parameterName).hasValue() && !getParameter(parameterName).hasExpressions()) {\r
282         copy.parameters.remove(parameterName);\r
283       }\r
284       checkInState(copy);    \r
285     }\r
286   }\r
287   \r
288   public boolean hasParameters () {\r
289     return (parameters.keySet().size()>0);\r
290   }\r
291   \r
292   public boolean hasParameterValue(String parameterName) {\r
293     return (parameters.get(parameterName) != null && (parameters.get(parameterName).hasValue()));\r
294   }\r
295     \r
296   public String getEncodedQueryString () {\r
297     StringBuilder queryString = new StringBuilder("command="+name);\r
298     for (CommandParameter parameter : parameters.values()) {\r
299       if (parameter.hasValue() || parameter.hasExpressions()) {\r
300         queryString.append("&"+parameter.getEncodedQueryString());\r
301       }\r
302     }\r
303     return queryString.toString();\r
304   } \r
305     \r
306   public String getValueWithExpressions() {    \r
307     StringBuilder value = new StringBuilder("");\r
308     for (CommandParameter parameter : parameters.values()) {\r
309       if (parameter.hasValue() || parameter.hasExpressions()) {\r
310         value.append("&" + parameter.getName() + parameter.operator + parameter.getValueWithExpressions());\r
311       }\r
312    }\r
313     return value.toString();\r
314   }\r
315   \r
316   @Override\r
317   public boolean equals (Object otherCommand) {\r
318     logger.trace("Comparing commands ["+this.toString()+"] and ["+otherCommand.toString() +"]");\r
319     return\r
320         ((otherCommand instanceof Pazpar2Command)\r
321          && this.getValueWithExpressions().equals(((Pazpar2Command) otherCommand).getValueWithExpressions()));\r
322   }\r
323   \r
324   @Override\r
325   public int hashCode () {\r
326     return getValueWithExpressions().hashCode();\r
327   }\r
328   \r
329   public String toString () {\r
330     return parameters.toString();\r
331   }\r
332 \r
333   public String getParameterValue(String parameterName) {\r
334     return getParameter(parameterName)==null ? "" : getParameter(parameterName).getValueWithExpressions();    \r
335   }\r
336 \r
337   /*\r
338   public String getUrlEncodedParameterValue(String parameterName) {\r
339     return getParameter(parameterName).getEncodedQueryString();\r
340   }\r
341   */\r
342   \r
343   public void setSession (String sessionId) {\r
344     setParameter(new CommandParameter("session","=",sessionId));\r
345   }\r
346   \r
347   public String getSession() {\r
348     return getParameterValue("session");\r
349   } \r
350   \r
351   /**\r
352    * Notifies the state manager that this command changed a parameter\r
353    * \r
354    * @param command\r
355    */\r
356   protected void checkInState(Pazpar2Command command) {\r
357     Pz2Service.get().getStateMgr().checkIn(command);\r
358   }\r
359     \r
360   /**\r
361    * Implementing classes must provide their Service Proxy \r
362    * extension command if any extension parameters exists, \r
363    * or -- just to be polite -- 'this' if there is no\r
364    * Service Proxy extension to the given command.\r
365    * @return\r
366    */\r
367   public abstract ServiceProxyCommand getSp();\r
368      \r
369   /**\r
370    * Here implementing commands publish whether they only \r
371    * apply to the Service Proxy or can be executed \r
372    * against straight Pazpar2 as well. This is convenient for a \r
373    * UI that switches between service types either \r
374    * deployment time or run time.\r
375    *   \r
376    * @return false if the command applies to straight Pazpar2\r
377    */\r
378   public abstract boolean spOnly();  \r
379 }\r