Coverage Report - org.apache.tapestry.valid.NumberValidator
 
Classes in this File Line Coverage Branch Coverage Complexity
NumberValidator
0%
0/115
0%
0/44
1.8
NumberValidator$1
N/A
N/A
1.8
NumberValidator$BigDecimalAdaptor
0%
0/3
N/A
1.8
NumberValidator$BigIntegerAdaptor
0%
0/3
N/A
1.8
NumberValidator$ByteAdaptor
0%
0/3
N/A
1.8
NumberValidator$DoubleAdaptor
0%
0/3
N/A
1.8
NumberValidator$FloatAdaptor
0%
0/3
N/A
1.8
NumberValidator$IntAdaptor
0%
0/3
N/A
1.8
NumberValidator$IntegerNumberAdaptor
0%
0/2
N/A
1.8
NumberValidator$LongAdaptor
0%
0/3
N/A
1.8
NumberValidator$NumberStrategy
0%
0/6
0%
0/2
1.8
NumberValidator$RealNumberAdaptor
0%
0/2
N/A
1.8
NumberValidator$ShortAdaptor
0%
0/3
N/A
1.8
 
 1  
 // Copyright 2004, 2005 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 7  
 //     http://www.apache.org/licenses/LICENSE-2.0
 8  
 //
 9  
 // Unless required by applicable law or agreed to in writing, software
 10  
 // distributed under the License is distributed on an "AS IS" BASIS,
 11  
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 // See the License for the specific language governing permissions and
 13  
 // limitations under the License.
 14  
 
 15  
 package org.apache.tapestry.valid;
 16  
 
 17  
 import org.apache.hivemind.ApplicationRuntimeException;
 18  
 import org.apache.hivemind.lib.util.StrategyRegistry;
 19  
 import org.apache.hivemind.lib.util.StrategyRegistryImpl;
 20  
 import org.apache.hivemind.util.PropertyUtils;
 21  
 import org.apache.tapestry.IMarkupWriter;
 22  
 import org.apache.tapestry.IRequestCycle;
 23  
 import org.apache.tapestry.Tapestry;
 24  
 import org.apache.tapestry.form.IFormComponent;
 25  
 
 26  
 import java.math.BigDecimal;
 27  
 import java.math.BigInteger;
 28  
 import java.util.HashMap;
 29  
 import java.util.Map;
 30  
 
 31  
 /**
 32  
  * Simple validation for standard number classes. This is probably insufficient for anything tricky
 33  
  * and application specific, such as parsing currency.
 34  
  * 
 35  
  * @author Howard Lewis Ship
 36  
  * @since 1.0.8
 37  
  */
 38  
 
 39  
 public class NumberValidator extends AbstractNumericValidator
 40  
 {
 41  
     public static final int NUMBER_TYPE_INTEGER = 0;
 42  
 
 43  
     public static final int NUMBER_TYPE_REAL = 1;
 44  
     
 45  0
     private static final Map TYPES = new HashMap();
 46  
     
 47  0
     private static StrategyRegistry _numberAdaptors = new StrategyRegistryImpl();
 48  
     
 49  
     static
 50  
     {
 51  0
         TYPES.put("boolean", boolean.class);
 52  0
         TYPES.put("Boolean", Boolean.class);
 53  0
         TYPES.put("java.lang.Boolean", Boolean.class);
 54  0
         TYPES.put("char", char.class);
 55  0
         TYPES.put("Character", Character.class);
 56  0
         TYPES.put("java.lang.Character", Character.class);
 57  0
         TYPES.put("short", short.class);
 58  0
         TYPES.put("Short", Short.class);
 59  0
         TYPES.put("java.lang.Short", Short.class);
 60  0
         TYPES.put("int", int.class);
 61  0
         TYPES.put("Integer", Integer.class);
 62  0
         TYPES.put("java.lang.Integer", Integer.class);
 63  0
         TYPES.put("long", long.class);
 64  0
         TYPES.put("Long", Long.class);
 65  0
         TYPES.put("java.lang.Long", Long.class);
 66  0
         TYPES.put("float", float.class);
 67  0
         TYPES.put("Float", Float.class);
 68  0
         TYPES.put("java.lang.Float", Float.class);
 69  0
         TYPES.put("byte", byte.class);
 70  0
         TYPES.put("Byte", Byte.class);
 71  0
         TYPES.put("java.lang.Byte", Byte.class);
 72  0
         TYPES.put("double", double.class);
 73  0
         TYPES.put("Double", Double.class);
 74  0
         TYPES.put("java.lang.Double", Double.class);
 75  0
         TYPES.put("java.math.BigInteger", BigInteger.class);
 76  0
         TYPES.put("java.math.BigDecimal", BigDecimal.class);
 77  
     }
 78  
 
 79  0
     private Class _valueTypeClass = int.class;
 80  
 
 81  
     private Number _minimum;
 82  
 
 83  
     private Number _maximum;
 84  
 
 85  
     /**
 86  
      * This class is not meant for use outside of NumberValidator; it is public only to fascilitate
 87  
      * some unit testing.
 88  
      */
 89  0
     public abstract static class NumberStrategy
 90  
     {
 91  
         /**
 92  
          * Parses a non-empty {@link String}into the correct subclass of {@link Number}.
 93  
          * 
 94  
          * @throws NumberFormatException
 95  
          *             if the String can not be parsed.
 96  
          */
 97  
 
 98  
         public abstract Number parse(String value);
 99  
 
 100  
         /**
 101  
          * Indicates the type of the number represented -- integer or real. The information is used
 102  
          * to build the client-side validator. This method could return a boolean, but returns an
 103  
          * int to allow future extensions of the validator.
 104  
          * 
 105  
          * @return one of the predefined number types
 106  
          */
 107  
         public abstract int getNumberType();
 108  
 
 109  
         public int compare(Number left, Number right)
 110  
         {
 111  0
             Number comparisonRight = right;
 112  0
             if (!left.getClass().equals(comparisonRight.getClass()))
 113  0
                 comparisonRight = coerce(comparisonRight);
 114  
 
 115  0
             Comparable lc = (Comparable) left;
 116  
 
 117  0
             return lc.compareTo(comparisonRight);
 118  
         }
 119  
 
 120  
         /**
 121  
          * Invoked when comparing two Numbers of different types. The number is cooerced from its
 122  
          * ordinary type to the correct type for comparison.
 123  
          * 
 124  
          * @since 3.0
 125  
          */
 126  
         protected abstract Number coerce(Number number);
 127  
     }
 128  
 
 129  
     /**
 130  
      * Integer adaptor.
 131  
      */
 132  0
     private abstract static class IntegerNumberAdaptor extends NumberStrategy
 133  
     {
 134  
         public int getNumberType()
 135  
         {
 136  0
             return NUMBER_TYPE_INTEGER;
 137  
         }
 138  
     }
 139  
 
 140  
     /**
 141  
      * Integer adaptor.
 142  
      */
 143  0
     private abstract static class RealNumberAdaptor extends NumberStrategy
 144  
     {
 145  
         public int getNumberType()
 146  
         {
 147  0
             return NUMBER_TYPE_REAL;
 148  
         }
 149  
     }
 150  
 
 151  
     /**
 152  
      * Integer adaptor.
 153  
      */
 154  0
     private static class ByteAdaptor extends IntegerNumberAdaptor
 155  
     {
 156  
         public Number parse(String value)
 157  
         {
 158  0
             return new Byte(value);
 159  
         }
 160  
 
 161  
         protected Number coerce(Number number)
 162  
         {
 163  0
             return new Byte(number.byteValue());
 164  
         }
 165  
     }
 166  
 
 167  
     /**
 168  
      * Integer adaptor.
 169  
      */
 170  0
     private static class ShortAdaptor extends IntegerNumberAdaptor
 171  
     {
 172  
         public Number parse(String value)
 173  
         {
 174  0
             return new Short(value);
 175  
         }
 176  
 
 177  
         protected Number coerce(Number number)
 178  
         {
 179  0
             return new Short(number.shortValue());
 180  
         }
 181  
     }
 182  
 
 183  
     /**
 184  
      * Integer adaptor.
 185  
      */
 186  0
     private static class IntAdaptor extends IntegerNumberAdaptor
 187  
     {
 188  
         public Number parse(String value)
 189  
         {
 190  0
             return new Integer(value);
 191  
         }
 192  
 
 193  
         protected Number coerce(Number number)
 194  
         {
 195  0
             return new Integer(number.intValue());
 196  
         }
 197  
     }
 198  
 
 199  
     /**
 200  
      * Integer adaptor.
 201  
      */
 202  0
     private static class LongAdaptor extends IntegerNumberAdaptor
 203  
     {
 204  
         public Number parse(String value)
 205  
         {
 206  0
             return new Long(value);
 207  
         }
 208  
 
 209  
         protected Number coerce(Number number)
 210  
         {
 211  0
             return new Long(number.longValue());
 212  
         }
 213  
     }
 214  
 
 215  
     /**
 216  
      * Integer adaptor.
 217  
      */
 218  0
     private static class FloatAdaptor extends RealNumberAdaptor
 219  
     {
 220  
         public Number parse(String value)
 221  
         {
 222  0
             return new Float(value);
 223  
         }
 224  
 
 225  
         protected Number coerce(Number number)
 226  
         {
 227  0
             return new Float(number.floatValue());
 228  
         }
 229  
     }
 230  
 
 231  
     /**
 232  
      * Integer adaptor.
 233  
      */
 234  0
     private static class DoubleAdaptor extends RealNumberAdaptor
 235  
     {
 236  
         public Number parse(String value)
 237  
         {
 238  0
             return new Double(value);
 239  
         }
 240  
 
 241  
         protected Number coerce(Number number)
 242  
         {
 243  0
             return new Double(number.doubleValue());
 244  
         }
 245  
     }
 246  
 
 247  
     /**
 248  
      * Integer adaptor.
 249  
      */
 250  0
     private static class BigDecimalAdaptor extends RealNumberAdaptor
 251  
     {
 252  
         public Number parse(String value)
 253  
         {
 254  0
             return new BigDecimal(value);
 255  
         }
 256  
 
 257  
         protected Number coerce(Number number)
 258  
         {
 259  0
             return new BigDecimal(number.doubleValue());
 260  
         }
 261  
     }
 262  
 
 263  
     /**
 264  
      * Integer adaptor.
 265  
      */
 266  0
     private static class BigIntegerAdaptor extends IntegerNumberAdaptor
 267  
     {
 268  
         public Number parse(String value)
 269  
         {
 270  0
             return new BigInteger(value);
 271  
         }
 272  
 
 273  
         protected Number coerce(Number number)
 274  
         {
 275  0
             return new BigInteger(number.toString());
 276  
         }
 277  
     }
 278  
 
 279  
     static
 280  
     {
 281  0
         NumberStrategy byteAdaptor = new ByteAdaptor();
 282  0
         NumberStrategy shortAdaptor = new ShortAdaptor();
 283  0
         NumberStrategy intAdaptor = new IntAdaptor();
 284  0
         NumberStrategy longAdaptor = new LongAdaptor();
 285  0
         NumberStrategy floatAdaptor = new FloatAdaptor();
 286  0
         NumberStrategy doubleAdaptor = new DoubleAdaptor();
 287  
 
 288  0
         _numberAdaptors.register(Byte.class, byteAdaptor);
 289  0
         _numberAdaptors.register(byte.class, byteAdaptor);
 290  0
         _numberAdaptors.register(Short.class, shortAdaptor);
 291  0
         _numberAdaptors.register(short.class, shortAdaptor);
 292  0
         _numberAdaptors.register(Integer.class, intAdaptor);
 293  0
         _numberAdaptors.register(int.class, intAdaptor);
 294  0
         _numberAdaptors.register(Long.class, longAdaptor);
 295  0
         _numberAdaptors.register(long.class, longAdaptor);
 296  0
         _numberAdaptors.register(Float.class, floatAdaptor);
 297  0
         _numberAdaptors.register(float.class, floatAdaptor);
 298  0
         _numberAdaptors.register(Double.class, doubleAdaptor);
 299  0
         _numberAdaptors.register(double.class, doubleAdaptor);
 300  
 
 301  0
         _numberAdaptors.register(BigDecimal.class, new BigDecimalAdaptor());
 302  0
         _numberAdaptors.register(BigInteger.class, new BigIntegerAdaptor());
 303  0
     }
 304  
 
 305  
     public NumberValidator()
 306  0
     {
 307  
 
 308  0
     }
 309  
 
 310  
     /**
 311  
      * Initializes the NumberValidator with properties defined by the initializer.
 312  
      * 
 313  
      * @since 4.0
 314  
      */
 315  
 
 316  
     public NumberValidator(String initializer)
 317  0
     {
 318  0
         PropertyUtils.configureProperties(this, initializer);
 319  0
     }
 320  
 
 321  
     public String toString(IFormComponent field, Object value)
 322  
     {
 323  0
         if (value == null)
 324  0
             return null;
 325  
 
 326  0
         if (getZeroIsNull())
 327  
         {
 328  0
             Number number = (Number) value;
 329  
 
 330  0
             if (number.doubleValue() == 0.0)
 331  0
                 return null;
 332  
         }
 333  
 
 334  0
         return value.toString();
 335  
     }
 336  
 
 337  
     private NumberStrategy getStrategy(IFormComponent field)
 338  
     {
 339  0
         NumberStrategy result = getStrategy(_valueTypeClass);
 340  
 
 341  0
         if (result == null)
 342  0
             throw new ApplicationRuntimeException(Tapestry.format("NumberValidator.no-adaptor-for-field",
 343  
                 field,
 344  
                 _valueTypeClass.getName()));
 345  
 
 346  0
         return result;
 347  
     }
 348  
 
 349  
     /**
 350  
      * Returns an strategy for the given type.
 351  
      * <p>
 352  
      * Note: this method exists only for testing purposes. It is not meant to be invoked by user
 353  
      * code and is subject to change at any time.
 354  
      * 
 355  
      * @param type
 356  
      *            the type (a Number subclass) for which to return an adaptor
 357  
      * @return the adaptor, or null if no such adaptor may be found
 358  
      * @since 3.0
 359  
      */
 360  
     public static NumberStrategy getStrategy(Class type)
 361  
     {
 362  0
         return (NumberStrategy) _numberAdaptors.getStrategy(type);
 363  
     }
 364  
 
 365  
     public Object toObject(IFormComponent field, String value) throws ValidatorException
 366  
     {
 367  0
         if (checkRequired(field, value))
 368  0
             return null;
 369  
 
 370  0
         NumberStrategy adaptor = getStrategy(field);
 371  0
         Number result = null;
 372  
 
 373  
         try
 374  
         {
 375  0
             result = adaptor.parse(value);
 376  
         }
 377  0
         catch (NumberFormatException ex)
 378  
         {
 379  0
             throw new ValidatorException(buildInvalidNumericFormatMessage(field),
 380  
                     ValidationConstraint.NUMBER_FORMAT);
 381  0
         }
 382  
 
 383  0
         if (_minimum != null && adaptor.compare(result, _minimum) < 0)
 384  0
             throw new ValidatorException(buildNumberTooSmallMessage(field, _minimum),
 385  
                     ValidationConstraint.TOO_SMALL);
 386  
 
 387  0
         if (_maximum != null && adaptor.compare(result, _maximum) > 0)
 388  0
             throw new ValidatorException(buildNumberTooLargeMessage(field, _maximum),
 389  
                     ValidationConstraint.TOO_LARGE);
 390  
 
 391  0
         return result;
 392  
     }
 393  
 
 394  
     public Number getMaximum()
 395  
     {
 396  0
         return _maximum;
 397  
     }
 398  
 
 399  
     public boolean getHasMaximum()
 400  
     {
 401  0
         return _maximum != null;
 402  
     }
 403  
 
 404  
     public void setMaximum(Number maximum)
 405  
     {
 406  0
         _maximum = maximum;
 407  0
     }
 408  
 
 409  
     public Number getMinimum()
 410  
     {
 411  0
         return _minimum;
 412  
     }
 413  
 
 414  
     public boolean getHasMinimum()
 415  
     {
 416  0
         return _minimum != null;
 417  
     }
 418  
 
 419  
     public void setMinimum(Number minimum)
 420  
     {
 421  0
         _minimum = minimum;
 422  0
     }
 423  
 
 424  
     /**
 425  
      * @since 2.2
 426  
      */
 427  
 
 428  
     public void renderValidatorContribution(IFormComponent field, IMarkupWriter writer,
 429  
             IRequestCycle cycle)
 430  
     {
 431  0
         if (!isClientScriptingEnabled())
 432  0
             return;
 433  
 
 434  0
         if (!(isRequired() || _minimum != null || _maximum != null))
 435  0
             return;
 436  
 
 437  0
         Map symbols = new HashMap();
 438  
 
 439  0
         if (isRequired())
 440  0
             symbols.put("requiredMessage", buildRequiredMessage(field));
 441  
 
 442  0
         if (isIntegerNumber())
 443  0
             symbols.put("formatMessage", buildInvalidIntegerFormatMessage(field));
 444  
         else
 445  0
             symbols.put("formatMessage", buildInvalidNumericFormatMessage(field));
 446  
 
 447  0
         if (_minimum != null || _maximum != null)
 448  0
             symbols.put("rangeMessage", buildRangeMessage(field, _minimum, _maximum));
 449  
 
 450  0
         processValidatorScript(getScriptPath(), cycle, field, symbols);
 451  0
     }
 452  
 
 453  
     /**
 454  
      * Sets the value type from a string type name. The name may be a scalar numeric type, a fully
 455  
      * qualified class name, or the name of a numeric wrapper type from java.lang (with the package
 456  
      * name omitted).
 457  
      * 
 458  
      * @since 3.0
 459  
      */
 460  
 
 461  
     public void setValueType(String typeName)
 462  
     {
 463  0
         Class typeClass = (Class) TYPES.get(typeName);
 464  
 
 465  0
         if (typeClass == null)
 466  0
             throw new ApplicationRuntimeException(Tapestry.format("NumberValidator.unknown-type", typeName));
 467  
 
 468  0
         _valueTypeClass = typeClass;
 469  0
     }
 470  
 
 471  
     /** @since 3.0 * */
 472  
 
 473  
     public void setValueTypeClass(Class valueTypeClass)
 474  
     {
 475  0
         _valueTypeClass = valueTypeClass;
 476  0
     }
 477  
 
 478  
     /**
 479  
      * Returns the value type to convert strings back into. The default is int.
 480  
      * 
 481  
      * @since 3.0
 482  
      */
 483  
 
 484  
     public Class getValueTypeClass()
 485  
     {
 486  0
         return _valueTypeClass;
 487  
     }
 488  
 
 489  
     /** @since 3.0 */
 490  
 
 491  
     public boolean isIntegerNumber()
 492  
     {
 493  0
         NumberStrategy strategy = (NumberStrategy) _numberAdaptors.getStrategy(_valueTypeClass);
 494  0
         if (strategy == null)
 495  0
             return false;
 496  
 
 497  0
         return strategy.getNumberType() == NUMBER_TYPE_INTEGER;
 498  
     }
 499  
 
 500  
     protected String getDefaultScriptPath()
 501  
     {
 502  0
         return "/org/apache/tapestry/valid/NumberValidator.script";
 503  
     }
 504  
 }