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    }