1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.foundation;
20
21 /***
22 * A pure java implementation of NSRange.
23 * An NSRange represents a range of numbers
24 * having a starting location and spanning a
25 * length.
26 *
27 * @author michael@mpowers.net
28 * @author $Author: cgruber $
29 * @version $Revision: 920 $
30 */
31 public class NSRange implements Cloneable
32 {
33 /***
34 * An empty range.
35 */
36 public static final NSRange ZeroRange = new NSRange();
37
38 protected int loc;
39 protected int len;
40
41 /***
42 * Default constructor produces an empty range.
43 */
44 public NSRange ()
45 {
46 this( 0, 0 );
47 }
48
49 /***
50 * Produces a range with the specified location and length.
51 */
52 public NSRange (int location, int length)
53 {
54 loc = location;
55 len = length;
56 }
57
58 /***
59 * Produces a range that has the same location and length as
60 * the specified range.
61 */
62 public NSRange (NSRange aRange)
63 {
64 this( aRange.location(), aRange.length() );
65 }
66
67 /***
68 * Returns the location of this range.
69 */
70 public int location ()
71 {
72 return loc;
73 }
74
75 /***
76 * Returns the length of this range.
77 */
78 public int length ()
79 {
80 return len;
81 }
82
83 /***
84 * Returns the maximum extent of the range. This number is
85 * one more than the last position in the range.
86 */
87 public int maxRange ()
88 {
89 return location() + length() -1;
90 }
91
92 /***
93 * Returns whether this is an empty range, therefore
94 * whether the length is zero.
95 */
96 public boolean isEmpty ()
97 {
98 return ( length() == 0 );
99 }
100
101 /***
102 * Returns whether the specified location is contained
103 * within this range.
104 */
105 public boolean locationInRange (int location)
106 {
107 if ( location < location() ) return false;
108 if ( location >= maxRange() ) return false;
109 return true;
110 }
111
112 /***
113 * Returns whether the specified range is equal to this range.
114 */
115 public boolean isEqualToRange (NSRange aRange)
116 {
117 if ( aRange == null ) return false;
118 return ( ( aRange.location() == location() )
119 && ( aRange.length() == length() ) );
120 }
121
122 /***
123 * Returns whether the specified object is equal to this range.
124 */
125 public boolean equals (Object anObject)
126 {
127 if ( anObject instanceof NSRange )
128 return isEqualToRange( (NSRange) anObject );
129 return false;
130 }
131
132 /***
133 * Returns a hashCode.
134 */
135 public int hashCode ()
136 {
137
138 return ( location() << 2 ) & length();
139 }
140
141 /***
142 * Returns a string representation of this range.
143 */
144 public String toString ()
145 {
146 return "[NSRange: location = " + location()
147 + "; length = " + length() + "]";
148 }
149
150 /***
151 * Returns the union of this range and the specified range, if any.
152 * Gaps are filled, so the result is the smallest starting position
153 * and the largest ending position.
154 */
155 public NSRange rangeByUnioningRange (NSRange aRange)
156 {
157 if ( aRange == null ) return this;
158
159
160 int resultLoc = Math.min( this.location(), aRange.location() );
161 int resultLen = Math.max( this.location() + this.length(),
162 aRange.location() + aRange.length() ) - resultLoc;
163 return new NSRange( resultLoc, resultLen );
164 }
165
166 /***
167 * Returns the intersection of this range and the specified range,
168 * if any. If no intersection, returns an empty range.
169 */
170 public NSRange rangeByIntersectingRange (NSRange aRange)
171 {
172
173 if ( ! intersectsRange( aRange ) ) return ZeroRange;
174 int start = Math.max( this.location(), aRange.location() );
175 int end = Math.min( this.location() + this.length(),
176 aRange.location() + aRange.length() );
177 return new NSRange( start, end - start );
178 }
179
180 /***
181 * Returns whether the specified range overlaps
182 * at any point with this range.
183 */
184 public boolean intersectsRange (NSRange aRange)
185 {
186
187 if ( aRange == null ) return false;
188 if ( ( this.location() >= aRange.location() )
189 && ( this.location() < aRange.location() + aRange.length() ) )
190 return true;
191 if ( ( aRange.location() >= this.location() )
192 && ( aRange.location() < this.location() + this.length() ) )
193 return true;
194 return false;
195 }
196
197 /***
198 * Returns whether this range is completely
199 * contained within the specified range.
200 */
201 public boolean isSubrangeOfRange (NSRange aRange)
202 {
203
204 if ( aRange == null ) return false;
205 if ( ( this.location() >= aRange.location() )
206 && ( this.maxRange() <= aRange.maxRange() ) )
207 return true;
208 return false;
209 }
210
211 /***
212 * Eliminates any intersections between this range and the specified
213 * range. This produces two ranges, either of which may be empty.
214 * These two ranges are returned by modifying the supplied second
215 * and third parameters.
216 */
217 public void subtractRange (NSRange aRange,
218 NSMutableRange firstResult, NSMutableRange secondResult)
219 {
220 if ( aRange == null ) return;
221
222
223
224 if ( ! intersectsRange( aRange ) )
225 {
226 if ( firstResult != null )
227 {
228 firstResult.setLocation( this.location() );
229 firstResult.setLength( this.length() );
230 }
231 if ( secondResult != null )
232 {
233 secondResult.setLocation( aRange.location() );
234 secondResult.setLength( aRange.location() );
235 }
236 return;
237 }
238
239
240
241 if ( isSubrangeOfRange( aRange ) )
242 {
243 if ( firstResult != null )
244 {
245 firstResult.setLocation( aRange.location() );
246 firstResult.setLength( this.location() - aRange.location() );
247 }
248 if ( secondResult != null )
249 {
250 secondResult.setLocation( this.maxRange() );
251 secondResult.setLength(
252 aRange.maxRange() - this.maxRange() - 1 );
253 }
254 return;
255 }
256
257
258
259 if ( aRange.isSubrangeOfRange( this ) )
260 {
261 if ( firstResult != null )
262 {
263 firstResult.setLocation( this.location() );
264 firstResult.setLength( aRange.location() - this.location() );
265 }
266 if ( secondResult != null )
267 {
268 secondResult.setLocation( aRange.maxRange() );
269 secondResult.setLength(
270 this.maxRange() - aRange.maxRange() - 1 );
271 }
272 return;
273 }
274
275
276
277
278 NSRange firstRange, secondRange;
279 if ( this.location() <= aRange.location() )
280 {
281 firstRange = this;
282 secondRange = aRange;
283 }
284 else
285 {
286 firstRange = aRange;
287 secondRange = this;
288 }
289
290 if ( firstResult != null )
291 {
292 firstResult.setLocation( firstRange.location() );
293 firstResult.setLength(
294 secondRange.location() - firstRange.location() );
295 }
296 if ( secondResult != null )
297 {
298 secondResult.setLocation( firstRange.maxRange() );
299 secondResult.setLength(
300 secondRange.maxRange() - aRange.maxRange() - 1 );
301 }
302 return;
303
304 }
305
306 /***
307 * Returns a copy of this range.
308 */
309 public Object clone ()
310 {
311 return new NSRange( location(), length() );
312 }
313
314 /***
315 * Parses a range from a string of the form "{x,y}" where
316 * x is the location and y is the length. If not parsable,
317 * an IllegalArgumentException is thrown.
318 */
319 public static NSRange fromString (String aString)
320 {
321
322 try
323 {
324 java.util.StringTokenizer tokens =
325 new java.util.StringTokenizer( aString, "{,}" );
326 int loc = Integer.parseInt( tokens.nextToken() );
327 int len = Integer.parseInt( tokens.nextToken() );
328 return new NSRange( loc, len );
329 }
330 catch ( Exception exc )
331 {
332 throw new IllegalArgumentException( exc.toString() );
333 }
334 }
335
336 }
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351