View Javadoc

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