1 | package org.expeditee.core;
|
---|
2 |
|
---|
3 | /**
|
---|
4 | * Represents a range of ordered values defined by a start and end value.
|
---|
5 | * Range can be open or closed at each end (i.e. the start and end points
|
---|
6 | * can be considered inside or outside the range). Either/both ends can
|
---|
7 | * also be null representing no lower/upper bound. If the upper bound is
|
---|
8 | * lower than the lower bound, the range is an outside range (where points
|
---|
9 | * outside the two bounds are considered contained by the range).
|
---|
10 | *
|
---|
11 | * @author cts16
|
---|
12 | *
|
---|
13 | * @param <T> The type of values in this range. Must have an order and
|
---|
14 | * therefore must implement Comparable.
|
---|
15 | */
|
---|
16 | public class Range<T extends Comparable<T>> {
|
---|
17 |
|
---|
18 | /** The lowest value in the range. If null, then the range has no lower bound. */
|
---|
19 | public T lowerBound;
|
---|
20 | /** The highest value in the range. If null, then the range has no upper bound. */
|
---|
21 | public T upperBound;
|
---|
22 | /** Whether the lowest value is open or closed (outside or inside the range respectively). */
|
---|
23 | public boolean lowerBoundClosed;
|
---|
24 | /** Whether the end value is open or closed. */
|
---|
25 | public boolean upperBoundClosed;
|
---|
26 |
|
---|
27 | /** Standard constructor considers the lower bound closed and upper bound open. */
|
---|
28 | public Range(T lowerBound, T upperBound)
|
---|
29 | {
|
---|
30 | this(lowerBound, upperBound, true, false);
|
---|
31 | }
|
---|
32 |
|
---|
33 | /** Standard constructor. */
|
---|
34 | public Range(T lowerBound, T upperBound, boolean lowerBoundClosed, boolean upperBoundClosed)
|
---|
35 | {
|
---|
36 | this.lowerBound = lowerBound;
|
---|
37 | this.upperBound = upperBound;
|
---|
38 | this.lowerBoundClosed = lowerBoundClosed;
|
---|
39 | this.upperBoundClosed = upperBoundClosed;
|
---|
40 | }
|
---|
41 |
|
---|
42 | /** Whether this range contains the given value. */
|
---|
43 | public boolean contains(T t)
|
---|
44 | {
|
---|
45 | // A null value is never inside the bound
|
---|
46 | if (t == null) return false;
|
---|
47 |
|
---|
48 | // See how the value compares to the two boundaries
|
---|
49 | int compStart = lowerBound != null ? t.compareTo(lowerBound) : 0;
|
---|
50 | int compEnd = upperBound != null ? t.compareTo(upperBound) : 0;
|
---|
51 |
|
---|
52 | // See if this is an outside range
|
---|
53 | boolean outsideRange = lowerBound != null && upperBound != null && upperBound.compareTo(lowerBound) < 0;
|
---|
54 |
|
---|
55 | // Make sure it's above the lower bound
|
---|
56 | boolean aboveLowerBound = (lowerBound == null) || // No lower bound, or...
|
---|
57 | (compStart > 0) || // Strictly above the lower bound, or...
|
---|
58 | (compStart == 0 && lowerBoundClosed); // On the lower bound but it's closed
|
---|
59 |
|
---|
60 | // Make sure it's below the upper bound
|
---|
61 | boolean belowUpperBound = (upperBound == null) || // No upper bound, or...
|
---|
62 | (compEnd < 0) || // Strictly below the upper bound, or...
|
---|
63 | (compEnd == 0 && upperBoundClosed); // On the upper bound but it's closed
|
---|
64 |
|
---|
65 | // In an inside range if we are below the upper bound and above the lower bound,
|
---|
66 | // in an outside range if we are below the upper bound or above the lower bound
|
---|
67 | return (aboveLowerBound && belowUpperBound) ||
|
---|
68 | (outsideRange && (aboveLowerBound || belowUpperBound));
|
---|
69 | }
|
---|
70 |
|
---|
71 | /** Creates an inside range when the order of the two ends is unknown. Both ends are considered closed. */
|
---|
72 | public static <T extends Comparable<T>> Range<T> insideRangeFromUncertainEnds(T anEnd, T anotherEnd)
|
---|
73 | {
|
---|
74 | return insideRangeFromUncertainEnds(anEnd, anotherEnd, true, true);
|
---|
75 | }
|
---|
76 |
|
---|
77 | /** Creates an inside range when the order of the two ends is unknown. */
|
---|
78 | public static <T extends Comparable<T>> Range<T> insideRangeFromUncertainEnds(T anEnd, T anotherEnd, boolean anEndClosed, boolean anotherEndClosed)
|
---|
79 | {
|
---|
80 | // If both ends are null then ordering doesn't matter
|
---|
81 | if (anEnd == null && anotherEnd == null) return new Range<T>(anEnd, anotherEnd, anEndClosed, anotherEndClosed);
|
---|
82 |
|
---|
83 | // Can't determine order of a single end
|
---|
84 | if (anEnd == null || anotherEnd == null) return null;
|
---|
85 |
|
---|
86 | // Put the two ends in order (so anEnd is lower than anotherEnd),
|
---|
87 | // keeping the respective closed flags with their ends
|
---|
88 | if (anEnd.compareTo(anotherEnd) > 0) {
|
---|
89 | T temp = anEnd;
|
---|
90 | anEnd = anotherEnd;
|
---|
91 | anotherEnd = temp;
|
---|
92 | boolean bTemp = anEndClosed;
|
---|
93 | anEndClosed = anotherEndClosed;
|
---|
94 | anotherEndClosed = bTemp;
|
---|
95 | }
|
---|
96 |
|
---|
97 | // Create a range from the reorder ends
|
---|
98 | return new Range<T>(anEnd, anotherEnd, anEndClosed, anotherEndClosed);
|
---|
99 | }
|
---|
100 | }
|
---|