001    /*
002     *  Copyright 2001-2005 Stephen Colebourne
003     *
004     *  Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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.joda.time.field;
017    
018    import org.joda.time.DateTimeField;
019    import org.joda.time.DateTimeFieldType;
020    import org.joda.time.IllegalFieldValueException;
021    
022    /**
023     * General utilities that don't fit elsewhere.
024     * <p>
025     * FieldUtils is thread-safe and immutable.
026     *
027     * @author Stephen Colebourne
028     * @since 1.0
029     */
030    public class FieldUtils {
031    
032        /**
033         * Restricted constructor.
034         */
035        private FieldUtils() {
036            super();
037        }
038        
039        //------------------------------------------------------------------------
040        /**
041         * Negates the input throwing an exception if it can't negate it.
042         * 
043         * @param value  the value to negate
044         * @return the negated value
045         * @throws ArithmeticException if the value is Integer.MIN_VALUE
046         * @since 1.1
047         */
048        public static int safeNegate(int value) {
049            if (value == Integer.MIN_VALUE) {
050                throw new ArithmeticException("Integer.MIN_VALUE cannot be negated");
051            }
052            return -value;
053        }
054        
055        /**
056         * Add two values throwing an exception if overflow occurs.
057         * 
058         * @param val1  the first value
059         * @param val2  the second value
060         * @return the new total
061         * @throws ArithmeticException if the value is too big or too small
062         */
063        public static int safeAdd(int val1, int val2) {
064            int sum = val1 + val2;
065            // If there is a sign change, but the two values have the same sign...
066            if ((val1 ^ sum) < 0 && (val1 ^ val2) >= 0) {
067                throw new ArithmeticException
068                    ("The calculation caused an overflow: " + val1 + " + " + val2);
069            }
070            return sum;
071        }
072        
073        /**
074         * Add two values throwing an exception if overflow occurs.
075         * 
076         * @param val1  the first value
077         * @param val2  the second value
078         * @return the new total
079         * @throws ArithmeticException if the value is too big or too small
080         */
081        public static long safeAdd(long val1, long val2) {
082            long sum = val1 + val2;
083            // If there is a sign change, but the two values have the same sign...
084            if ((val1 ^ sum) < 0 && (val1 ^ val2) >= 0) {
085                throw new ArithmeticException
086                    ("The calculation caused an overflow: " + val1 + " + " + val2);
087            }
088            return sum;
089        }
090        
091        /**
092         * Subtracts two values throwing an exception if overflow occurs.
093         * 
094         * @param val1  the first value, to be taken away from
095         * @param val2  the second value, the amount to take away
096         * @return the new total
097         * @throws ArithmeticException if the value is too big or too small
098         */
099        public static long safeSubtract(long val1, long val2) {
100            long diff = val1 - val2;
101            // If there is a sign change, but the two values have different signs...
102            if ((val1 ^ diff) < 0 && (val1 ^ val2) < 0) {
103                throw new ArithmeticException
104                    ("The calculation caused an overflow: " + val1 + " - " + val2);
105            }
106            return diff;
107        }
108        
109        /**
110         * Multiply two values throwing an exception if overflow occurs.
111         * 
112         * @param val1  the first value
113         * @param val2  the second value
114         * @return the new total
115         * @throws ArithmeticException if the value is too big or too small
116         * @since 1.2
117         */
118        public static int safeMultiply(int val1, int val2) {
119            long total = (long) val1 * (long) val2;
120            if (total < Integer.MIN_VALUE || total > Integer.MAX_VALUE) {
121              throw new ArithmeticException("Multiplication overflows an int: " + val1 + " * " + val2);
122            }
123            return (int) total;
124        }
125    
126        /**
127         * Multiply two values throwing an exception if overflow occurs.
128         * 
129         * @param val1  the first value
130         * @param val2  the second value
131         * @return the new total
132         * @throws ArithmeticException if the value is too big or too small
133         * @since 1.2
134         */
135        public static long safeMultiply(long val1, int val2) {
136            switch (val2) {
137                case -1:
138                    if (val1 == Long.MIN_VALUE) {
139                        throw new ArithmeticException("Multiplication overflows a long: " + val1 + " * " + val2);
140                    }
141                    return -val1;
142                case 0:
143                    return 0L;
144                case 1:
145                    return val1;
146            }
147            long total = val1 * val2;
148            if (total / val2 != val1) {
149              throw new ArithmeticException("Multiplication overflows a long: " + val1 + " * " + val2);
150            }
151            return total;
152        }
153    
154        /**
155         * Multiply two values throwing an exception if overflow occurs.
156         * 
157         * @param val1  the first value
158         * @param val2  the second value
159         * @return the new total
160         * @throws ArithmeticException if the value is too big or too small
161         */
162        public static long safeMultiply(long val1, long val2) {
163            if (val2 == 1) {
164                return val1;
165            }
166            if (val1 == 1) {
167                return val2;
168            }
169            if (val1 == 0 || val2 == 0) {
170                return 0;
171            }
172            long total = val1 * val2;
173            if (total / val2 != val1 || val1 == Long.MIN_VALUE && val2 == -1 || val2 == Long.MIN_VALUE && val1 == -1) {
174                throw new ArithmeticException("Multiplication overflows a long: " + val1 + " * " + val2);
175            }
176            return total;
177        }
178        
179        /**
180         * Casts to an int throwing an exception if overflow occurs.
181         * 
182         * @param value  the value
183         * @return the value as an int
184         * @throws ArithmeticException if the value is too big or too small
185         */
186        public static int safeToInt(long value) {
187            if (Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE) {
188                return (int) value;
189            }
190            throw new ArithmeticException("Value cannot fit in an int: " + value);
191        }
192        
193        /**
194         * Multiply two values to return an int throwing an exception if overflow occurs.
195         * 
196         * @param val1  the first value
197         * @param val2  the second value
198         * @return the new total
199         * @throws ArithmeticException if the value is too big or too small
200         */
201        public static int safeMultiplyToInt(long val1, long val2) {
202            long val = FieldUtils.safeMultiply(val1, val2);
203            return FieldUtils.safeToInt(val);
204        }
205    
206        //-----------------------------------------------------------------------
207        /**
208         * Verify that input values are within specified bounds.
209         * 
210         * @param value  the value to check
211         * @param lowerBound  the lower bound allowed for value
212         * @param upperBound  the upper bound allowed for value
213         * @throws IllegalFieldValueException if value is not in the specified bounds
214         */
215        public static void verifyValueBounds(DateTimeField field, 
216                                             int value, int lowerBound, int upperBound) {
217            if ((value < lowerBound) || (value > upperBound)) {
218                throw new IllegalFieldValueException
219                    (field.getType(), Integer.valueOf(value),
220                     Integer.valueOf(lowerBound), Integer.valueOf(upperBound));
221            }
222        }
223    
224        /**
225         * Verify that input values are within specified bounds.
226         * 
227         * @param value  the value to check
228         * @param lowerBound  the lower bound allowed for value
229         * @param upperBound  the upper bound allowed for value
230         * @throws IllegalFieldValueException if value is not in the specified bounds
231         * @since 1.1
232         */
233        public static void verifyValueBounds(DateTimeFieldType fieldType, 
234                                             int value, int lowerBound, int upperBound) {
235            if ((value < lowerBound) || (value > upperBound)) {
236                throw new IllegalFieldValueException
237                    (fieldType, Integer.valueOf(value),
238                     Integer.valueOf(lowerBound), Integer.valueOf(upperBound));
239            }
240        }
241    
242        /**
243         * Verify that input values are within specified bounds.
244         * 
245         * @param value  the value to check
246         * @param lowerBound  the lower bound allowed for value
247         * @param upperBound  the upper bound allowed for value
248         * @throws IllegalFieldValueException if value is not in the specified bounds
249         */
250        public static void verifyValueBounds(String fieldName,
251                                             int value, int lowerBound, int upperBound) {
252            if ((value < lowerBound) || (value > upperBound)) {
253                throw new IllegalFieldValueException
254                    (fieldName, Integer.valueOf(value),
255                     Integer.valueOf(lowerBound), Integer.valueOf(upperBound));
256            }
257        }
258    
259        /**
260         * Utility method used by addWrapField implementations to ensure the new
261         * value lies within the field's legal value range.
262         *
263         * @param currentValue the current value of the data, which may lie outside
264         * the wrapped value range
265         * @param wrapValue  the value to add to current value before
266         *  wrapping.  This may be negative.
267         * @param minValue the wrap range minimum value.
268         * @param maxValue the wrap range maximum value.  This must be
269         *  greater than minValue (checked by the method).
270         * @return the wrapped value
271         * @throws IllegalArgumentException if minValue is greater
272         *  than or equal to maxValue
273         */
274        public static int getWrappedValue(int currentValue, int wrapValue,
275                                          int minValue, int maxValue) {
276            return getWrappedValue(currentValue + wrapValue, minValue, maxValue);
277        }
278    
279        /**
280         * Utility method that ensures the given value lies within the field's
281         * legal value range.
282         * 
283         * @param value  the value to fit into the wrapped value range
284         * @param minValue the wrap range minimum value.
285         * @param maxValue the wrap range maximum value.  This must be
286         *  greater than minValue (checked by the method).
287         * @return the wrapped value
288         * @throws IllegalArgumentException if minValue is greater
289         *  than or equal to maxValue
290         */
291        public static int getWrappedValue(int value, int minValue, int maxValue) {
292            if (minValue >= maxValue) {
293                throw new IllegalArgumentException("MIN > MAX");
294            }
295    
296            int wrapRange = maxValue - minValue + 1;
297            value -= minValue;
298    
299            if (value >= 0) {
300                return (value % wrapRange) + minValue;
301            }
302    
303            int remByRange = (-value) % wrapRange;
304    
305            if (remByRange == 0) {
306                return 0 + minValue;
307            }
308            return (wrapRange - remByRange) + minValue;
309        }
310    
311        //-----------------------------------------------------------------------
312        /**
313         * Compares two objects as equals handling null.
314         * 
315         * @param object1  the first object
316         * @param object2  the second object
317         * @return true if equal
318         * @since 1.4
319         */
320        public static boolean equals(Object object1, Object object2) {
321            if (object1 == object2) {
322                return true;
323            }
324            if (object1 == null || object2 == null) {
325                return false;
326            }
327            return object1.equals(object2);
328        }
329    
330    }