001 /*
002 * Copyright 2011 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community License, Version 2.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/ecl2.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.kfs.module.cam.util;
017
018 import java.util.Currency;
019
020 import org.apache.commons.lang.ArrayUtils;
021 import org.kuali.rice.kns.util.KualiDecimal;
022
023 /**
024 * Utility class that divides currency into equal targets with remainder to cents in some buckets.<br>
025 */
026 public class KualiDecimalUtils {
027
028 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiDecimalUtils.class);
029 private static final int[] cents = new int[] { 1, 10, 100, 1000 };
030
031 private long totalAmount;
032 private Currency currencyCode;
033
034 /**
035 * Default constructor.
036 */
037 public KualiDecimalUtils() {
038 }
039
040 /**
041 * Constructs a KualiDecimalService.
042 *
043 * @param amount
044 * @param currency
045 */
046 public KualiDecimalUtils(KualiDecimal totalAmount, Currency currencyCode) {
047 this.currencyCode = currencyCode;
048 this.totalAmount = Math.round(totalAmount.multiply(new KualiDecimal(centFactor())).floatValue());
049 }
050
051 /**
052 * Gets the default number of fraction digits used with the given currency.
053 *
054 * @return int
055 */
056 private int centFactor() {
057 return cents[currencyCode.getDefaultFractionDigits()];
058 }
059
060 /**
061 * Sets a new amount.
062 *
063 * @param amount
064 * @return KualiDecimalService
065 */
066 private KualiDecimalUtils setNewAmount(long amount) {
067 KualiDecimalUtils kds = new KualiDecimalUtils();
068 kds.totalAmount = amount;
069 return kds;
070 }
071
072 /**
073 * Allocate a sum of money amongst many targets by quantity.
074 *
075 * @param divisor
076 * @return KualiDecimal[]
077 */
078 public KualiDecimal[] allocateByQuantity(int divisor) {
079 // calculate lowest and highest amount
080 KualiDecimalUtils lowAmount = setNewAmount(totalAmount / divisor);
081 KualiDecimalUtils highAmount = setNewAmount(lowAmount.totalAmount + 1);
082
083 int remainder = (int) totalAmount % divisor;
084
085 // allocate amounts into array
086 KualiDecimal[] amountsArray = new KualiDecimal[divisor];
087
088 // set the lowest amount into array
089 KualiDecimal low = new KualiDecimal(lowAmount.totalAmount);
090 for (int i = remainder; i < divisor; i++) {
091 amountsArray[i] = low.divide(new KualiDecimal(centFactor()));
092 }
093
094 // set the highest amount into array
095 KualiDecimal high = new KualiDecimal(highAmount.totalAmount);
096 for (int i = 0; i < remainder; i++) {
097 amountsArray[i] = high.divide(new KualiDecimal(centFactor()));
098 }
099
100 ArrayUtils.reverse(amountsArray);
101
102 return amountsArray;
103 }
104
105 /**
106 * Allocate a sum of money amongst many targets by ratio.
107 *
108 * @param divisor
109 * @return KualiDecimal[]
110 */
111 public static KualiDecimal[] allocateByRatio(KualiDecimal amount, double[] ratios) {
112
113 if (ratios == null || ratios.length == 0 || amount == null) {
114 return amount == null ? null : new KualiDecimal[] { amount };
115 }
116 double value = amount.doubleValue();
117
118 // if just one ratio, apply and return
119 if (ratios.length == 1) {
120 return new KualiDecimal[] { new KualiDecimal(value * ratios[0]) };
121 }
122 KualiDecimal allocated = KualiDecimal.ZERO;
123 // allocate amounts into array
124 KualiDecimal[] amountsArray = new KualiDecimal[ratios.length];
125 for (int i = 0; i < ratios.length - 1; i++) {
126 KualiDecimal currAmount = new KualiDecimal(value * ratios[i]);
127 amountsArray[i] = currAmount;
128 allocated = allocated.add(currAmount);
129 }
130 // adjust the last one with remaining value
131 amountsArray[ratios.length - 1] = new KualiDecimal(value).subtract(allocated);
132 return amountsArray;
133 }
134
135 /**
136 * Makes sure no null pointer exception occurs on fields that can accurately be null when multiplying. If either field are null
137 * the value is returned.
138 *
139 * @param value
140 * @param multiplier
141 * @return
142 */
143 public KualiDecimal safeMultiply(KualiDecimal value, double multiplier) {
144 if (value == null) {
145 return value;
146 }
147 else {
148 return new KualiDecimal(value.doubleValue() * multiplier);
149 }
150 }
151
152 /**
153 * Makes sure no null pointer exception occurs on fields that can accurately be null when subtracting. If either field are null
154 * the value is returned.
155 *
156 * @param value
157 * @param subtrahend
158 * @return
159 */
160 public KualiDecimal safeSubtract(KualiDecimal value, KualiDecimal subtrahend) {
161 if (subtrahend == null || value == null) {
162 return value;
163 }
164
165 return value.subtract(subtrahend);
166 }
167 }