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