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