View Javadoc

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