1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package info.rolandkrueger.roklib.webapps.urldispatching;
26
27 import info.rolandkrueger.roklib.util.LoggingManager;
28 import info.rolandkrueger.roklib.util.conditionalengine.AbstractCondition;
29 import info.rolandkrueger.roklib.util.helper.CheckForNull;
30 import info.rolandkrueger.roklib.util.net.IURLProvider;
31 import info.rolandkrueger.roklib.webapps.urldispatching.urlparameters.EnumURLParameterErrors;
32 import info.rolandkrueger.roklib.webapps.urldispatching.urlparameters.IURLParameter;
33
34 import java.io.Serializable;
35 import java.io.UnsupportedEncodingException;
36 import java.net.MalformedURLException;
37 import java.net.URL;
38 import java.net.URLDecoder;
39 import java.net.URLEncoder;
40 import java.util.HashMap;
41 import java.util.Iterator;
42 import java.util.LinkedList;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.TreeMap;
46
47 import org.apache.log4j.Logger;
48
49 public abstract class AbstractURLActionHandler implements IURLActionHandler
50 {
51 private static final long serialVersionUID = 8450975393827044559L;
52 private static final String[] STRING_ARRAY_PROTOTYPE = new String[] {};
53 private static final Logger LOG = LoggingManager.getInstance ().getLogger (AbstractURLActionHandler.class);
54
55 private List<CommandForCondition> mCommandsForCondition;
56 private List<IURLParameter<?>> mURLParameters;
57 private List<String> mActionArgumentOrder;
58 protected List<IURLActionHandler> mHandlerChain;
59 private Map<String, AbstractURLActionHandler> mSubHandlers;
60 private Map<String, List<Serializable>> mActionArgumentMap;
61 private AbstractURLActionHandler mParentHandler;
62 private AbstractURLActionCommand mDefaultCommand;
63 protected String mActionName;
64 private String mActionURI;
65 private boolean mCaseSensitive = false;
66
67 public AbstractURLActionHandler (String actionName)
68 {
69 CheckForNull.check (actionName);
70 mActionName = actionName;
71 mActionURI = actionName;
72 mDefaultCommand = null;
73 }
74
75 protected void setCaseSensitive (boolean caseSensitive)
76 {
77 mCaseSensitive = caseSensitive;
78 if (mCaseSensitive & mSubHandlers != null)
79 {
80 Map<String, AbstractURLActionHandler> subHandlers = mSubHandlers;
81 mSubHandlers = new HashMap<String, AbstractURLActionHandler> (4);
82 mSubHandlers.putAll (subHandlers);
83 }
84 }
85
86 private Map<String, AbstractURLActionHandler> getSubHandlerMap ()
87 {
88 if (mSubHandlers == null)
89 {
90 if (! mCaseSensitive)
91 {
92 mSubHandlers = new TreeMap<String, AbstractURLActionHandler> (String.CASE_INSENSITIVE_ORDER);
93 }
94 else
95 {
96 mSubHandlers = new HashMap<String, AbstractURLActionHandler> (4);
97 }
98 }
99 return mSubHandlers;
100 }
101
102 public boolean isCaseSensitive ()
103 {
104 return mCaseSensitive;
105 }
106
107 public String getActionName ()
108 {
109 return mActionName;
110 }
111
112 public void setDefaultActionCommand (AbstractURLActionCommand command)
113 {
114 mDefaultCommand = command;
115 }
116
117 protected void registerURLParameter (IURLParameter<?> parameter)
118 {
119 if (parameter == null) return;
120 if (mURLParameters == null)
121 mURLParameters = new LinkedList<IURLParameter<?>> ();
122 if ( ! mURLParameters.contains (parameter))
123 mURLParameters.add (parameter);
124 }
125
126 protected void registerURLParameter (IURLParameter<?> parameter, boolean isOptional)
127 {
128 registerURLParameter (parameter);
129 parameter.setOptional (isOptional);
130 }
131
132 protected boolean haveRegisteredURLParametersErrors ()
133 {
134 if (mURLParameters == null) return false;
135 boolean result = false;
136
137 for (IURLParameter<?> parameter : mURLParameters)
138 {
139 result |= parameter.getError () != EnumURLParameterErrors.NO_ERROR;
140 }
141
142 return result;
143 }
144
145 public final AbstractURLActionCommand handleURL (List<String> pUriTokens,
146 Map<String, List<String>> pParameters, ParameterMode pParameterMode)
147 {
148 if (mCommandsForCondition != null)
149 {
150 for (CommandForCondition cfc : mCommandsForCondition)
151 {
152 if (cfc.mCondition.getBooleanValue () == true)
153 return cfc.mDefaultCommandForCondition;
154 }
155 }
156 if (mURLParameters != null)
157 {
158 if (pParameterMode == ParameterMode.QUERY)
159 {
160 for (IURLParameter<?> parameter : mURLParameters)
161 {
162 parameter.clearValue ();
163 parameter.consume (pParameters);
164 }
165 } else
166 {
167 List<String> parameterNames = new LinkedList <String> ();
168 for (IURLParameter<?> parameter : mURLParameters)
169 {
170 parameterNames.addAll (parameter.getParameterNames ());
171 }
172 if (pParameterMode == ParameterMode.DIRECTORY_WITH_NAMES)
173 {
174 Map<String, List<String>> parameters = new HashMap<String, List<String>> (4);
175 String parameterName;
176 String value;
177 for (Iterator<String> it = pUriTokens.iterator (); it.hasNext ();)
178 {
179 parameterName = it.next ();
180 try
181 {
182 parameterName = URLDecoder.decode (parameterName, "UTF-8");
183 } catch (UnsupportedEncodingException e)
184 {
185
186 }
187 value = "";
188 if (parameterNames.contains (parameterName))
189 {
190 it.remove ();
191 if (it.hasNext ())
192 {
193 value = it.next ();
194 try
195 {
196 value = URLDecoder.decode (value, "UTF-8");
197 } catch (UnsupportedEncodingException e)
198 {
199
200 }
201 it.remove ();
202 }
203 List<String> values = parameters.get (parameterName);
204 if (values == null)
205 {
206 values = new LinkedList<String> ();
207 parameters.put (parameterName, values);
208 }
209 values.add (value);
210 }
211 }
212 for (IURLParameter<?> parameter : mURLParameters)
213 {
214 parameter.clearValue ();
215 parameter.consume (parameters);
216 }
217 } else
218 {
219 List<String> valueList = new LinkedList<String> ();
220 for (IURLParameter<?> parameter : mURLParameters)
221 {
222 parameter.clearValue ();
223 if (pUriTokens.isEmpty ()) continue;
224 valueList.clear ();
225 int singleValueCount = parameter.getSingleValueCount ();
226 int i = 0;
227 while ( ! pUriTokens.isEmpty () && i < singleValueCount)
228 {
229 String token = pUriTokens.remove (0);
230 try
231 {
232 token = URLDecoder.decode (token, "UTF-8");
233 } catch (UnsupportedEncodingException e)
234 {
235
236 }
237 valueList.add (token);
238 ++i;
239 }
240 parameter.consumeList (valueList.toArray (STRING_ARRAY_PROTOTYPE));
241 }
242 }
243 }
244 }
245
246 if (mHandlerChain != null)
247 {
248 for (IURLActionHandler chainedHandler : mHandlerChain)
249 {
250 if (LOG.isTraceEnabled ())
251 {
252 LOG.trace ("Executing chained handler " + chainedHandler + " (" + mHandlerChain.size () + " chained handler(s) in list)");
253 }
254 AbstractURLActionCommand commandFromChain = chainedHandler.handleURL (pUriTokens, pParameters, pParameterMode);
255 if (commandFromChain != null) return commandFromChain;
256 }
257 }
258
259 return handleURLImpl (pUriTokens, pParameters, pParameterMode);
260 }
261
262 protected abstract AbstractURLActionCommand handleURLImpl (List<String> uriTokens,
263 Map<String, List<String>> parameters,
264 ParameterMode parameterMode);
265
266 private String urlEncode (String term)
267 {
268 try
269 {
270 return URLEncoder.encode (term, "UTF-8");
271 } catch (UnsupportedEncodingException e)
272 {
273
274 return term;
275 }
276 }
277
278 public final void addSubHandler (AbstractURLActionHandler subHandler)
279 {
280 CheckForNull.check (subHandler);
281 if (subHandler.mParentHandler != null)
282 throw new IllegalArgumentException (String.format ("This sub-handler instance has " +
283 "already been added to another action handler. This handler = '%s'; sub-handler = '%s'",
284 mActionName, subHandler.mActionName));
285 subHandler.mParentHandler = this;
286 subHandler.mActionURI = String.format ("%s%s%s", getActionURI (), "/", urlEncode (subHandler.mActionName));
287 getSubHandlerMap ().put (subHandler.mActionName, subHandler);
288 subHandler.setCaseSensitive (mCaseSensitive);
289 }
290
291 public String getActionURI ()
292 {
293 return mActionURI;
294 }
295
296 protected final void setParent (AbstractURLActionHandler parent)
297 {
298 mParentHandler = parent;
299 }
300
301 protected IURLProvider getContextURLProvider ()
302 {
303 if (mParentHandler != null)
304 {
305 return mParentHandler.getContextURLProvider ();
306 } else
307 {
308 throw new IllegalStateException ("Unable to provide IURLProvider object: this URL " +
309 "action handler or one of its ancestors hasn't yet been added to a " + URLActionDispatcher.class.getName ());
310 }
311 }
312
313 public URL getParameterizedActionURL (boolean clearAfterwards)
314 {
315 return getParameterizedActionURL (clearAfterwards, ParameterMode.QUERY);
316 }
317
318 public URL getParameterizedActionURL (boolean clearAfterwards, ParameterMode parameterMode)
319 {
320 return getParameterizedActionURL (clearAfterwards, parameterMode, false);
321 }
322
323 public URL getParameterizedActionURL (boolean clearAfterwards, ParameterMode parameterMode, boolean addHashMark)
324 {
325 return getParameterizedActionURL (clearAfterwards, parameterMode, addHashMark, null);
326 }
327
328 public URL getParameterizedActionURL (boolean clearAfterwards, ParameterMode parameterMode, boolean addHashMark, URL baseURL)
329 {
330 StringBuilder buf = new StringBuilder ();
331 if (addHashMark)
332 {
333 buf.append ('#');
334 buf.append (getActionURI ().substring (1));
335 } else
336 {
337 buf.append (getActionURI ());
338 }
339
340 boolean removeLastCharacter = false;
341 if (mActionArgumentMap != null && ! mActionArgumentMap.isEmpty ())
342 {
343 if (parameterMode == ParameterMode.QUERY)
344 {
345 buf.append ('?');
346 for (String argument : mActionArgumentOrder)
347 {
348 for (Serializable value : mActionArgumentMap.get (argument))
349 {
350 buf.append (urlEncode (argument)).append ('=').append (urlEncode (value.toString ()));
351 buf.append ('&');
352 removeLastCharacter = true;
353 }
354 }
355 } else
356 {
357 buf.append ('/');
358 for (String argument : mActionArgumentOrder)
359 {
360 for (Serializable value : mActionArgumentMap.get (argument))
361 {
362 if (parameterMode == ParameterMode.DIRECTORY_WITH_NAMES)
363 {
364 buf.append (urlEncode (argument)).append ('/');
365 }
366 buf.append (urlEncode (value.toString ()));
367 buf.append ('/');
368 removeLastCharacter = true;
369 }
370 }
371 }
372 }
373
374 if (removeLastCharacter) buf.setLength (buf.length () - 1);
375
376 try
377 {
378 URL url = baseURL == null ? getContextURLProvider ().getURL () : baseURL;
379 return new URL (url, buf.toString ());
380 } catch (MalformedURLException e)
381 {
382 throw new RuntimeException ("Unable to create URL object.", e);
383 } finally
384 {
385 if (clearAfterwards)
386 {
387 clearActionArguments ();
388 }
389 }
390 }
391
392 public final void addDefaultCommandForCondition (AbstractURLActionCommand command, AbstractCondition condition)
393 {
394 CheckForNull.check (command, condition);
395 if (mCommandsForCondition == null)
396 mCommandsForCondition = new LinkedList<CommandForCondition> ();
397 CommandForCondition cfc = new CommandForCondition ();
398 cfc.mDefaultCommandForCondition = command;
399 cfc.mCondition = condition;
400 mCommandsForCondition.add (cfc);
401 }
402
403 public void addToHandlerChain (IURLActionHandler handler)
404 {
405 CheckForNull.check (handler);
406 if (mHandlerChain == null)
407 {
408 mHandlerChain = new LinkedList<IURLActionHandler> ();
409 }
410 mHandlerChain.add (handler);
411 }
412
413
414
415
416
417
418 public void addActionArgument (String argumentName, Serializable ... argumentValues)
419 {
420 CheckForNull.check (argumentName);
421 if (mActionArgumentMap == null)
422 {
423 mActionArgumentMap = new HashMap<String, List<Serializable>> (4);
424 mActionArgumentOrder = new LinkedList<String> ();
425 }
426
427 List<Serializable> valueList = mActionArgumentMap.get (argumentName);
428 if (valueList == null)
429 {
430 valueList = new LinkedList<Serializable> ();
431 mActionArgumentMap.put (argumentName, valueList);
432 }
433 for (Serializable value : argumentValues)
434 {
435 if (value != null)
436 valueList.add (value);
437 }
438 if (valueList.isEmpty ())
439 {
440 mActionArgumentMap.remove (argumentName);
441 } else if (!mActionArgumentOrder.contains (argumentName))
442 {
443 mActionArgumentOrder.add (argumentName);
444 }
445 }
446
447 public void clearActionArguments ()
448 {
449 if (mActionArgumentMap != null)
450 {
451 mActionArgumentMap.clear ();
452 mActionArgumentOrder.clear ();
453 }
454 }
455
456 public final void clearDefaultCommands ()
457 {
458 mCommandsForCondition.clear ();
459 }
460
461 protected final AbstractURLActionCommand forwardToSubHandler (String handlerID, List<String> uriTokens,
462 Map<String, List<String>> parameters, ParameterMode parameterMode)
463 {
464
465 AbstractURLActionHandler subHandler = getSubHandlerMap ().get (handlerID);
466 if (subHandler == null) return mDefaultCommand;
467
468 return subHandler.handleURL (uriTokens, parameters, parameterMode);
469 }
470
471 private static class CommandForCondition implements Serializable
472 {
473 private static final long serialVersionUID = 2090692709855753816L;
474
475 private AbstractURLActionCommand mDefaultCommandForCondition;
476 private AbstractCondition mCondition;
477 }
478
479 public void getActionURLOverview (List<String> targetList)
480 {
481 StringBuilder buf = new StringBuilder ();
482 buf.append (getActionURI ());
483
484 if (mURLParameters != null && mURLParameters.size () > 0)
485 {
486 buf.append (" ? ");
487 for (IURLParameter<?> parameter : mURLParameters)
488 {
489 buf.append (parameter).append (", ");
490 }
491 buf.setLength (buf.length () - 2);
492 }
493 if (buf.length () > 0)
494 targetList.add (buf.toString ());
495 for (AbstractURLActionHandler subHandler : getSubHandlerMap ().values ())
496 {
497 subHandler.getActionURLOverview (targetList);
498 }
499 }
500
501 @Override
502 public String toString ()
503 {
504 return String.format ("%s='%s'", getClass ().getSimpleName (), mActionName);
505 }
506 }