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
122                    ("The calculation caused an overflow: " + val1 + " * " + val2);
123            }
124            return (int) total;
125        }
126    
127        /**
128         * Multiply two values throwing an exception if overflow occurs.
129         * 
130         * @param val1  the first value
131         * @param scalar  the second value
132         * @return the new total
133         * @throws ArithmeticException if the value is too big or too small
134         * @since 1.2
135         */
136        public static long safeMultiply(long val1, int scalar) {
137            switch (scalar) {
138            case -1:
139                return -val1;
140            case 0:
141                return 0L;
142            case 1:
143                return val1;
144            }
145            long total = val1 * scalar;
146            if (total / scalar != val1) {
147                throw new ArithmeticException
148                    ("The calculation caused an overflow: " + val1 + " * " + scalar);
149            }
150            return total;
151        }
152    
153        /**
154         * Multiply two values throwing an exception if overflow occurs.
155         * 
156         * @param val1  the first value
157         * @param val2  the second value
158         * @return the new total
159         * @throws ArithmeticException if the value is too big or too small
160         */
161        public static long safeMultiply(long val1, long val2) {
162            if (val2 == 1) {
163                return val1;
164            }
165            if (val1 == 1) {
166                return val2;
167            }
168            if (val1 == 0 || val2 == 0) {
169                return 0;
170            }
171            long total = val1 * val2;
172            if (total / val2 != val1 || val1 == Long.MIN_VALUE && val2 == -1 || val2 == Long.MIN_VALUE && val1 == -1) {
173                throw new ArithmeticException("Multiplication overflows a long: " + val1 + " * " + val2);
174            }
175            return total;
176        }
177        
178        /**
179         * Casts to an int throwing an exception if overflow occurs.
180         * 
181         * @param value  the value
182         * @return the value as an int
183         * @throws ArithmeticException if the value is too big or too small
184         */
185        public static int safeToInt(long value) {
186            if (Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE) {
187                return (int) value;
188            }
189            throw new ArithmeticException("Value cannot fit in an int: " + value);
190        }
191        
192        /**
193         * Multiply two values to return an int throwing an exception if overflow occurs.
194         * 
195         * @param val1  the first value
196         * @param val2  the second value
197         * @return the new total
198         * @throws ArithmeticException if the value is too big or too small
199         */
200        public static int safeMultiplyToInt(long val1, long val2) {
201            long val = FieldUtils.safeMultiply(val1, val2);
202            return FieldUtils.safeToInt(val);
203        }
204    
205        //-----------------------------------------------------------------------
206        /**
207         * Verify that input values are within specified bounds.
208         * 
209         * @param value  the value to check
210         * @param lowerBound  the lower bound allowed for value
211         * @param upperBound  the upper bound allowed for value
212         * @throws IllegalFieldValueException if value is not in the specified bounds
213         */
214        public static void verifyValueBounds(DateTimeField field, 
215                                             int value, int lowerBound, int upperBound) {
216            if ((value < lowerBound) || (value > upperBound)) {
217                throw new IllegalFieldValueException
218                    (field.getType(), Integer.valueOf(value),
219                     Integer.valueOf(lowerBound), Integer.valueOf(upperBound));
220            }
221        }
222    
223        /**
224         * Verify that input values are within specified bounds.
225         * 
226         * @param value  the value to check
227         * @param lowerBound  the lower bound allowed for value
228         * @param upperBound  the upper bound allowed for value
229         * @throws IllegalFieldValueException if value is not in the specified bounds
230         * @since 1.1
231         */
232        public static void verifyValueBounds(DateTimeFieldType fieldType, 
233                                             int value, int lowerBound, int upperBound) {
234            if ((value < lowerBound) || (value > upperBound)) {
235                throw new IllegalFieldValueException
236                    (fieldType, Integer.valueOf(value),
237                     Integer.valueOf(lowerBound), Integer.valueOf(upperBound));
238            }
239        }
240    
241        /**
242         * Verify that input values are within specified bounds.
243         * 
244         * @param value  the value to check
245         * @param lowerBound  the lower bound allowed for value
246         * @param upperBound  the upper bound allowed for value
247         * @throws IllegalFieldValueException if value is not in the specified bounds
248         */
249        public static void verifyValueBounds(String fieldName,
250                                             int value, int lowerBound, int upperBound) {
251            if ((value < lowerBound) || (value > upperBound)) {
252                throw new IllegalFieldValueException
253                    (fieldName, Integer.valueOf(value),
254                     Integer.valueOf(lowerBound), Integer.valueOf(upperBound));
255            }
256        }
257    
258        /**
259         * Utility method used by addWrapField implementations to ensure the new
260         * value lies within the field's legal value range.
261         *
262         * @param currentValue the current value of the data, which may lie outside
263         * the wrapped value range
264         * @param wrapValue  the value to add to current value before
265         *  wrapping.  This may be negative.
266         * @param minValue the wrap range minimum value.
267         * @param maxValue the wrap range maximum value.  This must be
268         *  greater than minValue (checked by the method).
269         * @return the wrapped value
270         * @throws IllegalArgumentException if minValue is greater
271         *  than or equal to maxValue
272         */
273        public static int getWrappedValue(int currentValue, int wrapValue,
274                                          int minValue, int maxValue) {
275            return getWrappedValue(currentValue + wrapValue, minValue, maxValue);
276        }
277    
278        /**
279         * Utility method that ensures the given value lies within the field's
280         * legal value range.
281         * 
282         * @param value  the value to fit into the wrapped value range
283         * @param minValue the wrap range minimum value.
284         * @param maxValue the wrap range maximum value.  This must be
285         *  greater than minValue (checked by the method).
286         * @return the wrapped value
287         * @throws IllegalArgumentException if minValue is greater
288         *  than or equal to maxValue
289         */
290        public static int getWrappedValue(int value, int minValue, int maxValue) {
291            if (minValue >= maxValue) {
292                throw new IllegalArgumentException("MIN > MAX");
293            }
294    
295            int wrapRange = maxValue - minValue + 1;
296            value -= minValue;
297    
298            if (value >= 0) {
299                return (value % wrapRange) + minValue;
300            }
301    
302            int remByRange = (-value) % wrapRange;
303    
304            if (remByRange == 0) {
305                return 0 + minValue;
306            }
307            return (wrapRange - remByRange) + minValue;
308        }
309    
310        //-----------------------------------------------------------------------
311        /**
312         * Compares two objects as equals handling null.
313         * 
314         * @param object1  the first object
315         * @param object2  the second object
316         * @return true if equal
317         * @since 1.4
318         */
319        public static boolean equals(Object object1, Object object2) {
320            if (object1 == object2) {
321                return true;
322            }
323            if (object1 == null || object2 == null) {
324                return false;
325            }
326            return object1.equals(object2);
327        }
328    
329    }