Coverage Report - org.kuali.rice.kns.util.AbstractKualiDecimal
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractKualiDecimal
0%
0/84
0%
0/50
1.667
 
 1  
 /*
 2  
  * Copyright 2005-2008 The Kuali Foundation
 3  
  * 
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kns.util;
 17  
 
 18  
 import java.math.BigDecimal;
 19  
 
 20  
 import org.apache.commons.lang.StringUtils;
 21  
 
 22  
 /**
 23  
  * This class is a wrapper around java.math.BigDecimal. It exposes the only the
 24  
  * needed functionality of BigDecimal and uses a standard ROUND_BEHAVIOR of
 25  
  * BigDecimal.ROUND_HALF_UP
 26  
  * 
 27  
  * Members of this class are, like BigDecimal, immutable; even methods which
 28  
  * might be expected to change the value actually just return a new instance
 29  
  * with the new value.
 30  
  */
 31  
 public abstract class AbstractKualiDecimal<T extends AbstractKualiDecimal> extends Number implements Comparable {
 32  
         
 33  
         public static final int ROUND_BEHAVIOR = BigDecimal.ROUND_HALF_UP;
 34  
 
 35  0
     public static final KualiDecimal ZERO = new KualiDecimal(BigDecimal.ZERO);
 36  
     
 37  
         protected BigDecimal value;
 38  
 
 39  0
         public AbstractKualiDecimal() {
 40  0
         }
 41  
         
 42  
         /**
 43  
          * This is the base constructor, used by constructors that take other types
 44  
          * 
 45  
          * @param value
 46  
          *            String containing numeric value - defaults to zero
 47  
          */
 48  0
         public AbstractKualiDecimal(String value, int scale) {
 49  0
                 if (StringUtils.isBlank(value)) {
 50  0
                         this.value = BigDecimal.ZERO.setScale(scale,ROUND_BEHAVIOR);
 51  
                 } else {
 52  0
                         this.value = new BigDecimal(value).setScale(scale, ROUND_BEHAVIOR);
 53  
                 }
 54  0
         }
 55  
 
 56  0
         public AbstractKualiDecimal(int value, int scale) {
 57  0
             this.value = new BigDecimal(value).setScale(scale, ROUND_BEHAVIOR);
 58  0
         }
 59  
 
 60  0
         public AbstractKualiDecimal(double value, int scale) {
 61  0
                 this.value = new BigDecimal(value).setScale(scale, ROUND_BEHAVIOR);
 62  0
         }
 63  
 
 64  
         public AbstractKualiDecimal(BigDecimal value, int scale) {
 65  0
                 this(value.toPlainString(), scale);
 66  0
         }
 67  
 
 68  
         /**
 69  
          * @param operand
 70  
          * @return true if this AbstractKualiDecimal is less than the given
 71  
          *         AbstractKualiDecimal
 72  
          */
 73  
         public boolean isLessThan(AbstractKualiDecimal operand) {
 74  0
                 if (operand == null) {
 75  0
                         throw new IllegalArgumentException("invalid (null) operand");
 76  
                 }
 77  
 
 78  0
                 return (this.compareTo(operand) == -1);
 79  
         }
 80  
 
 81  
         /**
 82  
          * @param operand
 83  
          * @return true if this AbstractKualiDecimal is greater than the given
 84  
          *         AbstractKualiDecimal
 85  
          */
 86  
         public boolean isGreaterThan(AbstractKualiDecimal operand) {
 87  0
                 if (operand == null) {
 88  0
                         throw new IllegalArgumentException("invalid (null) operand");
 89  
                 }
 90  
 
 91  0
                 return (this.compareTo(operand) == 1);
 92  
         }
 93  
 
 94  
         /**
 95  
          * @param operand
 96  
          * @return true if this AbstractKualiDecimal is less than or equal to the
 97  
          *         given AbstractKualiDecimal
 98  
          */
 99  
         public boolean isLessEqual(AbstractKualiDecimal operand) {
 100  0
                 if (operand == null) {
 101  0
                         throw new IllegalArgumentException("invalid (null) operand");
 102  
                 }
 103  
 
 104  0
                 return !isGreaterThan(operand);
 105  
         }
 106  
 
 107  
         /**
 108  
          * @param operand
 109  
          * @return true if this AbstractKualiDecimal is greater than or equal to the
 110  
          *         given AbstractKualiDecimal
 111  
          */
 112  
         public boolean isGreaterEqual(AbstractKualiDecimal operand) {
 113  0
                 if (operand == null) {
 114  0
                         throw new IllegalArgumentException("invalid (null) operand");
 115  
                 }
 116  
 
 117  0
                 return !isLessThan(operand);
 118  
         }
 119  
 
 120  
         /**
 121  
          * @return true if the given String can be used to construct a valid
 122  
          *         AbstractKualiDecimal
 123  
          */
 124  
         public static boolean isNumeric(String s) {
 125  0
                 boolean isValid = false;
 126  
 
 127  0
                 if (!StringUtils.isBlank(s)) {
 128  
                         try {
 129  0
                                 new BigDecimal(s);
 130  0
                                 isValid = true;
 131  0
                         } catch (NumberFormatException e) {
 132  0
                         }
 133  
                 }
 134  
 
 135  0
                 return isValid;
 136  
         }
 137  
 
 138  
         // Number methods
 139  
         /**
 140  
          * @see java.lang.Number#doubleValue()
 141  
          */
 142  
         @Override
 143  
         public double doubleValue() {
 144  0
                 return this.value.doubleValue();
 145  
         }
 146  
 
 147  
         /**
 148  
          * @see java.lang.Number#floatValue()
 149  
          */
 150  
         @Override
 151  
         public float floatValue() {
 152  0
                 return this.value.floatValue();
 153  
         }
 154  
 
 155  
         /**
 156  
          * @see java.lang.Number#intValue()
 157  
          */
 158  
         @Override
 159  
         public int intValue() {
 160  0
                 return this.value.intValue();
 161  
         }
 162  
 
 163  
         /**
 164  
          * @see java.lang.Number#longValue()
 165  
          */
 166  
         @Override
 167  
         public long longValue() {
 168  0
                 return this.value.longValue();
 169  
         }
 170  
 
 171  
         /**
 172  
          * @return the value of this instance as a BigDecimal.
 173  
          */
 174  
         public BigDecimal bigDecimalValue() {
 175  0
                 return this.value;
 176  
         }
 177  
 
 178  
         // Comparable methods
 179  
         /**
 180  
          * Compares this AbstractKualiDecimal with the specified Object. If the
 181  
          * Object is a AbstractKualiDecimal, this method behaves like
 182  
          * java.lang.Comparable#compareTo(java.lang.Object).
 183  
          * 
 184  
          * Otherwise, it throws a <tt>ClassCastException</tt> (as KualiDecimals
 185  
          * are comparable only to other KualiDecimals).
 186  
          * 
 187  
          * @see java.lang.Comparable#compareTo(java.lang.Object)
 188  
          */
 189  
         public int compareTo(Object o) {
 190  0
                 return compareTo((AbstractKualiDecimal) o);
 191  
         }
 192  
         
 193  
         /**
 194  
          * Returns the result of comparing the values of this AbstractKualiDecimal
 195  
          * and the given AbstractKualiDecimal.
 196  
          * 
 197  
          * @see java.lang.Comparable#compareTo(java.lang.Object)
 198  
          */
 199  
         public int compareTo(AbstractKualiDecimal k) {
 200  0
                 return this.value.compareTo(k.value);
 201  
         }
 202  
 
 203  
         // Object methods
 204  
         /**
 205  
          * @see java.lang.Object#equals(java.lang.Object)
 206  
          */
 207  
         @Override
 208  
         public boolean equals(Object obj) {
 209  0
                 boolean equals = false;
 210  
 
 211  0
                 if (obj instanceof AbstractKualiDecimal) {
 212  0
                         AbstractKualiDecimal k = (AbstractKualiDecimal) obj;
 213  
 
 214  
                         // using AbstractKualiDecimal.compareTo instead of BigDecimal.equals
 215  
                         // since
 216  
                         // BigDecimal.equals only returns true if the
 217  
                         // scale and precision are equal, rather than comparing the actual
 218  
                         // (scaled) values
 219  0
                         equals = (this.compareTo(k) == 0);
 220  
                 }
 221  
 
 222  0
                 return equals;
 223  
         }
 224  
 
 225  
         /**
 226  
          * 
 227  
          * @see java.lang.Object#hashCode()
 228  
          */
 229  
         @Override
 230  
         public int hashCode() {
 231  0
                 return this.value.hashCode();
 232  
         }
 233  
 
 234  
         /**
 235  
          * @see java.lang.Object#toString()
 236  
          */
 237  
         @Override
 238  
         public String toString() {
 239  0
                 return this.value.toString();
 240  
         }
 241  
 
 242  
         /**
 243  
          * @return true if this T is less than zero
 244  
          */
 245  
         public boolean isNegative() {
 246  0
                 return (this.compareTo(ZERO) == -1);
 247  
         }
 248  
 
 249  
         /**
 250  
          * @return true if this T is greater than zero
 251  
          */
 252  
         public boolean isPositive() {
 253  0
                 return (this.compareTo(ZERO) == 1);
 254  
         }
 255  
 
 256  
         /**
 257  
          * @return true if this T is equal to zero
 258  
          */
 259  
         public boolean isZero() {
 260  0
                 return (this.compareTo(ZERO) == 0);
 261  
         }
 262  
 
 263  
         /**
 264  
          * @return a T with the same scale and the absolute value
 265  
          */
 266  
         public T abs() {
 267  0
                 T absolute = null;
 268  
 
 269  0
                 if (isNegative()) {
 270  0
                         absolute = negated();
 271  
                 } else {
 272  0
                         absolute = newInstance(this.value, this.value.scale());
 273  
                 }
 274  
 
 275  0
                 return absolute;
 276  
         }
 277  
 
 278  
         /**
 279  
          * @return true if this T is not equal to zero
 280  
          */
 281  
         public boolean isNonZero() {
 282  0
                 return !this.isZero();
 283  
         }
 284  
 
 285  
         /**
 286  
          * Wraps BigDecimal's add method to accept and return T instances instead of
 287  
          * BigDecimals, so that users of the class don't have to typecast the return
 288  
          * value.
 289  
          * 
 290  
          * @param addend
 291  
          * @return result of adding the given addend to this value
 292  
          * @throws IllegalArgumentException
 293  
          *             if the given addend is null
 294  
          */
 295  
         public T add(T addend) {
 296  0
                 if (addend == null) {
 297  0
                         throw new IllegalArgumentException("invalid (null) addend");
 298  
                 }
 299  
 
 300  0
                 BigDecimal sum = this.value.add(addend.value);
 301  0
                 return newInstance(sum, sum.scale());
 302  
         }
 303  
 
 304  
         /**
 305  
          * Wraps BigDecimal's subtract method to accept and return T instances
 306  
          * instead of BigDecimals, so that users of the class don't have to typecast
 307  
          * the return value.
 308  
          * 
 309  
          * @param subtrahend
 310  
          * @return result of the subtracting the given subtrahend from this value
 311  
          * @throws IllegalArgumentException
 312  
          *             if the given subtrahend is null
 313  
          */
 314  
         public T subtract(T subtrahend) {
 315  0
                 if (subtrahend == null) {
 316  0
                         throw new IllegalArgumentException("invalid (null) subtrahend");
 317  
                 }
 318  
 
 319  0
                 BigDecimal difference = this.value.subtract(subtrahend.value);
 320  0
                 return newInstance(difference, difference.scale());
 321  
         }
 322  
 
 323  
         /**
 324  
          * Wraps BigDecimal's multiply method to accept and return T instances
 325  
          * instead of BigDecimals, so that users of the class don't have to typecast
 326  
          * the return value.
 327  
          * 
 328  
          * @param multiplicand
 329  
          * @return result of multiplying this value by the given multiplier
 330  
          * @throws IllegalArgumentException
 331  
          *             if the given multiplier is null
 332  
          */
 333  
         public T multiply(T multiplier) {
 334  0
             return multiply(multiplier, true);
 335  
         }
 336  
 
 337  
     /**
 338  
      * Overloaded multiply method where we can specify if we need to preserve the precision of the result
 339  
      * 
 340  
      * @param multiplicand
 341  
      * @param applyScale
 342  
      * @return result of multiplying this value by the given multiplier
 343  
      * @throws IllegalArgumentException
 344  
      *             if the given multiplier is null
 345  
      */
 346  
    public T multiply(T multiplier, boolean applyScale) {
 347  0
         if (multiplier == null) {
 348  0
             throw new IllegalArgumentException("invalid (null) multiplier");
 349  
         }
 350  
 
 351  0
         BigDecimal product = this.value.multiply(multiplier.value);  
 352  0
         return newInstance(product, applyScale ? this.value.scale() : product.scale());
 353  
     }
 354  
 
 355  
         /**
 356  
          * This method calculates the mod between to T values by first casting to
 357  
          * doubles and then by performing the % operation on the two primitives.
 358  
          * 
 359  
          * @param modulus
 360  
          *            The other value to apply the mod to.
 361  
          * @return result of performing the mod calculation
 362  
          * @throws IllegalArgumentException
 363  
          *             if the given modulus is null
 364  
          */
 365  
         public T mod(T modulus) {
 366  0
             return mod(modulus, true);
 367  
         }
 368  
 
 369  
     /**
 370  
      * Overloaded mod method where we can specify if we want to preserve the result's precision
 371  
      * 
 372  
      * @param modulus
 373  
      *            The other value to apply the mod to.
 374  
      * @param applyScale
 375  
      * @return result of performing the mod calculation
 376  
      * @throws IllegalArgumentException
 377  
      *             if the given modulus is null
 378  
      */
 379  
   
 380  
     public T mod(T modulus, boolean applyScale) {
 381  0
         if (modulus == null) {
 382  0
             throw new IllegalArgumentException("invalid (null) modulus");
 383  
         }
 384  0
         double difference = this.value.doubleValue() % modulus.doubleValue();
 385  
 
 386  0
         int scaleToApply = applyScale ? this.value.scale() : new BigDecimal(difference).scale();
 387  
         
 388  
         //return (T) newInstance(new BigDecimal(difference).setScale(scaleToApply, BigDecimal.ROUND_UNNECESSARY), scaleToApply);
 389  0
         return (T) newInstance(new BigDecimal(difference), scaleToApply);
 390  
     }
 391  
 
 392  
 
 393  
         /**
 394  
          * Wraps BigDecimal's divide method to enforce the default Kuali rounding
 395  
          * behavior
 396  
          * 
 397  
          * @param divisor
 398  
          * @return result of dividing this value by the given divisor
 399  
          * @throws an
 400  
          *             IllegalArgumentException if the given divisor is null
 401  
          */
 402  
         public T divide(T divisor) {
 403  0
                 return divide(divisor, true);
 404  
         }
 405  
 
 406  
         /**
 407  
          * @return a T with the same scale and a negated value (iff the value is
 408  
          *         non-zero)
 409  
          */
 410  
         public T negated() {
 411  0
                 return multiply(newInstance("-1"));
 412  
         }
 413  
 
 414  
         public T divide(T divisor, boolean applyScale) {
 415  0
                 if (divisor == null) {
 416  0
                         throw new IllegalArgumentException("invalid (null) divisor");
 417  
                 }
 418  0
                 BigDecimal quotient = this.value.divide(divisor.value, ROUND_BEHAVIOR);
 419  
 
 420  0
                 T result = newInstance(quotient, applyScale ? this.value.scale() : quotient.scale());
 421  0
                 return result;
 422  
         }
 423  
 
 424  
         protected abstract T newInstance(String value);
 425  
 
 426  
         protected abstract T newInstance(double value);
 427  
         
 428  
         protected abstract T newInstance(double value, int scale);
 429  
 
 430  
         protected abstract T newInstance(BigDecimal value);
 431  
 
 432  
         protected abstract T newInstance(BigDecimal value, int scale);
 433  
 }