1 package org.apache.turbine.util.parser;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.beans.IndexedPropertyDescriptor;
23 import java.beans.Introspector;
24 import java.beans.PropertyDescriptor;
25 import java.io.UnsupportedEncodingException;
26 import java.lang.reflect.Method;
27 import java.math.BigDecimal;
28 import java.text.DateFormat;
29 import java.text.NumberFormat;
30 import java.text.ParseException;
31 import java.text.ParsePosition;
32 import java.util.Calendar;
33 import java.util.Collections;
34 import java.util.Date;
35 import java.util.Enumeration;
36 import java.util.GregorianCalendar;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.Locale;
40 import java.util.Map;
41 import java.util.Set;
42
43 import org.apache.commons.collections.iterators.ArrayIterator;
44 import org.apache.commons.lang.ArrayUtils;
45 import org.apache.commons.lang.StringUtils;
46 import org.apache.commons.logging.Log;
47 import org.apache.commons.logging.LogFactory;
48 import org.apache.torque.om.NumberKey;
49 import org.apache.torque.om.StringKey;
50 import org.apache.turbine.TurbineConstants;
51 import org.apache.turbine.util.DateSelector;
52 import org.apache.turbine.util.TimeSelector;
53 import org.apache.turbine.util.pool.RecyclableSupport;
54
55 /***
56 * BaseValueParser is a base class for classes that need to parse
57 * name/value Parameters, for example GET/POST data or Cookies
58 * (DefaultParameterParser and DefaultCookieParser).
59 *
60 * <p>It can also be used standalone, for an example see DataStreamParser.
61 *
62 * <p>NOTE: The name= portion of a name=value pair may be converted
63 * to lowercase or uppercase when the object is initialized and when
64 * new data is added. This behaviour is determined by the url.case.folding
65 * property in TurbineResources.properties. Adding a name/value pair may
66 * overwrite existing name=value pairs if the names match:
67 *
68 * <pre>
69 * ValueParser vp = new BaseValueParser();
70 * vp.add("ERROR",1);
71 * vp.add("eRrOr",2);
72 * int result = vp.getInt("ERROR");
73 * </pre>
74 *
75 * In the above example, result is 2.
76 *
77 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
78 * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a>
79 * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
80 * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
81 * @author <a href="mailto:seade@backstagetech.com.au">Scott Eade</a>
82 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
83 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
84 * @version $Id: BaseValueParser.java 646751 2008-04-10 10:55:06Z tv $
85 */
86 public class BaseValueParser
87 extends RecyclableSupport
88 implements ValueParser
89 {
90 /*** Logging */
91 private static Log log = LogFactory.getLog(BaseValueParser.class);
92
93 /*** String values which would evaluate to Boolean.TRUE */
94 private static String[] trueValues = {"TRUE","T","YES","Y","1","ON"};
95
96 /*** String values which would evaluate to Boolean.FALSE */
97 private static String[] falseValues = {"FALSE","F","NO","N","0","OFF"};
98
99 /***
100 * Random access storage for parameter data. The keys must always be
101 * Strings. The values will be arrays of Strings.
102 */
103 private Map parameters = new HashMap();
104
105 /*** The character encoding to use when converting to byte arrays */
106 private String characterEncoding = TurbineConstants.PARAMETER_ENCODING_DEFAULT;
107
108 /*** The locale to use when converting dates, floats and decimals */
109 private Locale locale = Locale.getDefault();
110
111 /*** The DateFormat to use for converting dates */
112 private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale);
113
114 /*** The NumberFormat to use when converting floats and decimals */
115 private NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
116
117 /***
118 * A static version of the convert method, which
119 * trims the string data and applies the conversion specified in
120 * the property given by URL_CASE_FOLDING. It returns a new
121 * string so that it does not destroy the value data.
122 *
123 * @param value A String to be processed.
124 * @return A new String converted to lowercase and trimmed.
125 * @deprecated Use ParserUtils.convertAndTrim(value).
126 */
127 public static String convertAndTrim(String value)
128 {
129 return ParserUtils.convertAndTrim(value);
130 }
131
132 /***
133 * Default constructor
134 */
135 public BaseValueParser()
136 {
137 this(TurbineConstants.PARAMETER_ENCODING_DEFAULT);
138 }
139
140 /***
141 * Constructor that takes a character encoding
142 */
143 public BaseValueParser(String characterEncoding)
144 {
145 this(characterEncoding, Locale.getDefault());
146 }
147
148 /***
149 * Constructor that takes a character encoding and a locale
150 */
151 public BaseValueParser(String characterEncoding, Locale locale)
152 {
153 super();
154 setCharacterEncoding(characterEncoding);
155 setLocale(locale);
156 }
157
158 /***
159 * Recycles the parser with a character encoding.
160 *
161 * @param characterEncoding the character encoding.
162 *
163 * @todo Is this method used anywhere? Does it make any sense at all?
164 */
165 public void recycle(String characterEncoding)
166 {
167 setCharacterEncoding(characterEncoding);
168 super.recycle();
169 }
170
171 /***
172 * Disposes the parser.
173 */
174 public void dispose()
175 {
176 clear();
177 super.dispose();
178 }
179
180 /***
181 * Clear all name/value pairs out of this object.
182 */
183 public void clear()
184 {
185 parameters.clear();
186 }
187
188 /***
189 * Set the character encoding that will be used by this ValueParser.
190 */
191 public void setCharacterEncoding(String s)
192 {
193 characterEncoding = s;
194 }
195
196 /***
197 * Get the character encoding that will be used by this ValueParser.
198 */
199 public String getCharacterEncoding()
200 {
201 return characterEncoding;
202 }
203
204 /***
205 * Set the locale that will be used by this ValueParser.
206 */
207 public void setLocale(Locale l)
208 {
209 locale = l;
210 setDateFormat(DateFormat.getDateInstance(DateFormat.SHORT, locale));
211 setNumberFormat(NumberFormat.getNumberInstance(locale));
212 }
213
214 /***
215 * Get the locale that will be used by this ValueParser.
216 */
217 public Locale getLocale()
218 {
219 return locale;
220 }
221
222 /***
223 * Set the date format that will be used by this ValueParser.
224 */
225 public void setDateFormat(DateFormat df)
226 {
227 dateFormat = df;
228 }
229
230 /***
231 * Get the date format that will be used by this ValueParser.
232 */
233 public DateFormat getDateFormat()
234 {
235 return dateFormat;
236 }
237
238 /***
239 * Set the number format that will be used by this ValueParser.
240 */
241 public void setNumberFormat(NumberFormat nf)
242 {
243 numberFormat = nf;
244 }
245
246 /***
247 * Get the number format that will be used by this ValueParser.
248 */
249 public NumberFormat getNumberFormat()
250 {
251 return numberFormat;
252 }
253
254 /***
255 * Add a name/value pair into this object.
256 *
257 * @param name A String with the name.
258 * @param value A double with the value.
259 */
260 public void add(String name, double value)
261 {
262 add(name, numberFormat.format(value));
263 }
264
265 /***
266 * Add a name/value pair into this object.
267 *
268 * @param name A String with the name.
269 * @param value An int with the value.
270 */
271 public void add(String name, int value)
272 {
273 add(name, (long)value);
274 }
275
276 /***
277 * Add a name/value pair into this object.
278 *
279 * @param name A String with the name.
280 * @param value An Integer with the value.
281 */
282 public void add(String name, Integer value)
283 {
284 if (value != null)
285 {
286 add(name, value.intValue());
287 }
288 }
289
290 /***
291 * Add a name/value pair into this object.
292 *
293 * @param name A String with the name.
294 * @param value A long with the value.
295 */
296 public void add(String name, long value)
297 {
298 add(name, Long.toString(value));
299 }
300
301 /***
302 * Add a name/value pair into this object.
303 *
304 * @param name A String with the name.
305 * @param value A long with the value.
306 */
307 public void add(String name, String value)
308 {
309 if (value != null)
310 {
311 String [] items = getParam(name);
312 items = (String []) ArrayUtils.add(items, value);
313 putParam(name, items);
314 }
315 }
316
317 /***
318 * Add an array of Strings for a key. This
319 * is simply adding all the elements in the
320 * array one by one.
321 *
322 * @param name A String with the name.
323 * @param value A String Array.
324 */
325 public void add(String name, String [] value)
326 {
327
328
329 if (value != null)
330 {
331 for (int i = 0 ; i < value.length; i++)
332 {
333 if (value[i] != null)
334 {
335 add(name, value[i]);
336 }
337 }
338 }
339 }
340
341 /***
342 * Add a String parameters. If there are any Strings already
343 * associated with the name, append to the array. This is used
344 * for handling parameters from multipart POST requests.
345 *
346 * @param name A String with the name.
347 * @param value A String with the value.
348 *
349 * @deprecated Use add(name, value) instead.
350 */
351 public void append(String name, String value)
352 {
353 add(name, value);
354 }
355
356 /***
357 * Removes the named parameter from the contained hashtable. Wraps to the
358 * contained <code>Map.remove()</code>.
359 *
360 * @return The value that was mapped to the key (a <code>String[]</code>)
361 * or <code>null</code> if the key was not mapped.
362 */
363 public Object remove(String name)
364 {
365 return parameters.remove(convert(name));
366 }
367
368 /***
369 * Trims the string data and applies the conversion specified in
370 * the property given by URL_CASE_FOLDING. It returns a new
371 * string so that it does not destroy the value data.
372 *
373 * @param value A String to be processed.
374 * @return A new String converted to lowercase and trimmed.
375 */
376 public String convert(String value)
377 {
378 return ParserUtils.convertAndTrim(value);
379 }
380
381 /***
382 * Determine whether a given key has been inserted. All keys are
383 * stored in lowercase strings, so override method to account for
384 * this.
385 *
386 * @param key An Object with the key to search for.
387 * @return True if the object is found.
388 */
389 public boolean containsKey(Object key)
390 {
391 return parameters.containsKey(convert(String.valueOf(key)));
392 }
393
394 /***
395 * Check for existence of key_day, key_month and key_year
396 * parameters (as returned by DateSelector generated HTML).
397 *
398 * @param key A String with the selector name.
399 * @return True if keys are found.
400 */
401 public boolean containsDateSelectorKeys(String key)
402 {
403 return (containsKey(key + DateSelector.DAY_SUFFIX) &&
404 containsKey(key + DateSelector.MONTH_SUFFIX) &&
405 containsKey(key + DateSelector.YEAR_SUFFIX));
406 }
407
408 /***
409 * Check for existence of key_hour, key_minute and key_second
410 * parameters (as returned by TimeSelector generated HTML).
411 *
412 * @param key A String with the selector name.
413 * @return True if keys are found.
414 */
415 public boolean containsTimeSelectorKeys(String key)
416 {
417 return (containsKey(key + TimeSelector.HOUR_SUFFIX) &&
418 containsKey(key + TimeSelector.MINUTE_SUFFIX) &&
419 containsKey(key + TimeSelector.SECOND_SUFFIX));
420 }
421
422 /***
423 * Get an enumerator for the parameter keys.
424 *
425 * @return An <code>enumerator</code> of the keys.
426 * @deprecated use {@link #keySet} instead.
427 */
428 public Enumeration keys()
429 {
430 return Collections.enumeration(keySet());
431 }
432
433 /***
434 * Gets the set of keys
435 *
436 * @return A <code>Set</code> of the keys.
437 */
438 public Set keySet()
439 {
440 return parameters.keySet();
441 }
442
443 /***
444 * Returns all the available parameter names.
445 *
446 * @return A object array with the keys.
447 */
448 public Object[] getKeys()
449 {
450 return keySet().toArray();
451 }
452
453 /***
454 * Returns a Boolean object for the given string. If the value
455 * can not be parsed as a boolean, null is returned.
456 * <p>
457 * Valid values for true: true, t, on, 1, yes, y<br>
458 * Valid values for false: false, f, off, 0, no, n<br>
459 * <p>
460 * The string is compared without reguard to case.
461 *
462 * @param string A String with the value.
463 * @return A Boolean.
464 */
465 private Boolean parseBoolean(String string)
466 {
467 Boolean result = null;
468 String value = StringUtils.trim(string);
469
470 if (StringUtils.isNotEmpty(value))
471 {
472 for (int cnt = 0;
473 cnt < Math.max(trueValues.length, falseValues.length); cnt++)
474 {
475
476 if ((cnt < trueValues.length) &&
477 value.equalsIgnoreCase(trueValues[cnt]))
478 {
479 result = Boolean.TRUE;
480 break;
481 }
482
483 if ((cnt < falseValues.length) &&
484 value.equalsIgnoreCase(falseValues[cnt]))
485 {
486 result = Boolean.FALSE;
487 break;
488 }
489 }
490
491 if (result == null)
492 {
493 if (log.isWarnEnabled())
494 {
495 log.warn("Parameter with value of ("
496 + value + ") could not be converted to a Boolean");
497 }
498 }
499 }
500
501 return result;
502 }
503
504 /***
505 * Return a boolean for the given name. If the name does not
506 * exist, return defaultValue.
507 *
508 * @param name A String with the name.
509 * @param defaultValue The default value.
510 * @return A boolean.
511 */
512 public boolean getBoolean(String name, boolean defaultValue)
513 {
514 Boolean result = getBooleanObject(name);
515 return (result == null ? defaultValue : result.booleanValue());
516 }
517
518 /***
519 * Return a boolean for the given name. If the name does not
520 * exist, return false.
521 *
522 * @param name A String with the name.
523 * @return A boolean.
524 */
525 public boolean getBoolean(String name)
526 {
527 return getBoolean(name, false);
528 }
529
530 /***
531 * Return an array of booleans for the given name. If the name does
532 * not exist, return null.
533 *
534 * @param name A String with the name.
535 * @return A boolean[].
536 */
537 public boolean[] getBooleans(String name)
538 {
539 boolean[] result = null;
540 String value[] = getParam(name);
541 if (value != null)
542 {
543 result = new boolean[value.length];
544 for (int i = 0; i < value.length; i++)
545 {
546 Boolean bool = parseBoolean(value[i]);
547 result[i] = (bool == null ? false : bool.booleanValue());
548 }
549 }
550 return result;
551 }
552
553 /***
554 * Returns a Boolean object for the given name. If the parameter
555 * does not exist or can not be parsed as a boolean, null is returned.
556 * <p>
557 * Valid values for true: true, on, 1, yes<br>
558 * Valid values for false: false, off, 0, no<br>
559 * <p>
560 * The string is compared without reguard to case.
561 *
562 * @param name A String with the name.
563 * @return A Boolean.
564 */
565 public Boolean getBooleanObject(String name)
566 {
567 return parseBoolean(getString(name));
568 }
569
570 /***
571 * Returns a Boolean object for the given name. If the parameter
572 * does not exist or can not be parsed as a boolean, null is returned.
573 * <p>
574 * Valid values for true: true, on, 1, yes<br>
575 * Valid values for false: false, off, 0, no<br>
576 * <p>
577 * The string is compared without reguard to case.
578 *
579 * @param name A String with the name.
580 * @param defaultValue The default value.
581 * @return A Boolean.
582 */
583 public Boolean getBooleanObject(String name, Boolean defaultValue)
584 {
585 Boolean result = getBooleanObject(name);
586 return (result == null ? defaultValue : result);
587 }
588
589 /***
590 * Return a Boolean for the given name. If the name does not
591 * exist, return defaultValue.
592 *
593 * @param name A String with the name.
594 * @param defaultValue The default value.
595 * @return A Boolean.
596 * @deprecated use {@link #getBooleanObject} instead
597 */
598 public Boolean getBool(String name, boolean defaultValue)
599 {
600
601 return getBooleanObject(name, new Boolean(defaultValue));
602 }
603
604 /***
605 * Return a Boolean for the given name. If the name does not
606 * exist, return false.
607 *
608 * @param name A String with the name.
609 * @return A Boolean.
610 * @deprecated use {@link #getBooleanObject(String)} instead
611 */
612 public Boolean getBool(String name)
613 {
614 return getBooleanObject(name, Boolean.FALSE);
615 }
616
617 /***
618 * Return an array of Booleans for the given name. If the name does
619 * not exist, return null.
620 *
621 * @param name A String with the name.
622 * @return A Boolean[].
623 */
624 public Boolean[] getBooleanObjects(String name)
625 {
626 Boolean[] result = null;
627 String value[] = getParam(name);
628 if (value != null)
629 {
630 result = new Boolean[value.length];
631 for (int i = 0; i < value.length; i++)
632 {
633 result[i] = parseBoolean(value[i]);
634 }
635 }
636 return result;
637 }
638
639 /***
640 * Return a {@link Number} for the given string.
641 *
642 * @param string A String with the value.
643 * @return A Number.
644 *
645 */
646 private Number parseNumber(String string)
647 {
648 Number result = null;
649 String value = StringUtils.trim(string);
650
651 if (StringUtils.isNotEmpty(value))
652 {
653 ParsePosition pos = new ParsePosition(0);
654 Number number = numberFormat.parse(value, pos);
655
656 if (pos.getIndex() == value.length())
657 {
658
659 result = number;
660 }
661 else
662 {
663 if (log.isWarnEnabled())
664 {
665 log.warn("Parameter with value of ("
666 + value + ") could not be converted to a Number at position " + pos.getIndex());
667 }
668 }
669 }
670
671 return result;
672 }
673
674 /***
675 * Return a {@link Number} for the given name. If the name does not
676 * exist, return null. This is the base function for all numbers.
677 *
678 * @param name A String with the name.
679 * @return A Number.
680 *
681 */
682 private Number getNumber(String name)
683 {
684 return parseNumber(getString(name));
685 }
686
687 /***
688 * Return a double for the given name. If the name does not
689 * exist, return defaultValue.
690 *
691 * @param name A String with the name.
692 * @param defaultValue The default value.
693 * @return A double.
694 */
695 public double getDouble(String name, double defaultValue)
696 {
697 Number number = getNumber(name);
698 return (number == null ? defaultValue : number.doubleValue());
699 }
700
701 /***
702 * Return a double for the given name. If the name does not
703 * exist, return 0.0.
704 *
705 * @param name A String with the name.
706 * @return A double.
707 */
708 public double getDouble(String name)
709 {
710 return getDouble(name, 0.0);
711 }
712
713 /***
714 * Return an array of doubles for the given name. If the name does
715 * not exist, return null.
716 *
717 * @param name A String with the name.
718 * @return A double[].
719 */
720 public double[] getDoubles(String name)
721 {
722 double[] result = null;
723 String value[] = getParam(name);
724 if (value != null)
725 {
726 result = new double[value.length];
727 for (int i = 0; i < value.length; i++)
728 {
729 Number number = parseNumber(value[i]);
730 result[i] = (number == null ? 0.0 : number.doubleValue());
731 }
732 }
733 return result;
734 }
735
736 /***
737 * Return a Double for the given name. If the name does not
738 * exist, return defaultValue.
739 *
740 * @param name A String with the name.
741 * @param defaultValue The default value.
742 * @return A double.
743 */
744 public Double getDoubleObject(String name, Double defaultValue)
745 {
746 Number result = getNumber(name);
747 return (result == null ? defaultValue : new Double(result.doubleValue()));
748 }
749
750 /***
751 * Return a Double for the given name. If the name does not
752 * exist, return null.
753 *
754 * @param name A String with the name.
755 * @return A double.
756 */
757 public Double getDoubleObject(String name)
758 {
759 return getDoubleObject(name, null);
760 }
761
762 /***
763 * Return an array of doubles for the given name. If the name does
764 * not exist, return null.
765 *
766 * @param name A String with the name.
767 * @return A double[].
768 */
769 public Double[] getDoubleObjects(String name)
770 {
771 Double[] result = null;
772 String value[] = getParam(name);
773 if (value != null)
774 {
775 result = new Double[value.length];
776 for (int i = 0; i < value.length; i++)
777 {
778 Number number = parseNumber(value[i]);
779 result[i] = (number == null ? null : new Double(number.doubleValue()));
780 }
781 }
782 return result;
783 }
784
785 /***
786 * Return a float for the given name. If the name does not
787 * exist, return defaultValue.
788 *
789 * @param name A String with the name.
790 * @param defaultValue The default value.
791 * @return A float.
792 */
793 public float getFloat(String name, float defaultValue)
794 {
795 Number number = getNumber(name);
796 return (number == null ? defaultValue : number.floatValue());
797 }
798
799 /***
800 * Return a float for the given name. If the name does not
801 * exist, return 0.0.
802 *
803 * @param name A String with the name.
804 * @return A float.
805 */
806 public float getFloat(String name)
807 {
808 return getFloat(name, 0.0f);
809 }
810
811 /***
812 * Return an array of floats for the given name. If the name does
813 * not exist, return null.
814 *
815 * @param name A String with the name.
816 * @return A float[].
817 */
818 public float[] getFloats(String name)
819 {
820 float[] result = null;
821 String value[] = getParam(name);
822 if (value != null)
823 {
824 result = new float[value.length];
825 for (int i = 0; i < value.length; i++)
826 {
827 Number number = parseNumber(value[i]);
828 result[i] = (number == null ? 0.0f : number.floatValue());
829 }
830 }
831 return result;
832 }
833
834 /***
835 * Return a Float for the given name. If the name does not
836 * exist, return defaultValue.
837 *
838 * @param name A String with the name.
839 * @param defaultValue The default value.
840 * @return A Float.
841 */
842 public Float getFloatObject(String name, Float defaultValue)
843 {
844 Number result = getNumber(name);
845 return (result == null ? defaultValue : new Float(result.floatValue()));
846 }
847
848 /***
849 * Return a float for the given name. If the name does not
850 * exist, return null.
851 *
852 * @param name A String with the name.
853 * @return A Float.
854 */
855 public Float getFloatObject(String name)
856 {
857 return getFloatObject(name, null);
858 }
859
860 /***
861 * Return an array of floats for the given name. If the name does
862 * not exist, return null.
863 *
864 * @param name A String with the name.
865 * @return A float[].
866 */
867 public Float[] getFloatObjects(String name)
868 {
869 Float[] result = null;
870 String value[] = getParam(name);
871 if (value != null)
872 {
873 result = new Float[value.length];
874 for (int i = 0; i < value.length; i++)
875 {
876 Number number = parseNumber(value[i]);
877 result[i] = (number == null ? null : new Float(number.floatValue()));
878 }
879 }
880 return result;
881 }
882
883 /***
884 * Return a BigDecimal for the given name. If the name does not
885 * exist, return defaultValue.
886 *
887 * @param name A String with the name.
888 * @param defaultValue The default value.
889 * @return A BigDecimal.
890 */
891 public BigDecimal getBigDecimal(String name, BigDecimal defaultValue)
892 {
893 Number result = getNumber(name);
894 return (result == null ? defaultValue : new BigDecimal(result.doubleValue()));
895 }
896
897 /***
898 * Return a BigDecimal for the given name. If the name does not
899 * exist, return null.
900 *
901 * @param name A String with the name.
902 * @return A BigDecimal.
903 */
904 public BigDecimal getBigDecimal(String name)
905 {
906 return getBigDecimal(name, null);
907 }
908
909 /***
910 * Return an array of BigDecimals for the given name. If the name
911 * does not exist, return null.
912 *
913 * @param name A String with the name.
914 * @return A BigDecimal[].
915 */
916 public BigDecimal[] getBigDecimals(String name)
917 {
918 BigDecimal[] result = null;
919 String value[] = getParam(name);
920 if (value != null)
921 {
922 result = new BigDecimal[value.length];
923 for (int i = 0; i < value.length; i++)
924 {
925 Number number = parseNumber(value[i]);
926 result[i] = (number == null ? null : new BigDecimal(number.doubleValue()));
927 }
928 }
929 return result;
930 }
931
932 /***
933 * Return an int for the given name. If the name does not exist,
934 * return defaultValue.
935 *
936 * @param name A String with the name.
937 * @param defaultValue The default value.
938 * @return An int.
939 */
940 public int getInt(String name, int defaultValue)
941 {
942 Number result = getNumber(name);
943 return ((result == null || result instanceof Double) ? defaultValue : result.intValue());
944 }
945
946 /***
947 * Return an int for the given name. If the name does not exist,
948 * return 0.
949 *
950 * @param name A String with the name.
951 * @return An int.
952 */
953 public int getInt(String name)
954 {
955 return getInt(name, 0);
956 }
957
958 /***
959 * Return an Integer for the given name. If the name does not
960 * exist, return defaultValue.
961 *
962 * @param name A String with the name.
963 * @param defaultValue The default value.
964 * @return An Integer.
965 * @deprecated use {@link #getIntObject} instead
966 */
967 public Integer getInteger(String name, int defaultValue)
968 {
969 return getIntObject(name, new Integer(defaultValue));
970 }
971
972 /***
973 * Return an Integer for the given name. If the name does not
974 * exist, return defaultValue. You cannot pass in a null here for
975 * the default value.
976 *
977 * @param name A String with the name.
978 * @param def The default value.
979 * @return An Integer.
980 * @deprecated use {@link #getIntObject} instead
981 */
982 public Integer getInteger(String name, Integer def)
983 {
984 return getIntObject(name, def);
985 }
986
987 /***
988 * Return an Integer for the given name. If the name does not
989 * exist, return 0.
990 *
991 * @param name A String with the name.
992 * @return An Integer.
993 * @deprecated use {@link #getIntObject} instead
994 */
995 public Integer getInteger(String name)
996 {
997 return getIntObject(name, new Integer(0));
998 }
999
1000 /***
1001 * Return an array of ints for the given name. If the name does
1002 * not exist, return null.
1003 *
1004 * @param name A String with the name.
1005 * @return An int[].
1006 */
1007 public int[] getInts(String name)
1008 {
1009 int[] result = null;
1010 String value[] = getParam(name);
1011 if (value != null)
1012 {
1013 result = new int[value.length];
1014 for (int i = 0; i < value.length; i++)
1015 {
1016 Number number = parseNumber(value[i]);
1017 result[i] = ((number == null || number instanceof Double) ? 0 : number.intValue());
1018 }
1019 }
1020 return result;
1021 }
1022
1023 /***
1024 * Return an Integer for the given name. If the name does not exist,
1025 * return defaultValue.
1026 *
1027 * @param name A String with the name.
1028 * @param defaultValue The default value.
1029 * @return An Integer.
1030 */
1031 public Integer getIntObject(String name, Integer defaultValue)
1032 {
1033 Number result = getNumber(name);
1034 return ((result == null || result instanceof Double) ? defaultValue : new Integer(result.intValue()));
1035 }
1036
1037 /***
1038 * Return an Integer for the given name. If the name does not exist,
1039 * return null.
1040 *
1041 * @param name A String with the name.
1042 * @return An Integer.
1043 */
1044 public Integer getIntObject(String name)
1045 {
1046 return getIntObject(name, null);
1047 }
1048
1049 /***
1050 * Return an array of Integers for the given name. If the name
1051 * does not exist, return null.
1052 *
1053 * @param name A String with the name.
1054 * @return An Integer[].
1055 */
1056 public Integer[] getIntObjects(String name)
1057 {
1058 Integer[] result = null;
1059 String value[] = getParam(name);
1060 if (value != null)
1061 {
1062 result = new Integer[value.length];
1063 for (int i = 0; i < value.length; i++)
1064 {
1065 Number number = parseNumber(value[i]);
1066 result[i] = ((number == null || number instanceof Double) ? null : new Integer(number.intValue()));
1067 }
1068 }
1069 return result;
1070 }
1071
1072 /***
1073 * Return an array of Integers for the given name. If the name
1074 * does not exist, return null.
1075 *
1076 * @param name A String with the name.
1077 * @return An Integer[].
1078 * @deprecated use {@link #getIntObjects} instead
1079 */
1080 public Integer[] getIntegers(String name)
1081 {
1082 return getIntObjects(name);
1083 }
1084
1085 /***
1086 * Return a long for the given name. If the name does not exist,
1087 * return defaultValue.
1088 *
1089 * @param name A String with the name.
1090 * @param defaultValue The default value.
1091 * @return A long.
1092 */
1093 public long getLong(String name, long defaultValue)
1094 {
1095 Number result = getNumber(name);
1096 return ((result == null || result instanceof Double) ? defaultValue : result.longValue());
1097 }
1098
1099 /***
1100 * Return a long for the given name. If the name does not exist,
1101 * return 0.
1102 *
1103 * @param name A String with the name.
1104 * @return A long.
1105 */
1106 public long getLong(String name)
1107 {
1108 return getLong(name, 0);
1109 }
1110
1111 /***
1112 * Return an array of longs for the given name. If the name does
1113 * not exist, return null.
1114 *
1115 * @param name A String with the name.
1116 * @return A long[].
1117 */
1118 public long[] getLongs(String name)
1119 {
1120 long[] result = null;
1121 String value[] = getParam(name);
1122 if (value != null)
1123 {
1124 result = new long[value.length];
1125 for (int i = 0; i < value.length; i++)
1126 {
1127 Number number = parseNumber(value[i]);
1128 result[i] = ((number == null || number instanceof Double) ? 0L : number.longValue());
1129 }
1130 }
1131 return result;
1132 }
1133
1134 /***
1135 * Return an array of Longs for the given name. If the name does
1136 * not exist, return null.
1137 *
1138 * @param name A String with the name.
1139 * @return A Long[].
1140 */
1141 public Long[] getLongObjects(String name)
1142 {
1143 Long[] result = null;
1144 String value[] = getParam(name);
1145 if (value != null)
1146 {
1147 result = new Long[value.length];
1148 for (int i = 0; i < value.length; i++)
1149 {
1150 Number number = parseNumber(value[i]);
1151 result[i] = ((number == null || number instanceof Double) ? null : new Long(number.longValue()));
1152 }
1153 }
1154 return result;
1155 }
1156
1157 /***
1158 * Return a Long for the given name. If the name does
1159 * not exist, return null.
1160 *
1161 * @param name A String with the name.
1162 * @return A Long.
1163 */
1164 public Long getLongObject(String name)
1165 {
1166 return getLongObject(name, null);
1167 }
1168
1169 /***
1170 * Return a Long for the given name. If the name does
1171 * not exist, return the default value.
1172 *
1173 * @param name A String with the name.
1174 * @param defaultValue The default value.
1175 * @return A Long.
1176 */
1177 public Long getLongObject(String name, Long defaultValue)
1178 {
1179 Number result = getNumber(name);
1180 return ((result == null || result instanceof Double) ? defaultValue : new Long(result.longValue()));
1181 }
1182
1183 /***
1184 * Return a byte for the given name. If the name does not exist,
1185 * return defaultValue.
1186 *
1187 * @param name A String with the name.
1188 * @param defaultValue The default value.
1189 * @return A byte.
1190 */
1191 public byte getByte(String name, byte defaultValue)
1192 {
1193 Number result = getNumber(name);
1194 return ((result == null || result instanceof Double) ? defaultValue : result.byteValue());
1195 }
1196
1197 /***
1198 * Return a byte for the given name. If the name does not exist,
1199 * return 0.
1200 *
1201 * @param name A String with the name.
1202 * @return A byte.
1203 */
1204 public byte getByte(String name)
1205 {
1206 return getByte(name, (byte) 0);
1207 }
1208
1209 /***
1210 * Return an array of bytes for the given name. If the name does
1211 * not exist, return null. The array is returned according to the
1212 * HttpRequest's character encoding.
1213 *
1214 * @param name A String with the name.
1215 * @return A byte[].
1216 * @exception UnsupportedEncodingException
1217 */
1218 public byte[] getBytes(String name)
1219 throws UnsupportedEncodingException
1220 {
1221 byte result[] = null;
1222 String value = getString(name);
1223 if (value != null)
1224 {
1225 result = value.getBytes(getCharacterEncoding());
1226 }
1227 return result;
1228 }
1229
1230 /***
1231 * Return a byte for the given name. If the name does not exist,
1232 * return defaultValue.
1233 *
1234 * @param name A String with the name.
1235 * @param defaultValue The default value.
1236 * @return A byte.
1237 */
1238 public Byte getByteObject(String name, Byte defaultValue)
1239 {
1240 Number result = getNumber(name);
1241 return ((result == null || result instanceof Double) ? defaultValue : new Byte(result.byteValue()));
1242 }
1243
1244 /***
1245 * Return a byte for the given name. If the name does not exist,
1246 * return 0.
1247 *
1248 * @param name A String with the name.
1249 * @return A byte.
1250 */
1251 public Byte getByteObject(String name)
1252 {
1253 return getByteObject(name, null);
1254 }
1255
1256 /***
1257 * Return a String for the given name. If the name does not
1258 * exist, return null.
1259 *
1260 * @param name A String with the name.
1261 * @return A String or null if the key is unknown.
1262 */
1263 public String getString(String name)
1264 {
1265 String [] value = getParam(name);
1266
1267 return (value == null
1268 || value.length == 0)
1269 ? null : value[0];
1270 }
1271
1272 /***
1273 * Return a String for the given name. If the name does not
1274 * exist, return null. It is the same as the getString() method
1275 * however has been added for simplicity when working with
1276 * template tools such as Velocity which allow you to do
1277 * something like this:
1278 *
1279 * <code>$data.Parameters.form_variable_name</code>
1280 *
1281 * @param name A String with the name.
1282 * @return A String.
1283 */
1284 public String get(String name)
1285 {
1286 return getString(name);
1287 }
1288
1289 /***
1290 * Return a String for the given name. If the name does not
1291 * exist, return the defaultValue.
1292 *
1293 * @param name A String with the name.
1294 * @param defaultValue The default value.
1295 * @return A String.
1296 */
1297 public String getString(String name, String defaultValue)
1298 {
1299 String value = getString(name);
1300
1301 return (StringUtils.isEmpty(value) ? defaultValue : value );
1302 }
1303
1304 /***
1305 * Set a parameter to a specific value.
1306 *
1307 * This is useful if you want your action to override the values
1308 * of the parameters for the screen to use.
1309 * @param name The name of the parameter.
1310 * @param value The value to set.
1311 */
1312 public void setString(String name, String value)
1313 {
1314 if (value != null)
1315 {
1316 putParam(name, new String[]{value});
1317 }
1318 }
1319
1320 /***
1321 * Return an array of Strings for the given name. If the name
1322 * does not exist, return null.
1323 *
1324 * @param name A String with the name.
1325 * @return A String[].
1326 */
1327 public String[] getStrings(String name)
1328 {
1329 return getParam(name);
1330 }
1331
1332 /***
1333 * Return an array of Strings for the given name. If the name
1334 * does not exist, return the defaultValue.
1335 *
1336 * @param name A String with the name.
1337 * @param defaultValue The default value.
1338 * @return A String[].
1339 */
1340 public String[] getStrings(String name, String[] defaultValue)
1341 {
1342 String[] value = getParam(name);
1343
1344 return (value == null || value.length == 0)
1345 ? defaultValue : value;
1346 }
1347
1348 /***
1349 * Set a parameter to a specific value.
1350 *
1351 * This is useful if you want your action to override the values
1352 * of the parameters for the screen to use.
1353 * @param name The name of the parameter.
1354 * @param values The value to set.
1355 */
1356 public void setStrings(String name, String[] values)
1357 {
1358 if (values != null)
1359 {
1360 putParam(name, values);
1361 }
1362 }
1363
1364 /***
1365 * Return an Object for the given name. If the name does not
1366 * exist, return null.
1367 *
1368 * @param name A String with the name.
1369 * @return An Object.
1370 */
1371 public Object getObject(String name)
1372 {
1373 return getString(name);
1374 }
1375
1376 /***
1377 * Return an array of Objects for the given name. If the name
1378 * does not exist, return null.
1379 *
1380 * @param name A String with the name.
1381 * @return An Object[].
1382 */
1383 public Object[] getObjects(String name)
1384 {
1385 return getParam(name);
1386 }
1387
1388 /***
1389 * Returns a {@link java.util.Date} object. String is parsed by supplied
1390 * DateFormat. If the name does not exist or the value could not be
1391 * parsed into a date return the defaultValue.
1392 *
1393 * @param name A String with the name.
1394 * @param df A DateFormat.
1395 * @param defaultValue The default value.
1396 * @return A Date.
1397 */
1398 public Date getDate(String name, DateFormat df, Date defaultValue)
1399 {
1400 Date result = defaultValue;
1401 String value = StringUtils.trim(getString(name));
1402
1403 if (StringUtils.isNotEmpty(value))
1404 {
1405 try
1406 {
1407
1408 df.setLenient(false);
1409 result = df.parse(value);
1410 }
1411 catch (ParseException e)
1412 {
1413 logConvertionFailure(name, value, "Date");
1414 }
1415 }
1416
1417 return result;
1418 }
1419
1420 /***
1421 * Returns a {@link java.util.Date} object. If there are DateSelector or
1422 * TimeSelector style parameters then these are used. If not and there
1423 * is a parameter 'name' then this is parsed by DateFormat. If the
1424 * name does not exist, return null.
1425 *
1426 * @param name A String with the name.
1427 * @return A Date.
1428 */
1429 public Date getDate(String name)
1430 {
1431 Date date = null;
1432
1433 if (containsDateSelectorKeys(name))
1434 {
1435 try
1436 {
1437 Calendar cal = new GregorianCalendar(
1438 getInt(name + DateSelector.YEAR_SUFFIX),
1439 getInt(name + DateSelector.MONTH_SUFFIX),
1440 getInt(name + DateSelector.DAY_SUFFIX));
1441
1442
1443 cal.setLenient(false);
1444 date = cal.getTime();
1445 }
1446 catch (IllegalArgumentException e)
1447 {
1448 logConvertionFailure(name, "n/a", "Date");
1449 }
1450 }
1451 else if (containsTimeSelectorKeys(name))
1452 {
1453 try
1454 {
1455 String ampm = getString(name + TimeSelector.AMPM_SUFFIX);
1456 int hour = getInt(name + TimeSelector.HOUR_SUFFIX);
1457
1458
1459 if (ampm != null)
1460 {
1461 if (hour == 12)
1462 {
1463 hour = (Integer.parseInt(ampm) == Calendar.PM) ? 12 : 0;
1464 }
1465 else if (Integer.parseInt(ampm) == Calendar.PM)
1466 {
1467 hour += 12;
1468 }
1469 }
1470 Calendar cal = new GregorianCalendar(1, 1, 1,
1471 hour,
1472 getInt(name + TimeSelector.MINUTE_SUFFIX),
1473 getInt(name + TimeSelector.SECOND_SUFFIX));
1474
1475
1476 cal.setLenient(false);
1477 date = cal.getTime();
1478 }
1479 catch (IllegalArgumentException e)
1480 {
1481 logConvertionFailure(name, "n/a", "Date");
1482 }
1483 }
1484 else
1485 {
1486 date = getDate(name, dateFormat, null);
1487 }
1488
1489 return date;
1490 }
1491
1492 /***
1493 * Returns a {@link java.util.Date} object. String is parsed by supplied
1494 * DateFormat. If the name does not exist, return null.
1495 *
1496 * @param name A String with the name.
1497 * @param df A DateFormat.
1498 * @return A Date.
1499 */
1500 public Date getDate(String name, DateFormat df)
1501 {
1502 return getDate(name, df, null);
1503 }
1504
1505 /***
1506 * Return an NumberKey for the given name. If the name does not
1507 * exist, return null.
1508 *
1509 * @param name A String with the name.
1510 * @return A NumberKey, or <code>null</code> if unparsable.
1511 * @deprecated no replacement
1512 */
1513 public NumberKey getNumberKey(String name)
1514 {
1515 NumberKey result = null;
1516 try
1517 {
1518 String value = StringUtils.trim(getString(name));
1519 if (StringUtils.isNotEmpty(value))
1520 {
1521 result = new NumberKey(value);
1522 }
1523 }
1524 catch (ClassCastException e)
1525 {
1526 log.error("Parameter ("
1527 + name + ") could not be converted to a NumberKey", e);
1528 }
1529 return result;
1530 }
1531
1532 /***
1533 * Return an StringKey for the given name. If the name does not
1534 * exist, return null.
1535 *
1536 * @param name A String with the name.
1537 * @return A StringKey, or <code>null</code> if unparsable.
1538 * @deprecated no replacement
1539 */
1540 public StringKey getStringKey(String name)
1541 {
1542 StringKey result = null;
1543 try
1544 {
1545 String value = StringUtils.trim(getString(name));
1546 if (StringUtils.isNotEmpty(value))
1547 {
1548 result = new StringKey(value);
1549 }
1550 }
1551 catch (ClassCastException e)
1552 {
1553 log.error("Parameter ("
1554 + name + ") could not be converted to a StringKey", e);
1555 }
1556 return result;
1557 }
1558
1559 /***
1560 * Uses bean introspection to set writable properties of bean from
1561 * the parameters, where a (case-insensitive) name match between
1562 * the bean property and the parameter is looked for.
1563 *
1564 * @param bean An Object.
1565 * @exception Exception a generic exception.
1566 */
1567 public void setProperties(Object bean) throws Exception
1568 {
1569 Class beanClass = bean.getClass();
1570 PropertyDescriptor[] props
1571 = Introspector.getBeanInfo(beanClass).getPropertyDescriptors();
1572
1573 for (int i = 0; i < props.length; i++)
1574 {
1575 String propname = props[i].getName();
1576 Method setter = props[i].getWriteMethod();
1577 if (setter != null &&
1578 (containsKey(propname) ||
1579 containsDateSelectorKeys(propname) ||
1580 containsTimeSelectorKeys(propname)))
1581 {
1582 setProperty(bean, props[i]);
1583 }
1584 }
1585 }
1586
1587 /***
1588 * Simple method that attempts to get a textual representation of
1589 * this object's name/value pairs. String[] handling is currently
1590 * a bit rough.
1591 *
1592 * @return A textual representation of the parsed name/value pairs.
1593 */
1594 public String toString()
1595 {
1596 StringBuffer sb = new StringBuffer();
1597 for (Iterator iter = keySet().iterator(); iter.hasNext();)
1598 {
1599 String name = (String) iter.next();
1600
1601 sb.append('{');
1602 sb.append(name);
1603 sb.append('=');
1604 Object [] params = getToStringParam(name);
1605
1606 if (params == null)
1607 {
1608 sb.append("unknown?");
1609 }
1610 else if (params.length == 0)
1611 {
1612 sb.append("empty");
1613 }
1614 else
1615 {
1616 sb.append('[');
1617 for (Iterator it = new ArrayIterator(params); it.hasNext(); )
1618 {
1619 sb.append(it.next());
1620 if (it.hasNext())
1621 {
1622 sb.append(", ");
1623 }
1624 }
1625 sb.append(']');
1626 }
1627 sb.append("}\n");
1628 }
1629
1630 return sb.toString();
1631 }
1632
1633 /***
1634 * This method is only used in toString() and can be used by
1635 * derived classes to add their local parameters to the toString()
1636
1637 * @param name A string with the name
1638 *
1639 * @return the value object array or null if not set
1640 */
1641 protected Object [] getToStringParam(final String name)
1642 {
1643 return getParam(name);
1644 }
1645
1646 /***
1647 * Set the property 'prop' in the bean to the value of the
1648 * corresponding parameters. Supports all types supported by
1649 * getXXX methods plus a few more that come for free because
1650 * primitives have to be wrapped before being passed to invoke
1651 * anyway.
1652 *
1653 * @param bean An Object.
1654 * @param prop A PropertyDescriptor.
1655 * @exception Exception a generic exception.
1656 */
1657 protected void setProperty(Object bean,
1658 PropertyDescriptor prop)
1659 throws Exception
1660 {
1661 if (prop instanceof IndexedPropertyDescriptor)
1662 {
1663 throw new Exception(prop.getName() +
1664 " is an indexed property (not supported)");
1665 }
1666
1667 Method setter = prop.getWriteMethod();
1668 if (setter == null)
1669 {
1670 throw new Exception(prop.getName() +
1671 " is a read only property");
1672 }
1673
1674 Class propclass = prop.getPropertyType();
1675 Object[] args = {null};
1676
1677 if (propclass == String.class)
1678 {
1679 args[0] = getString(prop.getName());
1680 }
1681 else if (propclass == Byte.class || propclass == Byte.TYPE)
1682 {
1683 args[0] = getByteObject(prop.getName());
1684 }
1685 else if (propclass == Integer.class || propclass == Integer.TYPE)
1686 {
1687 args[0] = getIntObject(prop.getName());
1688 }
1689 else if (propclass == Long.class || propclass == Long.TYPE)
1690 {
1691 args[0] = getLongObject(prop.getName());
1692 }
1693 else if (propclass == Boolean.class || propclass == Boolean.TYPE)
1694 {
1695 args[0] = getBooleanObject(prop.getName());
1696 }
1697 else if (propclass == Double.class || propclass == Double.TYPE)
1698 {
1699 args[0] = getDoubleObject(prop.getName());
1700 }
1701 else if (propclass == Float.class || propclass == Float.TYPE)
1702 {
1703 args[0] = getFloatObject(prop.getName());
1704 }
1705 else if (propclass == BigDecimal.class)
1706 {
1707 args[0] = getBigDecimal(prop.getName());
1708 }
1709 else if (propclass == String[].class)
1710 {
1711 args[0] = getStrings(prop.getName());
1712 }
1713 else if (propclass == Object.class)
1714 {
1715 args[0] = getObject(prop.getName());
1716 }
1717 else if (propclass == int[].class)
1718 {
1719 args[0] = getInts(prop.getName());
1720 }
1721 else if (propclass == Integer[].class)
1722 {
1723 args[0] = getIntObjects(prop.getName());
1724 }
1725 else if (propclass == Date.class)
1726 {
1727 args[0] = getDate(prop.getName());
1728 }
1729 else if (propclass == NumberKey.class)
1730 {
1731 args[0] = getNumberKey(prop.getName());
1732 }
1733 else if (propclass == StringKey.class)
1734 {
1735 args[0] = getStringKey(prop.getName());
1736 }
1737 else
1738 {
1739 throw new Exception("property "
1740 + prop.getName()
1741 + " is of unsupported type "
1742 + propclass.toString());
1743 }
1744
1745 setter.invoke(bean, args);
1746 }
1747
1748 /***
1749 * Writes a log message about a convertion failure.
1750 *
1751 * @param paramName name of the parameter which could not be converted
1752 * @param value value of the parameter
1753 * @param type target data type.
1754 */
1755 private void logConvertionFailure(String paramName,
1756 String value, String type)
1757 {
1758 if (log.isWarnEnabled())
1759 {
1760 log.warn("Parameter (" + paramName
1761 + ") with value of ("
1762 + value + ") could not be converted to a " + type);
1763 }
1764 }
1765
1766 /***
1767 * Puts a key into the parameters map. Makes sure that the name is always
1768 * mapped correctly. This method also enforces the usage of arrays for the
1769 * parameters.
1770 *
1771 * @param name A String with the name.
1772 * @param value An array of Objects with the values.
1773 *
1774 */
1775 protected void putParam(final String name, final String [] value)
1776 {
1777 String key = convert(name);
1778 if (key != null)
1779 {
1780 parameters.put(key, value);
1781 }
1782 }
1783
1784 /***
1785 * fetches a key from the parameters map. Makes sure that the name is
1786 * always mapped correctly.
1787 *
1788 * @param name A string with the name
1789 *
1790 * @return the value object array or null if not set
1791 */
1792 protected String [] getParam(final String name)
1793 {
1794 String key = convert(name);
1795
1796 return (key != null) ? (String []) parameters.get(key) : null;
1797 }
1798 }