1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.util;
18
19import java.util.Collection;
20
21/**
22 * Simple static methods to be called at the start of your own methods to verify
23 * correct arguments and state.
24 */
25public class Preconditions {
26
27    /**
28     * Ensures that an object reference passed as a parameter to the calling
29     * method is not null.
30     *
31     * @param reference an object reference
32     * @return the non-null reference that was validated
33     * @throws NullPointerException if {@code reference} is null
34     */
35    public static <T> T checkNotNull(final T reference) {
36        if (reference == null) {
37            throw new NullPointerException();
38        }
39        return reference;
40    }
41
42    /**
43     * Ensures that an object reference passed as a parameter to the calling
44     * method is not null.
45     *
46     * @param reference an object reference
47     * @param errorMessage the exception message to use if the check fails; will
48     *     be converted to a string using {@link String#valueOf(Object)}
49     * @return the non-null reference that was validated
50     * @throws NullPointerException if {@code reference} is null
51     */
52    public static <T> T checkNotNull(final T reference, final Object errorMessage) {
53        if (reference == null) {
54            throw new NullPointerException(String.valueOf(errorMessage));
55        }
56        return reference;
57    }
58
59    /**
60     * Ensures the truth of an expression involving the state of the calling
61     * instance, but not involving any parameters to the calling method.
62     *
63     * @param expression a boolean expression
64     * @throws IllegalStateException if {@code expression} is false
65     */
66    public static void checkState(final boolean expression) {
67        if (!expression) {
68            throw new IllegalStateException();
69        }
70    }
71
72    /**
73     * Check the requested flags, throwing if any requested flags are outside
74     * the allowed set.
75     */
76    public static void checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
77        if ((requestedFlags & allowedFlags) != requestedFlags) {
78            throw new IllegalArgumentException("Requested flags 0x"
79                    + Integer.toHexString(requestedFlags) + ", but only 0x"
80                    + Integer.toHexString(allowedFlags) + " are allowed");
81        }
82    }
83
84    /**
85     * Ensures that that the argument numeric value is non-negative.
86     *
87     * @param value a numeric int value
88     * @param errorMessage the exception message to use if the check fails
89     * @return the validated numeric value
90     * @throws IllegalArgumentException if {@code value} was negative
91     */
92    public static int checkArgumentNonnegative(final int value, final String errorMessage) {
93        if (value < 0) {
94            throw new IllegalArgumentException(errorMessage);
95        }
96
97        return value;
98    }
99
100    /**
101     * Ensures that that the argument numeric value is non-negative.
102     *
103     * @param value a numeric long value
104     * @param errorMessage the exception message to use if the check fails
105     * @return the validated numeric value
106     * @throws IllegalArgumentException if {@code value} was negative
107     */
108    public static long checkArgumentNonnegative(final long value, final String errorMessage) {
109        if (value < 0) {
110            throw new IllegalArgumentException(errorMessage);
111        }
112
113        return value;
114    }
115
116    /**
117     * Ensures that that the argument numeric value is positive.
118     *
119     * @param value a numeric int value
120     * @param errorMessage the exception message to use if the check fails
121     * @return the validated numeric value
122     * @throws IllegalArgumentException if {@code value} was not positive
123     */
124    public static int checkArgumentPositive(final int value, final String errorMessage) {
125        if (value <= 0) {
126            throw new IllegalArgumentException(errorMessage);
127        }
128
129        return value;
130    }
131
132    /**
133     * Ensures that the argument floating point value is a finite number.
134     *
135     * <p>A finite number is defined to be both representable (that is, not NaN) and
136     * not infinite (that is neither positive or negative infinity).</p>
137     *
138     * @param value a floating point value
139     * @param valueName the name of the argument to use if the check fails
140     *
141     * @return the validated floating point value
142     *
143     * @throws IllegalArgumentException if {@code value} was not finite
144     */
145    public static float checkArgumentFinite(final float value, final String valueName) {
146        if (Float.isNaN(value)) {
147            throw new IllegalArgumentException(valueName + " must not be NaN");
148        } else if (Float.isInfinite(value)) {
149            throw new IllegalArgumentException(valueName + " must not be infinite");
150        }
151
152        return value;
153    }
154
155    /**
156     * Ensures that the argument floating point value is within the inclusive range.
157     *
158     * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
159     * will always be out of range.</p>
160     *
161     * @param value a floating point value
162     * @param lower the lower endpoint of the inclusive range
163     * @param upper the upper endpoint of the inclusive range
164     * @param valueName the name of the argument to use if the check fails
165     *
166     * @return the validated floating point value
167     *
168     * @throws IllegalArgumentException if {@code value} was not within the range
169     */
170    public static float checkArgumentInRange(float value, float lower, float upper,
171            String valueName) {
172        if (Float.isNaN(value)) {
173            throw new IllegalArgumentException(valueName + " must not be NaN");
174        } else if (value < lower) {
175            throw new IllegalArgumentException(
176                    String.format(
177                            "%s is out of range of [%f, %f] (too low)", valueName, lower, upper));
178        } else if (value > upper) {
179            throw new IllegalArgumentException(
180                    String.format(
181                            "%s is out of range of [%f, %f] (too high)", valueName, lower, upper));
182        }
183
184        return value;
185    }
186
187    /**
188     * Ensures that the argument int value is within the inclusive range.
189     *
190     * @param value a int value
191     * @param lower the lower endpoint of the inclusive range
192     * @param upper the upper endpoint of the inclusive range
193     * @param valueName the name of the argument to use if the check fails
194     *
195     * @return the validated int value
196     *
197     * @throws IllegalArgumentException if {@code value} was not within the range
198     */
199    public static int checkArgumentInRange(int value, int lower, int upper,
200            String valueName) {
201        if (value < lower) {
202            throw new IllegalArgumentException(
203                    String.format(
204                            "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
205        } else if (value > upper) {
206            throw new IllegalArgumentException(
207                    String.format(
208                            "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
209        }
210
211        return value;
212    }
213
214    /**
215     * Ensures that the array is not {@code null}, and none of its elements are {@code null}.
216     *
217     * @param value an array of boxed objects
218     * @param valueName the name of the argument to use if the check fails
219     *
220     * @return the validated array
221     *
222     * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
223     */
224    public static <T> T[] checkArrayElementsNotNull(final T[] value, final String valueName) {
225        if (value == null) {
226            throw new NullPointerException(valueName + " must not be null");
227        }
228
229        for (int i = 0; i < value.length; ++i) {
230            if (value[i] == null) {
231                throw new NullPointerException(
232                        String.format("%s[%d] must not be null", valueName, i));
233            }
234        }
235
236        return value;
237    }
238
239    /**
240     * Ensures that the {@link Collection} is not {@code null}, and none of its elements are
241     * {@code null}.
242     *
243     * @param value a {@link Collection} of boxed objects
244     * @param valueName the name of the argument to use if the check fails
245     *
246     * @return the validated {@link Collection}
247     *
248     * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
249     */
250    public static <T> Collection<T> checkCollectionElementsNotNull(final Collection<T> value,
251            final String valueName) {
252        if (value == null) {
253            throw new NullPointerException(valueName + " must not be null");
254        }
255
256        long ctr = 0;
257        for (T elem : value) {
258            if (elem == null) {
259                throw new NullPointerException(
260                        String.format("%s[%d] must not be null", valueName, ctr));
261            }
262            ++ctr;
263        }
264
265        return value;
266    }
267
268    /**
269     * Ensures that the {@link Collection} is not {@code null}, and contains at least one element.
270     *
271     * @param value a {@link Collection} of boxed elements.
272     * @param valueName the name of the argument to use if the check fails.
273
274     * @return the validated {@link Collection}
275     *
276     * @throws NullPointerException if the {@code value} was {@code null}
277     * @throws IllegalArgumentException if the {@code value} was empty
278     */
279    public static <T> Collection<T> checkCollectionNotEmpty(final Collection<T> value,
280            final String valueName) {
281        if (value == null) {
282            throw new NullPointerException(valueName + " must not be null");
283        }
284        if (value.isEmpty()) {
285            throw new IllegalArgumentException(valueName + " is empty");
286        }
287        return value;
288    }
289
290    /**
291     * Ensures that all elements in the argument floating point array are within the inclusive range
292     *
293     * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
294     * will always be out of range.</p>
295     *
296     * @param value a floating point array of values
297     * @param lower the lower endpoint of the inclusive range
298     * @param upper the upper endpoint of the inclusive range
299     * @param valueName the name of the argument to use if the check fails
300     *
301     * @return the validated floating point value
302     *
303     * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
304     * @throws NullPointerException if the {@code value} was {@code null}
305     */
306    public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
307            String valueName) {
308        checkNotNull(value, valueName + " must not be null");
309
310        for (int i = 0; i < value.length; ++i) {
311            float v = value[i];
312
313            if (Float.isNaN(v)) {
314                throw new IllegalArgumentException(valueName + "[" + i + "] must not be NaN");
315            } else if (v < lower) {
316                throw new IllegalArgumentException(
317                        String.format("%s[%d] is out of range of [%f, %f] (too low)",
318                                valueName, i, lower, upper));
319            } else if (v > upper) {
320                throw new IllegalArgumentException(
321                        String.format("%s[%d] is out of range of [%f, %f] (too high)",
322                                valueName, i, lower, upper));
323            }
324        }
325
326        return value;
327    }
328}
329