1 /* 2 * Copyright (c) 2012, 2020, 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.IOException; 65 import java.io.InvalidObjectException; 66 import java.io.ObjectInputStream; 67 import java.io.Serializable; 68 import java.time.DateTimeException; 69 70 /** 71 * The range of valid values for a date-time field. 72 * <p> 73 * All {@link TemporalField} instances have a valid range of values. 74 * For example, the ISO day-of-month runs from 1 to somewhere between 28 and 31. 75 * This class captures that valid range. 76 * <p> 77 * It is important to be aware of the limitations of this class. 78 * Only the minimum and maximum values are provided. 79 * It is possible for there to be invalid values within the outer range. 80 * For example, a weird field may have valid values of 1, 2, 4, 6, 7, thus 81 * have a range of '1 - 7', despite that fact that values 3 and 5 are invalid. 82 * <p> 83 * Instances of this class are not tied to a specific field. 84 * 85 * @implSpec 86 * This class is immutable and thread-safe. 87 * 88 * @since 1.8 89 */ 90 public final class ValueRange implements Serializable { 91 92 /** 93 * Serialization version. 94 */ 95 @java.io.Serial 96 private static final long serialVersionUID = -7317881728594519368L; 97 98 /** 99 * The smallest minimum value. 100 */ 101 private final long minSmallest; 102 /** 103 * The largest minimum value. 104 */ 105 private final long minLargest; 106 /** 107 * The smallest maximum value. 108 */ 109 private final long maxSmallest; 110 /** 111 * The largest maximum value. 112 */ 113 private final long maxLargest; 114 115 /** 116 * Obtains a fixed value range. 117 * <p> 118 * This factory obtains a range where the minimum and maximum values are fixed. 119 * For example, the ISO month-of-year always runs from 1 to 12. 120 * 121 * @param min the minimum value 122 * @param max the maximum value 123 * @return the ValueRange for min, max, not null 124 * @throws IllegalArgumentException if the minimum is greater than the maximum 125 */ of(long min, long max)126 public static ValueRange of(long min, long max) { 127 if (min > max) { 128 throw new IllegalArgumentException("Minimum value must be less than maximum value"); 129 } 130 return new ValueRange(min, min, max, max); 131 } 132 133 /** 134 * Obtains a variable value range. 135 * <p> 136 * This factory obtains a range where the minimum value is fixed and the maximum value may vary. 137 * For example, the ISO day-of-month always starts at 1, but ends between 28 and 31. 138 * 139 * @param min the minimum value 140 * @param maxSmallest the smallest maximum value 141 * @param maxLargest the largest maximum value 142 * @return the ValueRange for min, smallest max, largest max, not null 143 * @throws IllegalArgumentException if 144 * the minimum is greater than the smallest maximum, 145 * or the smallest maximum is greater than the largest maximum 146 */ of(long min, long maxSmallest, long maxLargest)147 public static ValueRange of(long min, long maxSmallest, long maxLargest) { 148 if (min > maxSmallest) { 149 throw new IllegalArgumentException("Minimum value must be less than smallest maximum value"); 150 } 151 return of(min, min, maxSmallest, maxLargest); 152 } 153 154 /** 155 * Obtains a fully variable value range. 156 * <p> 157 * This factory obtains a range where both the minimum and maximum value may vary. 158 * 159 * @param minSmallest the smallest minimum value 160 * @param minLargest the largest minimum value 161 * @param maxSmallest the smallest maximum value 162 * @param maxLargest the largest maximum value 163 * @return the ValueRange for smallest min, largest min, smallest max, largest max, not null 164 * @throws IllegalArgumentException if 165 * the smallest minimum is greater than the smallest maximum, 166 * or the smallest maximum is greater than the largest maximum, 167 * or the largest minimum is greater than the largest maximum, 168 * or the smallest minimum is greater than the largest minimum 169 */ of(long minSmallest, long minLargest, long maxSmallest, long maxLargest)170 public static ValueRange of(long minSmallest, long minLargest, long maxSmallest, long maxLargest) { 171 if (minSmallest > minLargest) { 172 throw new IllegalArgumentException("Smallest minimum value must be less than largest minimum value"); 173 } 174 if (maxSmallest > maxLargest) { 175 throw new IllegalArgumentException("Smallest maximum value must be less than largest maximum value"); 176 } 177 if (minLargest > maxLargest) { 178 throw new IllegalArgumentException("Largest minimum value must be less than largest maximum value"); 179 } 180 if (minSmallest > maxSmallest) { 181 throw new IllegalArgumentException("Smallest minimum value must be less than smallest maximum value"); 182 } 183 return new ValueRange(minSmallest, minLargest, maxSmallest, maxLargest); 184 } 185 186 /** 187 * Restrictive constructor. 188 * 189 * @param minSmallest the smallest minimum value 190 * @param minLargest the largest minimum value 191 * @param maxSmallest the smallest minimum value 192 * @param maxLargest the largest minimum value 193 */ ValueRange(long minSmallest, long minLargest, long maxSmallest, long maxLargest)194 private ValueRange(long minSmallest, long minLargest, long maxSmallest, long maxLargest) { 195 this.minSmallest = minSmallest; 196 this.minLargest = minLargest; 197 this.maxSmallest = maxSmallest; 198 this.maxLargest = maxLargest; 199 } 200 201 //----------------------------------------------------------------------- 202 /** 203 * Is the value range fixed and fully known. 204 * <p> 205 * For example, the ISO day-of-month runs from 1 to between 28 and 31. 206 * Since there is uncertainty about the maximum value, the range is not fixed. 207 * However, for the month of January, the range is always 1 to 31, thus it is fixed. 208 * 209 * @return true if the set of values is fixed 210 */ isFixed()211 public boolean isFixed() { 212 return minSmallest == minLargest && maxSmallest == maxLargest; 213 } 214 215 //----------------------------------------------------------------------- 216 /** 217 * Gets the minimum value that the field can take. 218 * <p> 219 * For example, the ISO day-of-month always starts at 1. 220 * The minimum is therefore 1. 221 * 222 * @return the minimum value for this field 223 */ getMinimum()224 public long getMinimum() { 225 return minSmallest; 226 } 227 228 /** 229 * Gets the largest possible minimum value that the field can take. 230 * <p> 231 * For example, the ISO day-of-month always starts at 1. 232 * The largest minimum is therefore 1. 233 * 234 * @return the largest possible minimum value for this field 235 */ getLargestMinimum()236 public long getLargestMinimum() { 237 return minLargest; 238 } 239 240 /** 241 * Gets the smallest possible maximum value that the field can take. 242 * <p> 243 * For example, the ISO day-of-month runs to between 28 and 31 days. 244 * The smallest maximum is therefore 28. 245 * 246 * @return the smallest possible maximum value for this field 247 */ getSmallestMaximum()248 public long getSmallestMaximum() { 249 return maxSmallest; 250 } 251 252 /** 253 * Gets the maximum value that the field can take. 254 * <p> 255 * For example, the ISO day-of-month runs to between 28 and 31 days. 256 * The maximum is therefore 31. 257 * 258 * @return the maximum value for this field 259 */ getMaximum()260 public long getMaximum() { 261 return maxLargest; 262 } 263 264 //----------------------------------------------------------------------- 265 /** 266 * Checks if all values in the range fit in an {@code int}. 267 * <p> 268 * This checks that all valid values are within the bounds of an {@code int}. 269 * <p> 270 * For example, the ISO month-of-year has values from 1 to 12, which fits in an {@code int}. 271 * By comparison, ISO nano-of-day runs from 1 to 86,400,000,000,000 which does not fit in an {@code int}. 272 * <p> 273 * This implementation uses {@link #getMinimum()} and {@link #getMaximum()}. 274 * 275 * @return true if a valid value always fits in an {@code int} 276 */ isIntValue()277 public boolean isIntValue() { 278 return getMinimum() >= Integer.MIN_VALUE && getMaximum() <= Integer.MAX_VALUE; 279 } 280 281 /** 282 * Checks if the value is within the valid range. 283 * <p> 284 * This checks that the value is within the stored range of values. 285 * 286 * @param value the value to check 287 * @return true if the value is valid 288 */ isValidValue(long value)289 public boolean isValidValue(long value) { 290 return (value >= getMinimum() && value <= getMaximum()); 291 } 292 293 /** 294 * Checks if the value is within the valid range and that all values 295 * in the range fit in an {@code int}. 296 * <p> 297 * This method combines {@link #isIntValue()} and {@link #isValidValue(long)}. 298 * 299 * @param value the value to check 300 * @return true if the value is valid and fits in an {@code int} 301 */ isValidIntValue(long value)302 public boolean isValidIntValue(long value) { 303 return isIntValue() && isValidValue(value); 304 } 305 306 /** 307 * Checks that the specified value is valid. 308 * <p> 309 * This validates that the value is within the valid range of values. 310 * The field is only used to improve the error message. 311 * 312 * @param value the value to check 313 * @param field the field being checked, may be null 314 * @return the value that was passed in 315 * @see #isValidValue(long) 316 */ checkValidValue(long value, TemporalField field)317 public long checkValidValue(long value, TemporalField field) { 318 if (isValidValue(value) == false) { 319 throw new DateTimeException(genInvalidFieldMessage(field, value)); 320 } 321 return value; 322 } 323 324 /** 325 * Checks that the specified value is valid and fits in an {@code int}. 326 * <p> 327 * This validates that the value is within the valid range of values and that 328 * all valid values are within the bounds of an {@code int}. 329 * The field is only used to improve the error message. 330 * 331 * @param value the value to check 332 * @param field the field being checked, may be null 333 * @return the value that was passed in 334 * @see #isValidIntValue(long) 335 */ checkValidIntValue(long value, TemporalField field)336 public int checkValidIntValue(long value, TemporalField field) { 337 if (isValidIntValue(value) == false) { 338 throw new DateTimeException(genInvalidFieldMessage(field, value)); 339 } 340 return (int) value; 341 } 342 genInvalidFieldMessage(TemporalField field, long value)343 private String genInvalidFieldMessage(TemporalField field, long value) { 344 if (field != null) { 345 return "Invalid value for " + field + " (valid values " + this + "): " + value; 346 } else { 347 return "Invalid value (valid values " + this + "): " + value; 348 } 349 } 350 351 //----------------------------------------------------------------------- 352 /** 353 * Restore the state of an ValueRange from the stream. 354 * Check that the values are valid. 355 * 356 * @param s the stream to read 357 * @throws IOException if an I/O error occurs 358 * @throws InvalidObjectException if 359 * the smallest minimum is greater than the smallest maximum, 360 * or the smallest maximum is greater than the largest maximum 361 * or the largest minimum is greater than the largest maximum 362 * @throws ClassNotFoundException if a class cannot be resolved 363 */ 364 @java.io.Serial readObject(ObjectInputStream s)365 private void readObject(ObjectInputStream s) 366 throws IOException, ClassNotFoundException, InvalidObjectException 367 { 368 s.defaultReadObject(); 369 if (minSmallest > minLargest) { 370 throw new InvalidObjectException("Smallest minimum value must be less than largest minimum value"); 371 } 372 if (maxSmallest > maxLargest) { 373 throw new InvalidObjectException("Smallest maximum value must be less than largest maximum value"); 374 } 375 if (minLargest > maxLargest) { 376 throw new InvalidObjectException("Minimum value must be less than maximum value"); 377 } 378 } 379 380 //----------------------------------------------------------------------- 381 /** 382 * Checks if this range is equal to another range. 383 * <p> 384 * The comparison is based on the four values, minimum, largest minimum, 385 * smallest maximum and maximum. 386 * Only objects of type {@code ValueRange} are compared, other types return false. 387 * 388 * @param obj the object to check, null returns false 389 * @return true if this is equal to the other range 390 */ 391 @Override equals(Object obj)392 public boolean equals(Object obj) { 393 if (obj == this) { 394 return true; 395 } 396 return (obj instanceof ValueRange other) 397 && minSmallest == other.minSmallest 398 && minLargest == other.minLargest 399 && maxSmallest == other.maxSmallest 400 && maxLargest == other.maxLargest; 401 } 402 403 /** 404 * A hash code for this range. 405 * 406 * @return a suitable hash code 407 */ 408 @Override hashCode()409 public int hashCode() { 410 long hash = minSmallest + (minLargest << 16) + (minLargest >> 48) + 411 (maxSmallest << 32) + (maxSmallest >> 32) + (maxLargest << 48) + 412 (maxLargest >> 16); 413 return (int) (hash ^ (hash >>> 32)); 414 } 415 416 //----------------------------------------------------------------------- 417 /** 418 * Outputs this range as a {@code String}. 419 * <p> 420 * The format will be '{min}/{largestMin} - {smallestMax}/{max}', 421 * where the largestMin or smallestMax sections may be omitted, together 422 * with associated slash, if they are the same as the min or max. 423 * 424 * @return a string representation of this range, not null 425 */ 426 @Override toString()427 public String toString() { 428 StringBuilder buf = new StringBuilder(); 429 buf.append(minSmallest); 430 if (minSmallest != minLargest) { 431 buf.append('/').append(minLargest); 432 } 433 buf.append(" - ").append(maxSmallest); 434 if (maxSmallest != maxLargest) { 435 buf.append('/').append(maxLargest); 436 } 437 return buf.toString(); 438 } 439 440 } 441