001/* 002 * Copyright (c) 2007-2013, Stephen Colebourne & Michael Nascimento Santos 003 * 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or without 007 * modification, are permitted provided that the following conditions are met: 008 * 009 * * Redistributions of source code must retain the above copyright notice, 010 * this list of conditions and the following disclaimer. 011 * 012 * * Redistributions in binary form must reproduce the above copyright notice, 013 * this list of conditions and the following disclaimer in the documentation 014 * and/or other materials provided with the distribution. 015 * 016 * * Neither the name of JSR-310 nor the names of its contributors 017 * may be used to endorse or promote products derived from this software 018 * without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 021 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 022 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 023 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 027 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 028 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 029 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 031 */ 032package org.threeten.bp.temporal; 033 034import java.io.Serializable; 035 036import org.threeten.bp.DateTimeException; 037 038/** 039 * The range of valid values for a date-time field. 040 * <p> 041 * All {@link TemporalField} instances have a valid range of values. 042 * For example, the ISO day-of-month runs from 1 to somewhere between 28 and 31. 043 * This class captures that valid range. 044 * <p> 045 * It is important to be aware of the limitations of this class. 046 * Only the minimum and maximum values are provided. 047 * It is possible for there to be invalid values within the outer range. 048 * For example, a weird field may have valid values of 1, 2, 4, 6, 7, thus 049 * have a range of '1 - 7', despite that fact that values 3 and 5 are invalid. 050 * <p> 051 * Instances of this class are not tied to a specific field. 052 * 053 * <h3>Specification for implementors</h3> 054 * This class is immutable and thread-safe. 055 */ 056public final class ValueRange implements Serializable { 057 058 /** 059 * Serialization version. 060 */ 061 private static final long serialVersionUID = -7317881728594519368L; 062 063 /** 064 * The smallest minimum value. 065 */ 066 private final long minSmallest; 067 /** 068 * The largest minimum value. 069 */ 070 private final long minLargest; 071 /** 072 * The smallest maximum value. 073 */ 074 private final long maxSmallest; 075 /** 076 * The largest maximum value. 077 */ 078 private final long maxLargest; 079 080 /** 081 * Obtains a fixed value range. 082 * <p> 083 * This factory obtains a range where the minimum and maximum values are fixed. 084 * For example, the ISO month-of-year always runs from 1 to 12. 085 * 086 * @param min the minimum value 087 * @param max the maximum value 088 * @return the ValueRange for min, max, not null 089 * @throws IllegalArgumentException if the minimum is greater than the maximum 090 */ 091 public static ValueRange of(long min, long max) { 092 if (min > max) { 093 throw new IllegalArgumentException("Minimum value must be less than maximum value"); 094 } 095 return new ValueRange(min, min, max, max); 096 } 097 098 /** 099 * 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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.getName() + " (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 */ 298 public int checkValidIntValue(long value, TemporalField field) { 299 if (isValidIntValue(value) == false) { 300 throw new DateTimeException("Invalid int value for " + field.getName() + ": " + 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 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 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 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}