001: /*
002: * Primitive Collections for Java.
003: * Copyright (C) 2002 Søren Bak
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019: package bak.pcj.set;
020:
021: import bak.pcj.util.Exceptions;
022: import java.io.Serializable;
023:
024: /**
025: * This class represents ranges of consecutive byte values.
026: *
027: * <p>Design note: Empty ranges cannot exist. It gives too many
028: * problems defining adjacency and intersections, and empty ranges
029: * are not really useful for their purpose of backing up range sets.
030: * It also removes the problem of overflow checking.
031: *
032: * @author Søren Bak
033: * @version 1.3 21-08-2003 20:10
034: * @since 1.0
035: */
036: public class ByteRange implements Comparable, Serializable {
037:
038: /**
039: * The first value in this range.
040: * @serial
041: */
042: private byte first;
043:
044: /**
045: * The last value in this range.
046: * @serial
047: */
048: private byte last;
049:
050: /**
051: * Creates a new range of consecutive byte values.
052: *
053: * @param first
054: * the first byte value in the range.
055: *
056: * @param last
057: * the last byte value in the range.
058: *
059: * @throws IllegalArgumentException
060: * if <tt>first > last</tt>.
061: */
062: public ByteRange(byte first, byte last) {
063: if (last < first)
064: Exceptions.invalidRangeBounds(String.valueOf(first), String
065: .valueOf(last));
066: this .first = first;
067: this .last = last;
068: }
069:
070: /*
071: * Creates a new range of consecutive byte values.
072: *
073: * @param first
074: * the first byte value in the range.
075: *
076: * @param length
077: * the number of consecutive values in the new
078: * range.
079: *
080: * @throws IllegalArgumentException
081: * if <tt>length <= 0</tt>;
082: * if the specified length results in an overflow,
083: * i.e. there are values not in the defined range of
084: * byte values.
085: */
086: /*
087: public ByteRange(byte first, int length) {
088: if (length <= 0)
089: throw new IllegalArgumentException();
090: // first+length-1 > MAX_VALUE rewritten to avoid overflow
091: if (first-1 > Byte.MAX_VALUE-length)
092: throw new IllegalArgumentException();
093: this.first = first;
094: this.last = (byte)(first+length-1);
095: }
096: */
097:
098: /**
099: * Returns the first byte value in this range.
100: *
101: * @return the first byte value in this range.
102: */
103: public byte first() {
104: return first;
105: }
106:
107: /**
108: * Returns the last byte value in this range.
109: *
110: * @return the last byte value in this range.
111: */
112: public byte last() {
113: return last;
114: }
115:
116: /**
117: * Returns the number of values in this range.
118: *
119: * @return the number of values in this range.
120: */
121: public int length() {
122: return (int) (last - first + 1);
123: }
124:
125: /**
126: * Indicates whether this range intersects a specified range.
127: *
128: * @param range
129: * the range with which to compare this range.
130: *
131: * @return <tt>true</tt> if this range has at least one
132: * value in common with the specified range.
133: *
134: * @throws NullPointerException
135: * if <tt>range</tt> is <tt>null</tt>.
136: */
137: public boolean intersects(ByteRange range) {
138: return (first >= range.first && first <= range.last)
139: || (range.first >= first && range.first <= last);
140: }
141:
142: /**
143: * Indicates whether this range is adjacent to a specified
144: * range.
145: *
146: * @param range
147: * the range with which to compare this range.
148: *
149: * @return <tt>true</tt> if this range is adjacent to the
150: * specified range.
151: *
152: * @throws NullPointerException
153: * if <tt>range</tt> is <tt>null</tt>.
154: */
155: public boolean adjacentTo(ByteRange range) {
156: return (last + 1 == range.first) || (range.last + 1 == first);
157: }
158:
159: /**
160: * Indicates whether this can be merged with a specified range.
161: * Two ranges can be merged if a range of consecutive values
162: * containing all values of both ranges exists.
163: *
164: * @param range
165: * the range to merge with.
166: *
167: * @return <tt>true</tt> if this range can be merged with
168: * the specified range; returns <tt>false</tt>
169: * otherwise.
170: *
171: * @throws NullPointerException
172: * if <tt>range</tt> is <tt>null</tt>.
173: */
174: public boolean canMergeWith(ByteRange range) {
175: return intersects(range) || adjacentTo(range);
176: }
177:
178: /**
179: * Creates a new range as a merge between this range and a
180: * specified range.
181: *
182: * @param range
183: * the range with which to merge this range.
184: *
185: * @throws NullPointerException
186: * if <tt>range</tt> is <tt>null</tt>.
187: *
188: * @throws IllegalArgumentException
189: * if this range cannot be merged with the specified
190: * range.
191: */
192: public ByteRange mergeWith(ByteRange range) {
193: if (!canMergeWith(range))
194: Exceptions.cannotMergeRanges(this , range);
195: return quickMergeWith(range);
196: }
197:
198: private ByteRange quickMergeWith(ByteRange range) {
199: byte nfirst = first < range.first ? first : range.first;
200: byte nlast = last > range.last ? last : range.last;
201: return new ByteRange(nfirst, nlast);
202: }
203:
204: public ByteRange tryMergeWith(ByteRange range) {
205: if (!canMergeWith(range))
206: return null;
207: return quickMergeWith(range);
208: }
209:
210: /**
211: * Returns the length of the intersection between this
212: * range and a specified range.
213: *
214: * @param range
215: * the range with which to intersect this rance.
216: *
217: * @return the length of the intersection between this
218: * range and a specified range.
219: *
220: * @throws NullPointerException
221: * if <tt>range</tt> is <tt>null</tt>.
222: */
223: public int intersectionLength(ByteRange range) {
224: int len;
225: if (first >= range.first && first <= range.last) {
226: byte end = last <= range.last ? last : range.last;
227: len = ((int) (end - first)) + 1;
228: } else if (range.first >= first && range.first <= last) {
229: byte end = last <= range.last ? last : range.last;
230: len = ((int) (end - range.first)) + 1;
231: } else
232: len = 0;
233: return len;
234: }
235:
236: /**
237: * Indicates whether a specified value is a member of this
238: * range.
239: *
240: * @param v
241: * the value to test for membership.
242: *
243: * @return <tt>true</tt> if the specified value is a member
244: * of this range; returns <tt>false</tt> otherwise.
245: */
246: public boolean contains(byte v) {
247: return v >= first && v <= last;
248: }
249:
250: /**
251: * Indicates whether this range is equal to some object.
252: *
253: * @param obj
254: * the object with which to compare this range.
255: *
256: * @return <tt>true</tt> if this range is equal to the
257: * specified object; returns <tt>false</tt>
258: * otherwise.
259: */
260: public boolean equals(Object obj) {
261: if (!(obj instanceof ByteRange))
262: return false;
263: ByteRange range = (ByteRange) obj;
264: return first == range.first && last == range.last;
265: }
266:
267: /**
268: * Compares this range with some object for order.
269: *
270: * @param obj
271: * the object with which to compare this range.
272: *
273: * @return <tt>-1</tt> if this range is less than
274: * <tt>obj</tt>; returns <tt>1</tt> if this range is
275: * greater than <tt>obj</tt>; returns <tt>0</tt>
276: * otherwise, in which case <tt>this.equals(obj)</tt>
277: * is <tt>true</tt>.
278: *
279: * @throws NullPointerException
280: * if <tt>obj</tt> is <tt>null</tt>.
281: *
282: * @throws ClassCastException
283: * if <tt>obj</tt> is not of class <tt>ByteRange</tt>.
284: */
285: public int compareTo(Object obj) {
286: ByteRange range = (ByteRange) obj;
287: if (first < range.first)
288: return -1;
289: if (first > range.first)
290: return 1;
291: if (last < range.last)
292: return -1;
293: if (last > range.last)
294: return 1;
295: return 0;
296: }
297:
298: /**
299: * Returns a hash code value for this range.
300: *
301: * @return a hash code value for this range.
302: */
303: public int hashCode() {
304: return (int) (first ^ last);
305: }
306:
307: /**
308: * Returns a string representation of this range.
309: *
310: * @return a string representation of this range.
311: */
312: public String toString() {
313: return bak.pcj.util.Display.display(first) + "-"
314: + bak.pcj.util.Display.display(last);
315: }
316:
317: }
|