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