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