1 /* 2 * Copyright (c) 2012, 2019, 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 */ 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 */ 147 public static ValueRange of(long min, long maxSmallest, long maxLargest) { 148 return of(min, min, maxSmallest, maxLargest); 149 } 150 151 /** 152 * Obtains a fully variable value range. 153 * <p> 154 * This factory obtains a range where both the minimum and maximum value may vary. 155 * 156 * @param minSmallest the smallest minimum value 157 * @param minLargest the largest minimum value 158 * @param maxSmallest the smallest maximum value 159 * @param maxLargest the largest maximum value 160 * @return the ValueRange for smallest min, largest min, smallest max, largest max, not null 161 * @throws IllegalArgumentException if 162 * the smallest minimum is greater than the smallest maximum, 163 * or the smallest maximum is greater than the largest maximum 164 * or the largest minimum is greater than the largest maximum 165 */ 166 public static ValueRange of(long minSmallest, long minLargest, long maxSmallest, long maxLargest) { 167 if (minSmallest > minLargest) { 168 throw new IllegalArgumentException("Smallest minimum value must be less than largest minimum value"); 169 } 170 if (maxSmallest > maxLargest) { 171 throw new IllegalArgumentException("Smallest maximum value must be less than largest maximum value"); 172 } 173 if (minLargest > maxLargest) { 174 throw new IllegalArgumentException("Minimum value must be less than maximum value"); 175 } 176 return new ValueRange(minSmallest, minLargest, maxSmallest, maxLargest); 177 } 178 179 /** 180 * Restrictive constructor. 181 * 182 * @param minSmallest the smallest minimum value 183 * @param minLargest the largest minimum value 184 * @param maxSmallest the smallest minimum value 185 * @param maxLargest the largest minimum value 186 */ 187 private ValueRange(long minSmallest, long minLargest, long maxSmallest, long maxLargest) { 188 this.minSmallest = minSmallest; 189 this.minLargest = minLargest; 190 this.maxSmallest = maxSmallest; 191 this.maxLargest = maxLargest; 192 } 193 194 //----------------------------------------------------------------------- 195 /** 196 * Is the value range fixed and fully known. 197 * <p> 198 * For example, the ISO day-of-month runs from 1 to between 28 and 31. 199 * Since there is uncertainty about the maximum value, the range is not fixed. 200 * However, for the month of January, the range is always 1 to 31, thus it is fixed. 201 * 202 * @return true if the set of values is fixed 203 */ 204 public boolean isFixed() { 205 return minSmallest == minLargest && maxSmallest == maxLargest; 206 } 207 208 //----------------------------------------------------------------------- 209 /** 210 * Gets the minimum value that the field can take. 211 * <p> 212 * For example, the ISO day-of-month always starts at 1. 213 * The minimum is therefore 1. 214 * 215 * @return the minimum value for this field 216 */ 217 public long getMinimum() { 218 return minSmallest; 219 } 220 221 /** 222 * Gets the largest possible minimum value that the field can take. 223 * <p> 224 * For example, the ISO day-of-month always starts at 1. 225 * The largest minimum is therefore 1. 226 * 227 * @return the largest possible minimum value for this field 228 */ 229 public long getLargestMinimum() { 230 return minLargest; 231 } 232 233 /** 234 * Gets the smallest possible maximum value that the field can take. 235 * <p> 236 * For example, the ISO day-of-month runs to between 28 and 31 days. 237 * The smallest maximum is therefore 28. 238 * 239 * @return the smallest possible maximum value for this field 240 */ 241 public long getSmallestMaximum() { 242 return maxSmallest; 243 } 244 245 /** 246 * Gets the maximum value that the field can take. 247 * <p> 248 * For example, the ISO day-of-month runs to between 28 and 31 days. 249 * The maximum is therefore 31. 250 * 251 * @return the maximum value for this field 252 */ 253 public long getMaximum() { 254 return maxLargest; 255 } 256 257 //----------------------------------------------------------------------- 258 /** 259 * Checks if all values in the range fit in an {@code int}. 260 * <p> 261 * This checks that all valid values are within the bounds of an {@code int}. 262 * <p> 263 * For example, the ISO month-of-year has values from 1 to 12, which fits in an {@code int}. 264 * By comparison, ISO nano-of-day runs from 1 to 86,400,000,000,000 which does not fit in an {@code int}. 265 * <p> 266 * This implementation uses {@link #getMinimum()} and {@link #getMaximum()}. 267 * 268 * @return true if a valid value always fits in an {@code int} 269 */ 270 public boolean isIntValue() { 271 return getMinimum() >= Integer.MIN_VALUE && getMaximum() <= Integer.MAX_VALUE; 272 } 273 274 /** 275 * Checks if the value is within the valid range. 276 * <p> 277 * This checks that the value is within the stored range of values. 278 * 279 * @param value the value to check 280 * @return true if the value is valid 281 */ 282 public boolean isValidValue(long value) { 283 return (value >= getMinimum() && value <= getMaximum()); 284 } 285 286 /** 287 * Checks if the value is within the valid range and that all values 288 * in the range fit in an {@code int}. 289 * <p> 290 * This method combines {@link #isIntValue()} and {@link #isValidValue(long)}. 291 * 292 * @param value the value to check 293 * @return true if the value is valid and fits in an {@code int} 294 */ 295 public boolean isValidIntValue(long value) { 296 return isIntValue() && isValidValue(value); 297 } 298 299 /** 300 * Checks that the specified value is valid. 301 * <p> 302 * This validates that the value is within the valid range of values. 303 * The field is only used to improve the error message. 304 * 305 * @param value the value to check 306 * @param field the field being checked, may be null 307 * @return the value that was passed in 308 * @see #isValidValue(long) 309 */ 310 public long checkValidValue(long value, TemporalField field) { 311 if (isValidValue(value) == false) { 312 throw new DateTimeException(genInvalidFieldMessage(field, value)); 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(genInvalidFieldMessage(field, value)); 332 } 333 return (int) value; 334 } 335 336 private String genInvalidFieldMessage(TemporalField field, long value) { 337 if (field != null) { 338 return "Invalid value for " + field + " (valid values " + this + "): " + value; 339 } else { 340 return "Invalid value (valid values " + this + "): " + value; 341 } 342 } 343 344 //----------------------------------------------------------------------- 345 /** 346 * Restore the state of an ValueRange from the stream. 347 * Check that the values are valid. 348 * 349 * @param s the stream to read 350 * @throws InvalidObjectException if 351 * the smallest minimum is greater than the smallest maximum, 352 * or the smallest maximum is greater than the largest maximum 353 * or the largest minimum is greater than the largest maximum 354 * @throws ClassNotFoundException if a class cannot be resolved 355 */ 356 @java.io.Serial 357 private void readObject(ObjectInputStream s) 358 throws IOException, ClassNotFoundException, InvalidObjectException 359 { 360 s.defaultReadObject(); 361 if (minSmallest > minLargest) { 362 throw new InvalidObjectException("Smallest minimum value must be less than largest minimum value"); 363 } 364 if (maxSmallest > maxLargest) { 365 throw new InvalidObjectException("Smallest maximum value must be less than largest maximum value"); 366 } 367 if (minLargest > maxLargest) { 368 throw new InvalidObjectException("Minimum value must be less than maximum value"); 369 } 370 } 371 372 //----------------------------------------------------------------------- 373 /** 374 * Checks if this range is equal to another range. 375 * <p> 376 * The comparison is based on the four values, minimum, largest minimum, 377 * smallest maximum and maximum. 378 * Only objects of type {@code ValueRange} are compared, other types return false. 379 * 380 * @param obj the object to check, null returns false 381 * @return true if this is equal to the other range 382 */ 383 @Override 384 public boolean equals(Object obj) { 385 if (obj == this) { 386 return true; 387 } 388 if (obj instanceof ValueRange) { 389 ValueRange other = (ValueRange) obj; 390 return minSmallest == other.minSmallest && minLargest == other.minLargest && 391 maxSmallest == other.maxSmallest && maxLargest == other.maxLargest; 392 } 393 return false; 394 } 395 396 /** 397 * A hash code for this range. 398 * 399 * @return a suitable hash code 400 */ 401 @Override 402 public int hashCode() { 403 long hash = minSmallest + (minLargest << 16) + (minLargest >> 48) + 404 (maxSmallest << 32) + (maxSmallest >> 32) + (maxLargest << 48) + 405 (maxLargest >> 16); 406 return (int) (hash ^ (hash >>> 32)); 407 } 408 409 //----------------------------------------------------------------------- 410 /** 411 * Outputs this range as a {@code String}. 412 * <p> 413 * The format will be '{min}/{largestMin} - {smallestMax}/{max}', 414 * where the largestMin or smallestMax sections may be omitted, together 415 * with associated slash, if they are the same as the min or max. 416 * 417 * @return a string representation of this range, not null 418 */ 419 @Override 420 public String toString() { 421 StringBuilder buf = new StringBuilder(); 422 buf.append(minSmallest); 423 if (minSmallest != minLargest) { 424 buf.append('/').append(minLargest); 425 } 426 buf.append(" - ").append(maxSmallest); 427 if (maxSmallest != maxLargest) { 428 buf.append('/').append(maxLargest); 429 } 430 return buf.toString(); 431 } 432 433 }