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