View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Blacksmith, Inc.
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
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     	// TODO: Test this logic.
138     	return ( location() << 2 ) & length(); // bitwise ops never my forte 
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     	// TODO: Test this logic.    
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     	// TODO: Test this logic.    
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     	// TODO: Test this logic.    
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     	// TODO: Test this logic.    
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 	    // TODO: Test this logic.
223 	    // no intersection: return this and aRange without calculation
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 	    // TODO: Test this logic.
240 	    // this range is completely contained by other range
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 ); // test this
253 	    	}
254 		    return;
255 	    }
256 
257 	    // TODO: Test this logic.
258 	    // other range is completely contained by this range
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 ); // test this
271 	    	}
272 		    return;
273 	    }
274 	    
275 	    // TODO: Test this logic.
276 	    // ranges intersect: remove only the intersection
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 ); // test this
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     	// TODO: Test this logic.    
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  * $Log$
340  * Revision 1.2  2006/02/16 13:15:00  cgruber
341  * Check in all sources in eclipse-friendly maven-enabled packages.
342  *
343  * Revision 1.1.1.1  2000/12/21 15:47:42  mpowers
344  * Contributing wotonomy.
345  *
346  * Revision 1.3  2000/12/20 16:25:38  michael
347  * Added log to all files.
348  *
349  *
350  */
351