Java tutorial
/** * Copyright 2005-2014 The Kuali Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ecl2.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kuali.rice.core.api.util.type; import org.apache.commons.lang.StringUtils; import java.math.BigDecimal; /** * This class is a wrapper around java.math.BigDecimal. It exposes the only the * needed functionality of BigDecimal and uses a standard ROUND_BEHAVIOR of * BigDecimal.ROUND_HALF_UP * * Members of this class are, like BigDecimal, immutable; even methods which * might be expected to change the value actually just return a new instance * with the new value. */ public abstract class AbstractKualiDecimal<T extends AbstractKualiDecimal> extends Number implements Comparable { public static final int ROUND_BEHAVIOR = BigDecimal.ROUND_HALF_UP; public static final KualiDecimal ZERO = new KualiDecimal(BigDecimal.ZERO); protected BigDecimal value; public AbstractKualiDecimal() { } /** * This is the base constructor, used by constructors that take other types * * @param value * String containing numeric value - defaults to zero */ public AbstractKualiDecimal(String value, int scale) { if (StringUtils.isBlank(value)) { this.value = BigDecimal.ZERO.setScale(scale, ROUND_BEHAVIOR); } else { this.value = new BigDecimal(value).setScale(scale, ROUND_BEHAVIOR); } } public AbstractKualiDecimal(int value, int scale) { this.value = new BigDecimal(value).setScale(scale, ROUND_BEHAVIOR); } public AbstractKualiDecimal(double value, int scale) { this.value = new BigDecimal(value).setScale(scale, ROUND_BEHAVIOR); } public AbstractKualiDecimal(BigDecimal value, int scale) { this(value.toPlainString(), scale); } /** * @param operand * @return true if this AbstractKualiDecimal is less than the given * AbstractKualiDecimal */ public boolean isLessThan(AbstractKualiDecimal operand) { if (operand == null) { throw new IllegalArgumentException("invalid (null) operand"); } return (this.compareTo(operand) == -1); } /** * @param operand * @return true if this AbstractKualiDecimal is greater than the given * AbstractKualiDecimal */ public boolean isGreaterThan(AbstractKualiDecimal operand) { if (operand == null) { throw new IllegalArgumentException("invalid (null) operand"); } return (this.compareTo(operand) == 1); } /** * @param operand * @return true if this AbstractKualiDecimal is less than or equal to the * given AbstractKualiDecimal */ public boolean isLessEqual(AbstractKualiDecimal operand) { if (operand == null) { throw new IllegalArgumentException("invalid (null) operand"); } return !isGreaterThan(operand); } /** * @param operand * @return true if this AbstractKualiDecimal is greater than or equal to the * given AbstractKualiDecimal */ public boolean isGreaterEqual(AbstractKualiDecimal operand) { if (operand == null) { throw new IllegalArgumentException("invalid (null) operand"); } return !isLessThan(operand); } /** * @return true if the given String can be used to construct a valid * AbstractKualiDecimal */ public static boolean isNumeric(String s) { boolean isValid = false; if (!StringUtils.isBlank(s)) { try { new BigDecimal(s); isValid = true; } catch (NumberFormatException e) { } } return isValid; } // Number methods /** * @see java.lang.Number#doubleValue() */ @Override public double doubleValue() { return this.value.doubleValue(); } /** * @see java.lang.Number#floatValue() */ @Override public float floatValue() { return this.value.floatValue(); } /** * @see java.lang.Number#intValue() */ @Override public int intValue() { return this.value.intValue(); } /** * @see java.lang.Number#longValue() */ @Override public long longValue() { return this.value.longValue(); } /** * @return the value of this instance as a BigDecimal. */ public BigDecimal bigDecimalValue() { return this.value; } // Comparable methods /** * Compares this AbstractKualiDecimal with the specified Object. If the * Object is a AbstractKualiDecimal, this method behaves like * java.lang.Comparable#compareTo(java.lang.Object). * * Otherwise, it throws a <tt>ClassCastException</tt> (as KualiDecimals * are comparable only to other KualiDecimals). * * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(Object o) { return compareTo((AbstractKualiDecimal) o); } /** * Returns the result of comparing the values of this AbstractKualiDecimal * and the given AbstractKualiDecimal. * * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(AbstractKualiDecimal k) { return this.value.compareTo(k.value); } // Object methods /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { boolean equals = false; if (obj instanceof AbstractKualiDecimal) { AbstractKualiDecimal k = (AbstractKualiDecimal) obj; // using AbstractKualiDecimal.compareTo instead of BigDecimal.equals // since // BigDecimal.equals only returns true if the // scale and precision are equal, rather than comparing the actual // (scaled) values equals = (this.compareTo(k) == 0); } return equals; } /** * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return this.value.hashCode(); } /** * @see java.lang.Object#toString() */ @Override public String toString() { return this.value.toString(); } /** * @return true if this T is less than zero */ public boolean isNegative() { return (this.compareTo(ZERO) == -1); } /** * @return true if this T is greater than zero */ public boolean isPositive() { return (this.compareTo(ZERO) == 1); } /** * @return true if this T is equal to zero */ public boolean isZero() { return (this.compareTo(ZERO) == 0); } /** * @return a T with the same scale and the absolute value */ public T abs() { T absolute = null; if (isNegative()) { absolute = negated(); } else { absolute = newInstance(this.value, this.value.scale()); } return absolute; } /** * @return true if this T is not equal to zero */ public boolean isNonZero() { return !this.isZero(); } /** * Wraps BigDecimal's add method to accept and return T instances instead of * BigDecimals, so that users of the class don't have to typecast the return * value. * * @param addend * @return result of adding the given addend to this value * @throws IllegalArgumentException * if the given addend is null */ public T add(T addend) { if (addend == null) { throw new IllegalArgumentException("invalid (null) addend"); } BigDecimal sum = this.value.add(addend.value); return newInstance(sum, sum.scale()); } /** * Wraps BigDecimal's subtract method to accept and return T instances * instead of BigDecimals, so that users of the class don't have to typecast * the return value. * * @param subtrahend * @return result of the subtracting the given subtrahend from this value * @throws IllegalArgumentException * if the given subtrahend is null */ public T subtract(T subtrahend) { if (subtrahend == null) { throw new IllegalArgumentException("invalid (null) subtrahend"); } BigDecimal difference = this.value.subtract(subtrahend.value); return newInstance(difference, difference.scale()); } /** * Wraps BigDecimal's multiply method to accept and return T instances * instead of BigDecimals, so that users of the class don't have to typecast * the return value. * * @param multiplicand * @return result of multiplying this value by the given multiplier * @throws IllegalArgumentException * if the given multiplier is null */ public T multiply(T multiplier) { return multiply(multiplier, true); } /** * Overloaded multiply method where we can specify if we need to preserve the precision of the result * * @param multiplicand * @param applyScale * @return result of multiplying this value by the given multiplier * @throws IllegalArgumentException * if the given multiplier is null */ public T multiply(T multiplier, boolean applyScale) { if (multiplier == null) { throw new IllegalArgumentException("invalid (null) multiplier"); } BigDecimal product = this.value.multiply(multiplier.value); return newInstance(product, applyScale ? this.value.scale() : product.scale()); } /** * This method calculates the mod between to T values by first casting to * doubles and then by performing the % operation on the two primitives. * * @param modulus * The other value to apply the mod to. * @return result of performing the mod calculation * @throws IllegalArgumentException * if the given modulus is null */ public T mod(T modulus) { return mod(modulus, true); } /** * Overloaded mod method where we can specify if we want to preserve the result's precision * * @param modulus * The other value to apply the mod to. * @param applyScale * @return result of performing the mod calculation * @throws IllegalArgumentException * if the given modulus is null */ public T mod(T modulus, boolean applyScale) { if (modulus == null) { throw new IllegalArgumentException("invalid (null) modulus"); } double difference = this.value.doubleValue() % modulus.doubleValue(); int scaleToApply = applyScale ? this.value.scale() : new BigDecimal(difference).scale(); //return (T) newInstance(new BigDecimal(difference).setScale(scaleToApply, BigDecimal.ROUND_UNNECESSARY), scaleToApply); return (T) newInstance(new BigDecimal(difference), scaleToApply); } /** * Wraps BigDecimal's divide method to enforce the default Kuali rounding * behavior * * @param divisor * @return result of dividing this value by the given divisor * @throws an * IllegalArgumentException if the given divisor is null */ public T divide(T divisor) { return divide(divisor, true); } /** * @return a T with the same scale and a negated value (iff the value is * non-zero) */ public T negated() { return multiply(newInstance("-1")); } public T divide(T divisor, boolean applyScale) { if (divisor == null) { throw new IllegalArgumentException("invalid (null) divisor"); } BigDecimal quotient = this.value.divide(divisor.value, ROUND_BEHAVIOR); T result = newInstance(quotient, applyScale ? this.value.scale() : quotient.scale()); return result; } protected abstract T newInstance(String value); protected abstract T newInstance(double value); protected abstract T newInstance(double value, int scale); protected abstract T newInstance(BigDecimal value); protected abstract T newInstance(BigDecimal value, int scale); }