• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *  * Redistributions of source code must retain the above copyright notice,
10  *    this list of conditions and the following disclaimer.
11  *
12  *  * Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  *  * Neither the name of JSR-310 nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package org.threeten.bp.temporal;
33 
34 import java.io.Serializable;
35 
36 import org.threeten.bp.DateTimeException;
37 
38 /**
39  * The range of valid values for a date-time field.
40  * <p>
41  * All {@link TemporalField} instances have a valid range of values.
42  * For example, the ISO day-of-month runs from 1 to somewhere between 28 and 31.
43  * This class captures that valid range.
44  * <p>
45  * It is important to be aware of the limitations of this class.
46  * Only the minimum and maximum values are provided.
47  * It is possible for there to be invalid values within the outer range.
48  * For example, a weird field may have valid values of 1, 2, 4, 6, 7, thus
49  * have a range of '1 - 7', despite that fact that values 3 and 5 are invalid.
50  * <p>
51  * Instances of this class are not tied to a specific field.
52  *
53  * <h3>Specification for implementors</h3>
54  * This class is immutable and thread-safe.
55  */
56 public final class ValueRange implements Serializable {
57 
58     /**
59      * Serialization version.
60      */
61     private static final long serialVersionUID = -7317881728594519368L;
62 
63     /**
64      * The smallest minimum value.
65      */
66     private final long minSmallest;
67     /**
68      * The largest minimum value.
69      */
70     private final long minLargest;
71     /**
72      * The smallest maximum value.
73      */
74     private final long maxSmallest;
75     /**
76      * The largest maximum value.
77      */
78     private final long maxLargest;
79 
80     /**
81      * Obtains a fixed value range.
82      * <p>
83      * This factory obtains a range where the minimum and maximum values are fixed.
84      * For example, the ISO month-of-year always runs from 1 to 12.
85      *
86      * @param min  the minimum value
87      * @param max  the maximum value
88      * @return the ValueRange for min, max, not null
89      * @throws IllegalArgumentException if the minimum is greater than the maximum
90      */
of(long min, long max)91     public static ValueRange of(long min, long max) {
92         if (min > max) {
93             throw new IllegalArgumentException("Minimum value must be less than maximum value");
94         }
95         return new ValueRange(min, min, max, max);
96     }
97 
98     /**
99      * Obtains a variable value range.
100      * <p>
101      * This factory obtains a range where the minimum value is fixed and the maximum value may vary.
102      * For example, the ISO day-of-month always starts at 1, but ends between 28 and 31.
103      *
104      * @param min  the minimum value
105      * @param maxSmallest  the smallest maximum value
106      * @param maxLargest  the largest maximum value
107      * @return the ValueRange for min, smallest max, largest max, not null
108      * @throws IllegalArgumentException if
109      *     the minimum is greater than the smallest maximum,
110      *  or the smallest maximum is greater than the largest maximum
111      */
of(long min, long maxSmallest, long maxLargest)112     public static ValueRange of(long min, long maxSmallest, long maxLargest) {
113         return of(min, min, maxSmallest, maxLargest);
114     }
115 
116     /**
117      * Obtains a fully variable value range.
118      * <p>
119      * This factory obtains a range where both the minimum and maximum value may vary.
120      *
121      * @param minSmallest  the smallest minimum value
122      * @param minLargest  the largest minimum value
123      * @param maxSmallest  the smallest maximum value
124      * @param maxLargest  the largest maximum value
125      * @return the ValueRange for smallest min, largest min, smallest max, largest max, not null
126      * @throws IllegalArgumentException if
127      *     the smallest minimum is greater than the smallest maximum,
128      *  or the smallest maximum is greater than the largest maximum
129      *  or the largest minimum is greater than the largest maximum
130      */
of(long minSmallest, long minLargest, long maxSmallest, long maxLargest)131     public static ValueRange of(long minSmallest, long minLargest, long maxSmallest, long maxLargest) {
132         if (minSmallest > minLargest) {
133             throw new IllegalArgumentException("Smallest minimum value must be less than largest minimum value");
134         }
135         if (maxSmallest > maxLargest) {
136             throw new IllegalArgumentException("Smallest maximum value must be less than largest maximum value");
137         }
138         if (minLargest > maxLargest) {
139             throw new IllegalArgumentException("Minimum value must be less than maximum value");
140         }
141         return new ValueRange(minSmallest, minLargest, maxSmallest, maxLargest);
142     }
143 
144     /**
145      * Restrictive constructor.
146      *
147      * @param minSmallest  the smallest minimum value
148      * @param minLargest  the largest minimum value
149      * @param maxSmallest  the smallest minimum value
150      * @param maxLargest  the largest minimum value
151      */
ValueRange(long minSmallest, long minLargest, long maxSmallest, long maxLargest)152     private ValueRange(long minSmallest, long minLargest, long maxSmallest, long maxLargest) {
153         this.minSmallest = minSmallest;
154         this.minLargest = minLargest;
155         this.maxSmallest = maxSmallest;
156         this.maxLargest = maxLargest;
157     }
158 
159     //-----------------------------------------------------------------------
160     /**
161      * Is the value range fixed and fully known.
162      * <p>
163      * For example, the ISO day-of-month runs from 1 to between 28 and 31.
164      * Since there is uncertainty about the maximum value, the range is not fixed.
165      * However, for the month of January, the range is always 1 to 31, thus it is fixed.
166      *
167      * @return true if the set of values is fixed
168      */
isFixed()169     public boolean isFixed() {
170         return minSmallest == minLargest && maxSmallest == maxLargest;
171     }
172 
173     //-----------------------------------------------------------------------
174     /**
175      * Gets the minimum value that the field can take.
176      * <p>
177      * For example, the ISO day-of-month always starts at 1.
178      * The minimum is therefore 1.
179      *
180      * @return the minimum value for this field
181      */
getMinimum()182     public long getMinimum() {
183         return minSmallest;
184     }
185 
186     /**
187      * Gets the largest possible minimum value that the field can take.
188      * <p>
189      * For example, the ISO day-of-month always starts at 1.
190      * The largest minimum is therefore 1.
191      *
192      * @return the largest possible minimum value for this field
193      */
getLargestMinimum()194     public long getLargestMinimum() {
195         return minLargest;
196     }
197 
198     /**
199      * Gets the smallest possible maximum value that the field can take.
200      * <p>
201      * For example, the ISO day-of-month runs to between 28 and 31 days.
202      * The smallest maximum is therefore 28.
203      *
204      * @return the smallest possible maximum value for this field
205      */
getSmallestMaximum()206     public long getSmallestMaximum() {
207         return maxSmallest;
208     }
209 
210     /**
211      * Gets the maximum value that the field can take.
212      * <p>
213      * For example, the ISO day-of-month runs to between 28 and 31 days.
214      * The maximum is therefore 31.
215      *
216      * @return the maximum value for this field
217      */
getMaximum()218     public long getMaximum() {
219         return maxLargest;
220     }
221 
222     //-----------------------------------------------------------------------
223     /**
224      * Checks if all values in the range fit in an {@code int}.
225      * <p>
226      * This checks that all valid values are within the bounds of an {@code int}.
227      * <p>
228      * For example, the ISO month-of-year has values from 1 to 12, which fits in an {@code int}.
229      * By comparison, ISO nano-of-day runs from 1 to 86,400,000,000,000 which does not fit in an {@code int}.
230      * <p>
231      * This implementation uses {@link #getMinimum()} and {@link #getMaximum()}.
232      *
233      * @return true if a valid value always fits in an {@code int}
234      */
isIntValue()235     public boolean isIntValue() {
236         return getMinimum() >= Integer.MIN_VALUE && getMaximum() <= Integer.MAX_VALUE;
237     }
238 
239     /**
240      * Checks if the value is within the valid range.
241      * <p>
242      * This checks that the value is within the stored range of values.
243      *
244      * @param value  the value to check
245      * @return true if the value is valid
246      */
isValidValue(long value)247     public boolean isValidValue(long value) {
248         return (value >= getMinimum() && value <= getMaximum());
249     }
250 
251     /**
252      * Checks if the value is within the valid range and that all values
253      * in the range fit in an {@code int}.
254      * <p>
255      * This method combines {@link #isIntValue()} and {@link #isValidValue(long)}.
256      *
257      * @param value  the value to check
258      * @return true if the value is valid and fits in an {@code int}
259      */
isValidIntValue(long value)260     public boolean isValidIntValue(long value) {
261         return isIntValue() && isValidValue(value);
262     }
263 
264     /**
265      * Checks that the specified value is valid.
266      * <p>
267      * This validates that the value is within the valid range of values.
268      * The field is only used to improve the error message.
269      *
270      * @param value  the value to check
271      * @param field  the field being checked, may be null
272      * @return the value that was passed in
273      * @see #isValidValue(long)
274      */
checkValidValue(long value, TemporalField field)275     public long checkValidValue(long value, TemporalField field) {
276         if (isValidValue(value) == false) {
277             if (field != null) {
278                 throw new DateTimeException("Invalid value for " + field + " (valid values " + this + "): " + value);
279             } else {
280                 throw new DateTimeException("Invalid value (valid values " + this + "): " + value);
281             }
282         }
283         return value;
284     }
285 
286     /**
287      * Checks that the specified value is valid and fits in an {@code int}.
288      * <p>
289      * This validates that the value is within the valid range of values and that
290      * all valid values are within the bounds of an {@code int}.
291      * The field is only used to improve the error message.
292      *
293      * @param value  the value to check
294      * @param field  the field being checked, may be null
295      * @return the value that was passed in
296      * @see #isValidIntValue(long)
297      */
checkValidIntValue(long value, TemporalField field)298     public int checkValidIntValue(long value, TemporalField field) {
299         if (isValidIntValue(value) == false) {
300             throw new DateTimeException("Invalid int value for " + field + ": " + value);
301         }
302         return (int) value;
303     }
304 
305     //-----------------------------------------------------------------------
306     /**
307      * Checks if this range is equal to another range.
308      * <p>
309      * The comparison is based on the four values, minimum, largest minimum,
310      * smallest maximum and maximum.
311      * Only objects of type {@code ValueRange} are compared, other types return false.
312      *
313      * @param obj  the object to check, null returns false
314      * @return true if this is equal to the other range
315      */
316     @Override
equals(Object obj)317     public boolean equals(Object obj) {
318         if (obj == this) {
319             return true;
320         }
321         if (obj instanceof ValueRange) {
322             ValueRange other = (ValueRange) obj;
323            return minSmallest == other.minSmallest && minLargest == other.minLargest &&
324                    maxSmallest == other.maxSmallest && maxLargest == other.maxLargest;
325         }
326         return false;
327     }
328 
329     /**
330      * A hash code for this range.
331      *
332      * @return a suitable hash code
333      */
334     @Override
hashCode()335     public int hashCode() {
336         long hash = minSmallest + minLargest << 16 + minLargest >> 48 + maxSmallest << 32 +
337             maxSmallest >> 32 + maxLargest << 48 + maxLargest >> 16;
338         return (int) (hash ^ (hash >>> 32));
339     }
340 
341     //-----------------------------------------------------------------------
342     /**
343      * Outputs this range as a {@code String}.
344      * <p>
345      * The format will be '{min}/{largestMin} - {smallestMax}/{max}',
346      * where the largestMin or smallestMax sections may be omitted, together
347      * with associated slash, if they are the same as the min or max.
348      *
349      * @return a string representation of this range, not null
350      */
351     @Override
toString()352     public String toString() {
353         StringBuilder buf = new StringBuilder();
354         buf.append(minSmallest);
355         if (minSmallest != minLargest) {
356             buf.append('/').append(minLargest);
357         }
358         buf.append(" - ").append(maxSmallest);
359         if (maxSmallest != maxLargest) {
360             buf.append('/').append(maxLargest);
361         }
362         return buf.toString();
363     }
364 
365 }
366