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ü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 }