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