View Javadoc

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