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 }