001: /*
002: * Copyright 2005-2006 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.core.util;
017:
018: import java.math.BigDecimal;
019:
020: import org.apache.commons.lang.StringUtils;
021:
022: /**
023: * This class is a wrapper around java.math.BigDecimal. It exposes the only the
024: * needed functionality of BigDecimal and uses a standard SCALE of 2 and
025: * ROUND_BEHAVIOR of BigDecimal.ROUND_HALF_UP
026: *
027: * Members of this class are, like BigDecimal, immutable; even methods which
028: * might be expected to change the value (like setScale, for example) actually
029: * just return a new instance with the new value.
030: */
031: public class KualiDecimal extends Number implements Comparable {
032: public static final KualiDecimal ZERO = new KualiDecimal(0);
033:
034: // Default rounding behavior is Banker's Rounding. This means that
035: // it rounds towards the "nearest neighbor" unless both neighbors
036: // are equidistant, in which case, round towards the even neighbor.
037: public static final int ROUND_BEHAVIOR = BigDecimal.ROUND_HALF_UP;
038:
039: public static final int SCALE = 2;
040:
041: private final BigDecimal value;
042:
043: /**
044: * This is the base constructor, used by constructors that take other types
045: *
046: * @param value
047: * String containing numeric value
048: * @throws IllegalArgumentException
049: * if the given String is null
050: */
051: public KualiDecimal(String value) {
052: if (value == null) {
053: throw new IllegalArgumentException(
054: "invalid (null) String in KualiDecimal constructor");
055: }
056: this .value = new BigDecimal(value).setScale(SCALE,
057: ROUND_BEHAVIOR);
058: }
059:
060: public KualiDecimal(int value) {
061: this (value + "");
062: }
063:
064: public KualiDecimal(double value) {
065: this (value + "");
066: }
067:
068: public KualiDecimal(BigDecimal value) {
069: this (value.toPlainString());
070: }
071:
072: /**
073: * @param operand
074: * @return true if this KualiDecimal is less than the given KualiDecimal
075: */
076: public boolean isLessThan(KualiDecimal operand) {
077: if (operand == null) {
078: throw new IllegalArgumentException("invalid (null) operand");
079: }
080:
081: return (this .compareTo(operand) == -1);
082: }
083:
084: /**
085: * @param operand
086: * @return true if this KualiDecimal is greater than the given KualiDecimal
087: */
088: public boolean isGreaterThan(KualiDecimal operand) {
089: if (operand == null) {
090: throw new IllegalArgumentException("invalid (null) operand");
091: }
092:
093: return (this .compareTo(operand) == 1);
094: }
095:
096: /**
097: * @param operand
098: * @return true if this KualiDecimal is less than or equal to the given
099: * KualiDecimal
100: */
101: public boolean isLessEqual(KualiDecimal operand) {
102: if (operand == null) {
103: throw new IllegalArgumentException("invalid (null) operand");
104: }
105:
106: return !isGreaterThan(operand);
107: }
108:
109: /**
110: * @param operand
111: * @return true if this KualiDecimal is greater than or equal to the given
112: * KualiDecimal
113: */
114: public boolean isGreaterEqual(KualiDecimal operand) {
115: if (operand == null) {
116: throw new IllegalArgumentException("invalid (null) operand");
117: }
118:
119: return !isLessThan(operand);
120: }
121:
122: /**
123: * @return true if this KualiDecimal is less than zero
124: */
125: public boolean isNegative() {
126: return (this .compareTo(ZERO) == -1);
127: }
128:
129: /**
130: * @return true if this KualiDecimal is greater than zero
131: */
132: public boolean isPositive() {
133: return (this .compareTo(ZERO) == 1);
134: }
135:
136: /**
137: * @return true if this KualiDecimal is equal to zero
138: */
139: public boolean isZero() {
140: return (this .compareTo(ZERO) == 0);
141: }
142:
143: /**
144: * @return true if this KualiDecimal is not equal to zero
145: */
146: public boolean isNonZero() {
147: return !this .isZero();
148: }
149:
150: /**
151: * Wraps BigDecimal's add method to accept and return KualiDecimal instances
152: * instead of BigDecimals, so that users of the class don't have to typecast
153: * the return value.
154: *
155: * @param addend
156: * @return result of adding the given addend to this value
157: * @throws IllegalArgumentException
158: * if the given addend is null
159: */
160: public KualiDecimal add(KualiDecimal addend) {
161: if (addend == null) {
162: throw new IllegalArgumentException("invalid (null) addend");
163: }
164:
165: BigDecimal sum = this .value.add(addend.value);
166: return new KualiDecimal(sum);
167: }
168:
169: /**
170: * Wraps BigDecimal's subtract method to accept and return KualiDecimal
171: * instances instead of BigDecimals, so that users of the class don't have
172: * to typecast the return value.
173: *
174: * @param subtrahend
175: * @return result of the subtracting the given subtrahend from this value
176: * @throws IllegalArgumentException
177: * if the given subtrahend is null
178: */
179: public KualiDecimal subtract(KualiDecimal subtrahend) {
180: if (subtrahend == null) {
181: throw new IllegalArgumentException(
182: "invalid (null) subtrahend");
183: }
184:
185: BigDecimal difference = this .value.subtract(subtrahend.value);
186: return new KualiDecimal(difference);
187: }
188:
189: /**
190: * Wraps BigDecimal's multiply method to accept and return KualiDecimal
191: * instances instead of BigDecimals, so that users of the class don't have
192: * to typecast the return value.
193: *
194: * @param multiplicand
195: * @return result of multiplying this value by the given multiplier
196: * @throws IllegalArgumentException
197: * if the given multiplier is null
198: */
199: public KualiDecimal multiply(KualiDecimal multiplier) {
200: if (multiplier == null) {
201: throw new IllegalArgumentException(
202: "invalid (null) multiplier");
203: }
204:
205: BigDecimal product = this .value.multiply(multiplier.value);
206: return new KualiDecimal(product);
207: }
208:
209: /**
210: * This method calculates the mod between to KualiDecimal values by first
211: * casting to doubles and then by performing the % operation on the two
212: * primitives.
213: *
214: * @param modulus
215: * The other value to apply the mod to.
216: * @return result of performing the mod calculation
217: * @throws IllegalArgumentException
218: * if the given modulus is null
219: */
220: public KualiDecimal mod(KualiDecimal modulus) {
221: if (modulus == null) {
222: throw new IllegalArgumentException("invalid (null) modulus");
223: }
224:
225: double difference = this .value.doubleValue()
226: % modulus.doubleValue();
227:
228: return new KualiDecimal(new BigDecimal(difference).setScale(
229: SCALE, BigDecimal.ROUND_UNNECESSARY));
230: }
231:
232: /**
233: * Wraps BigDecimal's divide method to enforce the default Kuali rounding
234: * behavior
235: *
236: * @param divisor
237: * @return result of dividing this value by the given divisor
238: * @throws an
239: * IllegalArgumentException if the given divisor is null
240: */
241: public KualiDecimal divide(KualiDecimal divisor) {
242: if (divisor == null) {
243: throw new IllegalArgumentException("invalid (null) divisor");
244: }
245:
246: BigDecimal quotient = this .value.divide(divisor.value,
247: ROUND_BEHAVIOR);
248: return new KualiDecimal(quotient);
249: }
250:
251: /**
252: * Wraps BigDecimal's setScale method to enforce the default Kuali rounding
253: * behavior.
254: *
255: * @return KualiDecimal instance set to the given scale, rounded with the
256: * default rounding behavior (if necessary)
257: */
258: public KualiDecimal setScale(int scale) {
259: BigDecimal scaled = this .value.setScale(scale, ROUND_BEHAVIOR);
260: return new KualiDecimal(scaled);
261: }
262:
263: /**
264: * Simplified wrapper over the setScale() method, this one has no arguments.
265: * When used with no arguments, it defaults to the Kuali default Scale and
266: * Rounding.
267: *
268: * @return a rounded, scaled, KualiDecimal
269: */
270: public KualiDecimal setScale() {
271: return setScale(SCALE);
272: }
273:
274: /**
275: * @return a KualiDecimal with the same scale and a negated value (iff the
276: * value is non-zero)
277: */
278: public KualiDecimal negated() {
279: return multiply(new KualiDecimal("-1"));
280: }
281:
282: /**
283: * @return a KualiDecimal with the same scale and the absolute value
284: */
285: public KualiDecimal abs() {
286: KualiDecimal absolute = null;
287:
288: if (isNegative()) {
289: absolute = negated();
290: } else {
291: absolute = this ;
292: }
293:
294: return absolute;
295: }
296:
297: /**
298: * @return true if the given String can be used to construct a valid
299: * KualiDecimal
300: */
301: public static boolean isNumeric(String s) {
302: boolean isValid = false;
303:
304: if (!StringUtils.isBlank(s)) {
305: try {
306: new KualiDecimal(s);
307: isValid = true;
308: } catch (NumberFormatException e) {
309: }
310: }
311:
312: return isValid;
313: }
314:
315: // Number methods
316: /**
317: * @see java.lang.Number#doubleValue()
318: */
319: @Override
320: public double doubleValue() {
321: return this .value.doubleValue();
322: }
323:
324: /**
325: * @see java.lang.Number#floatValue()
326: */
327: @Override
328: public float floatValue() {
329: return this .value.floatValue();
330: }
331:
332: /**
333: * @see java.lang.Number#intValue()
334: */
335: @Override
336: public int intValue() {
337: return this .value.intValue();
338: }
339:
340: /**
341: * @see java.lang.Number#longValue()
342: */
343: @Override
344: public long longValue() {
345: return this .value.longValue();
346: }
347:
348: /**
349: * @return the value of this instance as a BigDecimal.
350: */
351: public BigDecimal bigDecimalValue() {
352: return this .value;
353: }
354:
355: // Comparable methods
356: /**
357: * Compares this KualiDecimal with the specified Object. If the Object is a
358: * KualiDecimal, this method behaves like
359: * java.lang.Comparable#compareTo(java.lang.Object).
360: *
361: * Otherwise, it throws a <tt>ClassCastException</tt> (as KualiDecimals
362: * are comparable only to other KualiDecimals).
363: *
364: * @see java.lang.Comparable#compareTo(java.lang.Object)
365: */
366: public int compareTo(Object o) {
367: return compareTo((KualiDecimal) o);
368: }
369:
370: /**
371: * Returns the result of comparing the values of this KualiDecimal and the
372: * given KualiDecimal.
373: *
374: * @see java.lang.Comparable#compareTo(java.lang.Object)
375: */
376: public int compareTo(KualiDecimal k) {
377: return this .value.compareTo(k.value);
378: }
379:
380: // Object methods
381: /**
382: * @see java.lang.Object#equals(java.lang.Object)
383: */
384: @Override
385: public boolean equals(Object obj) {
386: boolean equals = false;
387:
388: if (obj instanceof KualiDecimal) {
389: KualiDecimal k = (KualiDecimal) obj;
390:
391: // using KualiDecimal.compareTo instead of BigDecimal.equals since
392: // BigDecimal.equals only returns true if the
393: // scale and precision are equal, rather than comparing the actual
394: // (scaled) values
395: equals = (this .compareTo(k) == 0);
396: }
397:
398: return equals;
399: }
400:
401: /**
402: *
403: * @see java.lang.Object#hashCode()
404: */
405: @Override
406: public int hashCode() {
407: return this .value.hashCode();
408: }
409:
410: /**
411: * @see java.lang.Object#toString()
412: */
413: @Override
414: public String toString() {
415: return this.value.toString();
416: }
417: }
|