001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2003, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.util;
018:
019: // JAI dependencies
020: import javax.media.jai.util.Range;
021:
022: // Geotools dependencies
023: import org.geotools.resources.ClassChanger;
024: import org.geotools.resources.XMath;
025:
026: /**
027: * A range of numbers. {@linkplain #union Union} and {@linkplain #intersect intersection}
028: * are computed as usual, except that widening conversions will be applied as needed.
029: *
030: * @since 2.0
031: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/util/NumberRange.java $
032: * @version $Id: NumberRange.java 26601 2007-08-19 23:16:35Z desruisseaux $
033: * @author Martin Desruisseaux
034: *
035: * @todo Apply covariant return type on {@link #getMinValue} and {@link #getMaxValue}
036: * when we will be allowed to compile for J2SE 1.5.
037: */
038: public class NumberRange extends Range {
039: /**
040: * Serial number for interoperability with different versions.
041: */
042: private static final long serialVersionUID = -818167965963008231L;
043:
044: /**
045: * Constructs an inclusive range of {@code byte} values.
046: *
047: * @param minimum The minimum value, inclusive.
048: * @param maximum The maximum value, <strong>inclusive</strong>.
049: */
050: public NumberRange(final byte minimum, final byte maximum) {
051: this (minimum, true, maximum, true);
052: }
053:
054: /**
055: * Constructs a range of {@code byte} values.
056: *
057: * @param minimum The minimum value.
058: * @param isMinIncluded Defines whether the minimum value is included in the Range.
059: * @param maximum The maximum value.
060: * @param isMaxIncluded Defines whether the maximum value is included in the Range.
061: */
062: public NumberRange(final byte minimum, final boolean isMinIncluded,
063: final byte maximum, final boolean isMaxIncluded) {
064: super (Byte.class, new Byte(minimum), isMinIncluded, new Byte(
065: maximum), isMaxIncluded);
066: }
067:
068: /**
069: * Constructs an inclusive range of {@code short} values.
070: *
071: * @param minimum The minimum value, inclusive.
072: * @param maximum The maximum value, <strong>inclusive</strong>.
073: */
074: public NumberRange(final short minimum, final short maximum) {
075: this (minimum, true, maximum, true);
076: }
077:
078: /**
079: * Constructs a range of {@code short} values.
080: *
081: * @param minimum The minimum value.
082: * @param isMinIncluded Defines whether the minimum value is included in the Range.
083: * @param maximum The maximum value.
084: * @param isMaxIncluded Defines whether the maximum value is included in the Range.
085: */
086: public NumberRange(final short minimum,
087: final boolean isMinIncluded, final short maximum,
088: final boolean isMaxIncluded) {
089: super (Short.class, new Short(minimum), isMinIncluded,
090: new Short(maximum), isMaxIncluded);
091: }
092:
093: /**
094: * Constructs an inclusive range of {@code int} values.
095: *
096: * @param minimum The minimum value, inclusive.
097: * @param maximum The maximum value, <strong>inclusive</strong>.
098: */
099: public NumberRange(final int minimum, final int maximum) {
100: this (minimum, true, maximum, true);
101: }
102:
103: /**
104: * Constructs a range of {@code int} values.
105: *
106: * @param minimum The minimum value.
107: * @param isMinIncluded Defines whether the minimum value is included in the Range.
108: * @param maximum The maximum value.
109: * @param isMaxIncluded Defines whether the maximum value is included in the Range.
110: */
111: public NumberRange(final int minimum, final boolean isMinIncluded,
112: final int maximum, final boolean isMaxIncluded) {
113: super (Integer.class, new Integer(minimum), isMinIncluded,
114: new Integer(maximum), isMaxIncluded);
115: }
116:
117: /**
118: * Constructs an inclusive range of {@code long} values.
119: *
120: * @param minimum The minimum value, inclusive.
121: * @param maximum The maximum value, <strong>inclusive</strong>.
122: */
123: public NumberRange(final long minimum, final long maximum) {
124: this (minimum, true, maximum, true);
125: }
126:
127: /**
128: * Constructs a range of {@code long} values.
129: *
130: * @param minimum The minimum value.
131: * @param isMinIncluded Defines whether the minimum value is included in the Range.
132: * @param maximum The maximum value.
133: * @param isMaxIncluded Defines whether the maximum value is included in the Range.
134: */
135: public NumberRange(final long minimum, final boolean isMinIncluded,
136: final long maximum, final boolean isMaxIncluded) {
137: super (Long.class, new Long(minimum), isMinIncluded, new Long(
138: maximum), isMaxIncluded);
139: }
140:
141: /**
142: * Constructs an inclusive range of {@code float} values.
143: *
144: * @param minimum The minimum value, inclusive.
145: * @param maximum The maximum value, <strong>inclusive</strong>.
146: */
147: public NumberRange(final float minimum, final float maximum) {
148: this (minimum, true, maximum, true);
149: }
150:
151: /**
152: * Constructs a range of {@code float} values.
153: *
154: * @param minimum The minimum value.
155: * @param isMinIncluded Defines whether the minimum value is included in the Range.
156: * @param maximum The maximum value.
157: * @param isMaxIncluded Defines whether the maximum value is included in the Range.
158: */
159: public NumberRange(final float minimum,
160: final boolean isMinIncluded, final float maximum,
161: final boolean isMaxIncluded) {
162: super (Float.class, new Float(minimum), isMinIncluded,
163: new Float(maximum), isMaxIncluded);
164: }
165:
166: /**
167: * Constructs an inclusive range of {@code double} values.
168: *
169: * @param minimum The minimum value, inclusive.
170: * @param maximum The maximum value, <strong>inclusive</strong>.
171: */
172: public NumberRange(final double minimum, final double maximum) {
173: this (minimum, true, maximum, true);
174: }
175:
176: /**
177: * Constructs a range of {@code double} values.
178: *
179: * @param minimum The minimum value.
180: * @param isMinIncluded Defines whether the minimum value is included in the Range.
181: * @param maximum The maximum value.
182: * @param isMaxIncluded Defines whether the maximum value is included in the Range.
183: */
184: public NumberRange(final double minimum,
185: final boolean isMinIncluded, final double maximum,
186: final boolean isMaxIncluded) {
187: super (Double.class, new Double(minimum), isMinIncluded,
188: new Double(maximum), isMaxIncluded);
189: }
190:
191: /**
192: * Constructs an inclusive range of {@link Comparable} objects.
193: * This constructor is used by {@link RangeSet#newRange} only.
194: *
195: * @param type The element class, usually one of {@link Byte}, {@link Short},
196: * {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
197: * @param minimum The minimum value, inclusive.
198: * @param maximum The maximum value, <strong>inclusive</strong>.
199: */
200: NumberRange(final Class type, final Comparable minimum,
201: final Comparable maximum) {
202: super (type, minimum, maximum);
203: }
204:
205: /**
206: * Constructs an inclusive range of {@link Number} objects.
207: *
208: * @param type The element class, usually one of {@link Byte}, {@link Short},
209: * {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
210: * @param minimum The minimum value, inclusive.
211: * @param maximum The maximum value, <strong>inclusive</strong>.
212: */
213: public NumberRange(final Class type, final Number minimum,
214: final Number maximum) {
215: super (type, (Comparable) minimum, (Comparable) maximum);
216: }
217:
218: /**
219: * Constructs a range of {@link Number} objects.
220: *
221: * @param type The element class, usually one of {@link Byte}, {@link Short},
222: * {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
223: * @param minimum The minimum value.
224: * @param isMinIncluded Defines whether the minimum value is included in the Range.
225: * @param maximum The maximum value.
226: * @param isMaxIncluded Defines whether the maximum value is included in the Range.
227: */
228: public NumberRange(final Class type, final Number minimum,
229: final boolean isMinIncluded, final Number maximum,
230: final boolean isMaxIncluded) {
231: super (type, (Comparable) minimum, isMinIncluded,
232: (Comparable) maximum, isMaxIncluded);
233: }
234:
235: /**
236: * Constructs a range with the same values than the specified range,
237: * casted to the specified type.
238: *
239: * @param type The element class, usually one of {@link Byte}, {@link Short},
240: * {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
241: * @param range The range to copy. The elements must be {@link Number} instances.
242: * @throws ClassCastException if some elements are not instances of {@link Number}.
243: */
244: NumberRange(final Class type, final Range range)
245: throws ClassCastException {
246: this (type, ClassChanger
247: .cast((Number) range.getMinValue(), type), range
248: .isMinIncluded(), ClassChanger.cast((Number) range
249: .getMaxValue(), type), range.isMaxIncluded());
250: }
251:
252: /**
253: * Constructs a range with the same type and the same values than the specified range.
254: * This is a copy constructor.
255: *
256: * @param range The range to copy. The elements must be {@link Number} instances.
257: * @throws ClassCastException if some elements are not instances of {@link Number}.
258: *
259: * @since 2.4
260: */
261: public NumberRange(final Range range) throws ClassCastException {
262: this (range.getElementClass(), (Number) range.getMinValue(),
263: range.isMinIncluded(), (Number) range.getMaxValue(),
264: range.isMaxIncluded());
265: }
266:
267: /**
268: * Wraps the specified {@link Range} in a {@code NumberRange} object. If the specified
269: * range is already an instance of {@code NumberRange}, then it is returned unchanged.
270: *
271: * @param range The range to wrap
272: * @return The same range than {@code range} as a {@code NumberRange} object.
273: */
274: public static NumberRange wrap(final Range range) {
275: if (range instanceof NumberRange) {
276: return (NumberRange) range;
277: }
278: return new NumberRange(range);
279: }
280:
281: /**
282: * Casts the specified range to the specified type. If this class is associated to a unit of
283: * measurement, then this method convert the {@code range} units to the same units than this
284: * instance. This method is overriden by {@link MeasurementRange} only in the way described
285: * above.
286: *
287: * @param type The class to cast to. Must be one of {@link Byte}, {@link Short},
288: * {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
289: * @return The casted range, or {@code range} if no cast is needed.
290: */
291: NumberRange convertAndCast(final Range range, final Class type) {
292: if (type.equals(range.getElementClass())) {
293: return wrap(range);
294: }
295: return new NumberRange(type, range);
296: }
297:
298: /**
299: * Casts this range to the specified type.
300: *
301: * @param type The class to cast to. Must be one of {@link Byte}, {@link Short},
302: * {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
303: * @return The casted range, or {@code this} if this range already uses
304: * the specified type.
305: */
306: public NumberRange castTo(final Class type) {
307: return convertAndCast(this , type);
308: }
309:
310: /**
311: * Returns {@code true} if the specified value is within this range.
312: */
313: //@Override
314: public boolean contains(final Comparable value) {
315: return contains((Number) value);
316: }
317:
318: /**
319: * Returns {@code true} if the specified value is within this range.
320: */
321: public boolean contains(final Number value) {
322: final Class type = ClassChanger.getWidestClass(
323: getElementClass(), value.getClass());
324: return castTo(type)._contains(ClassChanger.cast(value, type));
325: }
326:
327: /**
328: * Performs the contains test (no type check).
329: */
330: private boolean _contains(final Number value) {
331: return super .contains((Comparable) value);
332: }
333:
334: /**
335: * Returns true if the supplied range is fully contained within this range.
336: */
337: //@Override
338: public boolean contains(final Range range) {
339: final Class type = ClassChanger.getWidestClass(
340: getElementClass(), range.getElementClass());
341: return castTo(type)._contains(convertAndCast(range, type));
342: }
343:
344: /**
345: * Performs the test (no type check).
346: */
347: private boolean _contains(final Range range) {
348: return super .contains(range);
349: }
350:
351: /**
352: * Returns true if this range intersects the given range.
353: */
354: //@Override
355: public boolean intersects(final Range range) {
356: final Class type = ClassChanger.getWidestClass(
357: getElementClass(), range.getElementClass());
358: return castTo(type)._intersects(convertAndCast(range, type));
359: }
360:
361: /**
362: * Performs the test (no type check).
363: */
364: private boolean _intersects(final Range range) {
365: return super .intersects(range);
366: }
367:
368: /**
369: * Returns the union of this range with the given range.
370: * Widening conversions will be applied as needed.
371: *
372: * @todo The return type will be changed to {@code NumberRange} when J2SE 1.5
373: * will be available. We should then search for NumberRange.warp(...) in all
374: * client classes; some 'warp' may no longer be needed.
375: */
376: //@Override
377: public Range union(final Range range) {
378: final Class type = ClassChanger.getWidestClass(
379: getElementClass(), range.getElementClass());
380: return wrap(castTo(type)._union(convertAndCast(range, type)));
381: }
382:
383: /**
384: * Performs the union (no type check).
385: */
386: private Range _union(final Range range) {
387: return super .union(range);
388: }
389:
390: /**
391: * Returns the intersection of this range with the given range.
392: * Widening conversions will be applied as needed.
393: *
394: * @todo The return type will be changed to {@code NumberRange} when J2SE 1.5
395: * will be available. We should then search for NumberRange.warp(...) in all
396: * client classes; some 'warp' may no longer be needed.
397: */
398: //@Override
399: public Range intersect(final Range range) {
400: Class type = ClassChanger.getWidestClass(getElementClass(),
401: range.getElementClass());
402: final Range result = castTo(type)._intersect(
403: convertAndCast(range, type));
404: /*
405: * Use a finer type capable to holds the result (since the intersection may have
406: * reduced the range), but not finer than the finest type of the ranges used in
407: * the intersection calculation.
408: */
409: type = ClassChanger.getFinestClass(getElementClass(), range
410: .getElementClass());
411: return convertAndCast(result, ClassChanger.getWidestClass(type,
412: ClassChanger.getWidestClass(ClassChanger
413: .getFinestClass(((Number) result.getMinValue())
414: .doubleValue()), ClassChanger
415: .getFinestClass(((Number) result.getMaxValue())
416: .doubleValue()))));
417: }
418:
419: /**
420: * Performs the intersection (no type check).
421: */
422: private Range _intersect(final Range range) {
423: return super .intersect(range);
424: }
425:
426: /**
427: * Returns the range of values that are in this range but not in the given range.
428: *
429: * @todo Consider changing the return type to {@code NumberRange} when we will be allowed
430: * to compile for J2SE 1.5.
431: */
432: //@Override
433: public Range[] subtract(final Range range) {
434: Class type = ClassChanger.getWidestClass(getElementClass(),
435: range.getElementClass());
436: final Range[] result = castTo(type)._subtract(
437: convertAndCast(range, type));
438: if (result != null) {
439: for (int i = 0; i < result.length; i++) {
440: result[i] = wrap(result[i]);
441: }
442: }
443: return result;
444: }
445:
446: /**
447: * Performs the substraction (no type check).
448: */
449: private Range[] _subtract(final Range range) {
450: return super .subtract(range);
451: }
452:
453: /**
454: * Returns the {@linkplain #getMinValue minimum value} as a {@code double}.
455: * If this range is unbounded, then {@link Double#NEGATIVE_INFINITY} is returned.
456: */
457: public double getMinimum() {
458: final Number value = (Number) getMinValue();
459: return (value != null) ? value.doubleValue()
460: : Double.NEGATIVE_INFINITY;
461: }
462:
463: /**
464: * Returns the {@linkplain #getMinimum() minimum value} with the specified inclusive or
465: * exclusive state. If this range is unbounded, then {@link Double#NEGATIVE_INFINITY} is
466: * returned.
467: *
468: * @param inclusive {@code true} for the minimum value inclusive,
469: * or {@code false} for the minimum value exclusive.
470: * @return The minimum value, inclusive or exclusive as requested.
471: */
472: public double getMinimum(final boolean inclusive) {
473: double value = getMinimum();
474: if (inclusive != isMinIncluded()) {
475: value = XMath.rool(getElementClass(), value, inclusive ? +1
476: : -1);
477: }
478: return value;
479: }
480:
481: /**
482: * Returns the {@linkplain #getMaxValue maximum value} as a {@code double}.
483: * If this range is unbounded, then {@link Double#POSITIVE_INFINITY} is returned.
484: */
485: public double getMaximum() {
486: final Number value = (Number) getMaxValue();
487: return (value != null) ? value.doubleValue()
488: : Double.POSITIVE_INFINITY;
489: }
490:
491: /**
492: * Returns the {@linkplain #getMaximum() maximum value} with the specified inclusive or
493: * exclusive state. If this range is unbounded, then {@link Double#POSITIVE_INFINITY} is
494: * returned.
495: *
496: * @param inclusive {@code true} for the maximum value inclusive,
497: * or {@code false} for the maximum value exclusive.
498: * @return The maximum value, inclusive or exclusive as requested.
499: */
500: public double getMaximum(final boolean inclusive) {
501: double value = getMaximum();
502: if (inclusive != isMaxIncluded()) {
503: value = XMath.rool(getElementClass(), value, inclusive ? -1
504: : +1);
505: }
506: return value;
507: }
508: }
|