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