Clover coverage report - Code Coverage for tapestry release 4.0-alpha-3
Coverage timestamp: Mon May 16 2005 09:05:49 EDT
file stats: LOC: 476   Methods: 37
NCLOC: 315   Classes: 12
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
NumberValidator.java 53.3% 82.9% 81.1% 77.9%
coverage coverage
 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 java.math.BigDecimal;
 18   
 import java.math.BigInteger;
 19   
 import java.util.HashMap;
 20   
 import java.util.Map;
 21   
 
 22   
 import org.apache.hivemind.ApplicationRuntimeException;
 23   
 import org.apache.hivemind.lib.util.StrategyRegistry;
 24   
 import org.apache.hivemind.lib.util.StrategyRegistryImpl;
 25   
 import org.apache.hivemind.util.PropertyUtils;
 26   
 import org.apache.tapestry.IMarkupWriter;
 27   
 import org.apache.tapestry.IRequestCycle;
 28   
 import org.apache.tapestry.Tapestry;
 29   
 import org.apache.tapestry.form.IFormComponent;
 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   
     private static final Map TYPES = new HashMap();
 42   
 
 43   
     static
 44   
     {
 45  1
         TYPES.put("boolean", boolean.class);
 46  1
         TYPES.put("Boolean", Boolean.class);
 47  1
         TYPES.put("java.lang.Boolean", Boolean.class);
 48  1
         TYPES.put("char", char.class);
 49  1
         TYPES.put("Character", Character.class);
 50  1
         TYPES.put("java.lang.Character", Character.class);
 51  1
         TYPES.put("short", short.class);
 52  1
         TYPES.put("Short", Short.class);
 53  1
         TYPES.put("java.lang.Short", Short.class);
 54  1
         TYPES.put("int", int.class);
 55  1
         TYPES.put("Integer", Integer.class);
 56  1
         TYPES.put("java.lang.Integer", Integer.class);
 57  1
         TYPES.put("long", long.class);
 58  1
         TYPES.put("Long", Long.class);
 59  1
         TYPES.put("java.lang.Long", Long.class);
 60  1
         TYPES.put("float", float.class);
 61  1
         TYPES.put("Float", Float.class);
 62  1
         TYPES.put("java.lang.Float", Float.class);
 63  1
         TYPES.put("byte", byte.class);
 64  1
         TYPES.put("Byte", Byte.class);
 65  1
         TYPES.put("java.lang.Byte", Byte.class);
 66  1
         TYPES.put("double", double.class);
 67  1
         TYPES.put("Double", Double.class);
 68  1
         TYPES.put("java.lang.Double", Double.class);
 69  1
         TYPES.put("java.math.BigInteger", BigInteger.class);
 70  1
         TYPES.put("java.math.BigDecimal", BigDecimal.class);
 71   
     }
 72   
 
 73   
     private Class _valueTypeClass = int.class;
 74   
 
 75   
     private Number _minimum;
 76   
 
 77   
     private Number _maximum;
 78   
 
 79   
     private static StrategyRegistry _numberAdaptors = new StrategyRegistryImpl();
 80   
 
 81   
     public final static int NUMBER_TYPE_INTEGER = 0;
 82   
 
 83   
     public final static int NUMBER_TYPE_REAL = 1;
 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   
     public static abstract 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   
         abstract public 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   
         abstract public int getNumberType();
 108   
 
 109  15
         public int compare(Number left, Number right)
 110   
         {
 111  15
             if (!left.getClass().equals(right.getClass()))
 112  8
                 right = coerce(right);
 113   
 
 114  15
             Comparable lc = (Comparable) left;
 115   
 
 116  15
             return lc.compareTo(right);
 117   
         }
 118   
 
 119   
         /**
 120   
          * Invoked when comparing two Numbers of different types. The number is cooerced from its
 121   
          * ordinary type to the correct type for comparison.
 122   
          * 
 123   
          * @since 3.0
 124   
          */
 125   
         protected abstract Number coerce(Number number);
 126   
     }
 127   
 
 128   
     private static abstract class IntegerNumberAdaptor extends NumberStrategy
 129   
     {
 130  6
         public int getNumberType()
 131   
         {
 132  6
             return NUMBER_TYPE_INTEGER;
 133   
         }
 134   
     }
 135   
 
 136   
     private static abstract class RealNumberAdaptor extends NumberStrategy
 137   
     {
 138  3
         public int getNumberType()
 139   
         {
 140  3
             return NUMBER_TYPE_REAL;
 141   
         }
 142   
     }
 143   
 
 144   
     private static class ByteAdaptor extends IntegerNumberAdaptor
 145   
     {
 146  1
         public Number parse(String value)
 147   
         {
 148  1
             return new Byte(value);
 149   
         }
 150   
 
 151  1
         protected Number coerce(Number number)
 152   
         {
 153  1
             return new Byte(number.byteValue());
 154   
         }
 155   
     }
 156   
 
 157   
     private static class ShortAdaptor extends IntegerNumberAdaptor
 158   
     {
 159  1
         public Number parse(String value)
 160   
         {
 161  1
             return new Short(value);
 162   
         }
 163   
 
 164  1
         protected Number coerce(Number number)
 165   
         {
 166  1
             return new Short(number.shortValue());
 167   
         }
 168   
     }
 169   
 
 170   
     private static class IntAdaptor extends IntegerNumberAdaptor
 171   
     {
 172  8
         public Number parse(String value)
 173   
         {
 174  8
             return new Integer(value);
 175   
         }
 176   
 
 177  1
         protected Number coerce(Number number)
 178   
         {
 179  1
             return new Integer(number.intValue());
 180   
         }
 181   
     }
 182   
 
 183   
     private static class LongAdaptor extends IntegerNumberAdaptor
 184   
     {
 185  1
         public Number parse(String value)
 186   
         {
 187  1
             return new Long(value);
 188   
         }
 189   
 
 190  1
         protected Number coerce(Number number)
 191   
         {
 192  1
             return new Long(number.longValue());
 193   
         }
 194   
     }
 195   
 
 196   
     private static class FloatAdaptor extends RealNumberAdaptor
 197   
     {
 198  1
         public Number parse(String value)
 199   
         {
 200  1
             return new Float(value);
 201   
         }
 202   
 
 203  1
         protected Number coerce(Number number)
 204   
         {
 205  1
             return new Float(number.floatValue());
 206   
         }
 207   
     }
 208   
 
 209   
     private static class DoubleAdaptor extends RealNumberAdaptor
 210   
     {
 211  1
         public Number parse(String value)
 212   
         {
 213  1
             return new Double(value);
 214   
         }
 215   
 
 216  1
         protected Number coerce(Number number)
 217   
         {
 218  1
             return new Double(number.doubleValue());
 219   
         }
 220   
     }
 221   
 
 222   
     private static class BigDecimalAdaptor extends RealNumberAdaptor
 223   
     {
 224  1
         public Number parse(String value)
 225   
         {
 226  1
             return new BigDecimal(value);
 227   
         }
 228   
 
 229  1
         protected Number coerce(Number number)
 230   
         {
 231  1
             return new BigDecimal(number.doubleValue());
 232   
         }
 233   
     }
 234   
 
 235   
     private static class BigIntegerAdaptor extends IntegerNumberAdaptor
 236   
     {
 237  1
         public Number parse(String value)
 238   
         {
 239  1
             return new BigInteger(value);
 240   
         }
 241   
 
 242  1
         protected Number coerce(Number number)
 243   
         {
 244  1
             return new BigInteger(number.toString());
 245   
         }
 246   
     }
 247   
 
 248   
     static
 249   
     {
 250  1
         NumberStrategy byteAdaptor = new ByteAdaptor();
 251  1
         NumberStrategy shortAdaptor = new ShortAdaptor();
 252  1
         NumberStrategy intAdaptor = new IntAdaptor();
 253  1
         NumberStrategy longAdaptor = new LongAdaptor();
 254  1
         NumberStrategy floatAdaptor = new FloatAdaptor();
 255  1
         NumberStrategy doubleAdaptor = new DoubleAdaptor();
 256   
 
 257  1
         _numberAdaptors.register(Byte.class, byteAdaptor);
 258  1
         _numberAdaptors.register(byte.class, byteAdaptor);
 259  1
         _numberAdaptors.register(Short.class, shortAdaptor);
 260  1
         _numberAdaptors.register(short.class, shortAdaptor);
 261  1
         _numberAdaptors.register(Integer.class, intAdaptor);
 262  1
         _numberAdaptors.register(int.class, intAdaptor);
 263  1
         _numberAdaptors.register(Long.class, longAdaptor);
 264  1
         _numberAdaptors.register(long.class, longAdaptor);
 265  1
         _numberAdaptors.register(Float.class, floatAdaptor);
 266  1
         _numberAdaptors.register(float.class, floatAdaptor);
 267  1
         _numberAdaptors.register(Double.class, doubleAdaptor);
 268  1
         _numberAdaptors.register(double.class, doubleAdaptor);
 269   
 
 270  1
         _numberAdaptors.register(BigDecimal.class, new BigDecimalAdaptor());
 271  1
         _numberAdaptors.register(BigInteger.class, new BigIntegerAdaptor());
 272   
     }
 273   
 
 274  25
     public NumberValidator()
 275   
     {
 276   
 
 277   
     }
 278   
 
 279   
     /**
 280   
      * Initializes the NumberValidator with properties defined by the initializer.
 281   
      * 
 282   
      * @since 4.0
 283   
      */
 284   
 
 285  0
     public NumberValidator(String initializer)
 286   
     {
 287  0
         PropertyUtils.configureProperties(this, initializer);
 288   
     }
 289   
 
 290  14
     public String toString(IFormComponent field, Object value)
 291   
     {
 292  14
         if (value == null)
 293  0
             return null;
 294   
 
 295  14
         if (getZeroIsNull())
 296   
         {
 297  0
             Number number = (Number) value;
 298   
 
 299  0
             if (number.doubleValue() == 0.0)
 300  0
                 return null;
 301   
         }
 302   
 
 303  14
         return value.toString();
 304   
     }
 305   
 
 306  15
     private NumberStrategy getStrategy(IFormComponent field)
 307   
     {
 308  15
         NumberStrategy result = getStrategy(_valueTypeClass);
 309   
 
 310  15
         if (result == null)
 311  0
             throw new ApplicationRuntimeException(Tapestry.format(
 312   
                     "NumberValidator.no-adaptor-for-field",
 313   
                     field,
 314   
                     _valueTypeClass.getName()));
 315   
 
 316  15
         return result;
 317   
     }
 318   
 
 319   
     /**
 320   
      * Returns an strategy for the given type.
 321   
      * <p>
 322   
      * Note: this method exists only for testing purposes. It is not meant to be invoked by user
 323   
      * code and is subject to change at any time.
 324   
      * 
 325   
      * @param type
 326   
      *            the type (a Number subclass) for which to return an adaptor
 327   
      * @return the adaptor, or null if no such adaptor may be found
 328   
      * @since 3.0
 329   
      */
 330  31
     public static NumberStrategy getStrategy(Class type)
 331   
     {
 332  31
         return (NumberStrategy) _numberAdaptors.getStrategy(type);
 333   
     }
 334   
 
 335  15
     public Object toObject(IFormComponent field, String value) throws ValidatorException
 336   
     {
 337  15
         if (checkRequired(field, value))
 338  0
             return null;
 339   
 
 340  15
         NumberStrategy adaptor = getStrategy(field);
 341  15
         Number result = null;
 342   
 
 343  15
         try
 344   
         {
 345  15
             result = adaptor.parse(value);
 346   
         }
 347   
         catch (NumberFormatException ex)
 348   
         {
 349  2
             throw new ValidatorException(buildInvalidNumericFormatMessage(field),
 350   
                     ValidationConstraint.NUMBER_FORMAT);
 351   
         }
 352   
 
 353  13
         if (_minimum != null && adaptor.compare(result, _minimum) < 0)
 354  2
             throw new ValidatorException(buildNumberTooSmallMessage(field, _minimum),
 355   
                     ValidationConstraint.TOO_SMALL);
 356   
 
 357  11
         if (_maximum != null && adaptor.compare(result, _maximum) > 0)
 358  2
             throw new ValidatorException(buildNumberTooLargeMessage(field, _maximum),
 359   
                     ValidationConstraint.TOO_LARGE);
 360   
 
 361  9
         return result;
 362   
     }
 363   
 
 364  0
     public Number getMaximum()
 365   
     {
 366  0
         return _maximum;
 367   
     }
 368   
 
 369  0
     public boolean getHasMaximum()
 370   
     {
 371  0
         return _maximum != null;
 372   
     }
 373   
 
 374  4
     public void setMaximum(Number maximum)
 375   
     {
 376  4
         _maximum = maximum;
 377   
     }
 378   
 
 379  0
     public Number getMinimum()
 380   
     {
 381  0
         return _minimum;
 382   
     }
 383   
 
 384  0
     public boolean getHasMinimum()
 385   
     {
 386  0
         return _minimum != null;
 387   
     }
 388   
 
 389  4
     public void setMinimum(Number minimum)
 390   
     {
 391  4
         _minimum = minimum;
 392   
     }
 393   
 
 394   
     /**
 395   
      * @since 2.2
 396   
      */
 397   
 
 398  1
     public void renderValidatorContribution(IFormComponent field, IMarkupWriter writer,
 399   
             IRequestCycle cycle)
 400   
     {
 401  1
         if (!isClientScriptingEnabled())
 402  0
             return;
 403   
 
 404  1
         if (!(isRequired() || _minimum != null || _maximum != null))
 405  0
             return;
 406   
 
 407  1
         Map symbols = new HashMap();
 408   
 
 409  1
         if (isRequired())
 410  1
             symbols.put("requiredMessage", buildRequiredMessage(field));
 411   
 
 412  1
         if (isIntegerNumber())
 413  1
             symbols.put("formatMessage", buildInvalidIntegerFormatMessage(field));
 414   
         else
 415  0
             symbols.put("formatMessage", buildInvalidNumericFormatMessage(field));
 416   
 
 417  1
         if (_minimum != null || _maximum != null)
 418  0
             symbols.put("rangeMessage", buildRangeMessage(field, _minimum, _maximum));
 419   
 
 420  1
         processValidatorScript(getScriptPath(), cycle, field, symbols);
 421   
     }
 422   
 
 423   
     /**
 424   
      * Sets the value type from a string type name. The name may be a scalar numeric type, a fully
 425   
      * qualified class name, or the name of a numeric wrapper type from java.lang (with the package
 426   
      * name omitted).
 427   
      * 
 428   
      * @since 3.0
 429   
      */
 430   
 
 431  0
     public void setValueType(String typeName)
 432   
     {
 433  0
         Class typeClass = (Class) TYPES.get(typeName);
 434   
 
 435  0
         if (typeClass == null)
 436  0
             throw new ApplicationRuntimeException(Tapestry.format(
 437   
                     "NumberValidator.unknown-type",
 438   
                     typeName));
 439   
 
 440  0
         _valueTypeClass = typeClass;
 441   
     }
 442   
 
 443   
     /** @since 3.0 * */
 444   
 
 445  15
     public void setValueTypeClass(Class valueTypeClass)
 446   
     {
 447  15
         _valueTypeClass = valueTypeClass;
 448   
     }
 449   
 
 450   
     /**
 451   
      * Returns the value type to convert strings back into. The default is int.
 452   
      * 
 453   
      * @since 3.0
 454   
      */
 455   
 
 456  0
     public Class getValueTypeClass()
 457   
     {
 458  0
         return _valueTypeClass;
 459   
     }
 460   
 
 461   
     /** @since 3.0 */
 462   
 
 463  1
     public boolean isIntegerNumber()
 464   
     {
 465  1
         NumberStrategy strategy = (NumberStrategy) _numberAdaptors.getStrategy(_valueTypeClass);
 466  1
         if (strategy == null)
 467  0
             return false;
 468   
 
 469  1
         return strategy.getNumberType() == NUMBER_TYPE_INTEGER;
 470   
     }
 471   
 
 472  25
     protected String getDefaultScriptPath()
 473   
     {
 474  25
         return "/org/apache/tapestry/valid/NumberValidator.script";
 475   
     }
 476   
 }