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  import java.io.Serializable;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Enumeration;
26  import java.util.Iterator;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.ListIterator;
30  
31  /***
32  * NSArray is an unmodifiable List.  
33  * Calling the mutator methods of the List interface (add, addAll, 
34  * set, etc.) on an instance of NSArray will throw an Unsupported
35  * Operation exception: use a NSMutableArray instead.  This is to
36  * simulate Objective-C's pattern of exposing mutator methods only
37  * on mutable subinterface, which is wonderful for communicating
38  * via interface the contract on returned collections (whether you
39  * may modify return values) as well as implementing array faults.
40  *
41  * @author michael@mpowers.net
42  * @author $Author: cgruber $
43  * @version $Revision: 929 $
44  */
45  public class NSArray implements List, Serializable
46  {
47      /***
48      * Actual list that backs this instance.
49      */
50      List list;
51      
52  	/***
53  	* Return value when array index is not found.
54  	*/
55      public static final int NotFound = -1;
56  
57  	/***
58  	* A constant representing an empty array.
59  	*/
60      public static final NSArray EmptyArray = new NSArray();
61  
62      /***
63      * Returns an NSArray backed by the specified List.
64      * This is useful to "protect" an internal representation
65      * that is returned by a method of return type NSArray.
66      */
67      public static NSArray arrayBackedByList( List aList )
68      {
69          return new NSArray( aList, null );
70      }
71      
72      /***
73       * A constructor that uses the provided list as the backing 
74       * list object.  This is unlike ArrayList and other 
75       * java.util.Collection types insofar as that API requires
76       * that the provided Collection be copied into the newly constructed
77       * Collection.
78       * 
79       * TODO: See if this signature can be reasonably changed, as having a no-op parameter is a little counter-intuitive.
80       * @param aList A list that the caller wishes to become the backing list for the NSArray.
81       * @param ignored This parameter is entirely ignored, and is only there to distinguish the API.
82       */
83      NSArray( List aList, Object ignored ) // differentiates
84      {
85          list = aList;
86      }
87      
88      /***
89      * Constructor with a size hint, used by NSMutableArray.
90      */
91      NSArray( int aSize )
92      {
93          list = new ArrayList( aSize );
94      }
95      
96  	/***
97  	* Default constructor returns an empty array.
98  	*/
99      public NSArray ()
100     {
101     	list = new ArrayList();
102     }	
103 
104 	/***
105 	* Produces an array containing only the specified object.
106 	*/
107     public NSArray (Object anObject)
108     {
109     	this();
110 	    list.add( anObject );
111     }
112 
113 	/***
114 	* Produces an array containing the specified objects.
115 	*/
116     public NSArray (Object[] anArray)
117     {
118     	this();
119 	    for ( int i = 0; i < anArray.length; i++ )
120 	    {
121 	    	list.add( anArray[i] );
122 	    }
123     }
124 
125 	/***
126 	* Produces an array containing the objects in the specified collection.
127 	*/
128     public NSArray (Collection aCollection)
129     {
130         this();
131     	Iterator i = aCollection.iterator();
132         while ( i.hasNext() ) list.add( i.next() );
133     }
134 
135 	/***
136 	* Returns the number of items in this array.
137 	*/
138     public int count ()
139     {
140     	return list.size();
141     }
142 
143 	/***
144 	* Returns an array containing all objects in this array 
145 	* plus the specified object.
146 	*/
147     public NSArray arrayByAddingObject (Object anObject)
148     {
149     	NSArray result = new NSArray( this );
150 	    result.protectedAdd( anObject );
151 	    return result;
152     }
153 
154 	/***
155 	* Returns an array containing all objects in this array
156 	* plus all objects in the specified list.
157 	*/
158     public NSArray arrayByAddingObjectsFromArray (Collection aCollection)
159     {
160     	NSArray result = new NSArray( this );
161 	    result.protectedAddAll( aCollection );
162 	    return result;
163     }
164 
165 	/***
166 	* Returns a string containing the string representations of
167 	* each element in this array, with each element separated from
168 	* each neighboring element by the specified string.
169 	*/
170     public String componentsJoinedByString (String separator)
171     {
172     	StringBuffer buf = new StringBuffer();
173 	    Iterator it = list.iterator();
174 	    if ( it.hasNext() )
175 	    {
176 	    	buf.append( it.next().toString() );
177 	    }
178 	    while ( it.hasNext() )
179 	    {
180 	    	buf.append( separator );
181 		    buf.append( String.valueOf(it.next()) );
182 	    }
183 	    return buf.toString();
184     }
185 
186 	/***
187 	* Returns whether an equivalent object is contained in this array.
188 	*/
189     public boolean containsObject (Object anObject)
190     {
191     	return list.contains( anObject );
192     }
193 
194 	/***
195 	* Returns the first object in this array that is equivalent to
196 	* an object in the specified list, or null if no objects are
197 	* in common.
198 	*/
199     public Object firstObjectCommonWithArray (Collection aCollection)
200     {
201     	if ( aCollection == null ) return null;
202 	    
203 	    Object o;
204 	    Iterator it = list.iterator();
205 	    while ( it.hasNext() )
206 	    {
207 	    	o = it.next();
208 	    	if ( aCollection.contains( o ) ) return o;
209 	    }
210 	    return null;
211     }
212 
213 	/***
214 	* Returns whether the specified list contains elements equivalent
215 	* to those in this array in the same order.
216 	*/
217     public boolean isEqualToArray (List aList)
218     {
219     	return list.equals( aList );
220     }
221 
222 	/***
223 	* Returns the last object in this array, or null if the array is empty.
224 	*/
225     public Object lastObject ()
226     {
227     	int i;
228 	    if ( (i = list.size()) == 0 ) return null;
229 	    return list.get( i - 1 );
230     }
231 
232 	/***
233 	* 
234 	*/
235 /*
236     public NSArray sortedArrayUsingSelector (NSSelector);
237 */
238 
239 	/***
240 	* Returns an array comprised of only those elements whose
241 	* indices fall within the specified range.
242 	*/
243     public NSArray subarrayWithRange (NSRange aRange)
244     {
245     	//TODO: Test this logic.
246     	NSArray result = new NSArray();
247     	if ( aRange == null ) return result;
248 	    
249 	    int loc = aRange.location();
250 	    int max = aRange.maxRange();
251         int count = count();
252     	for ( int i = loc; i <= max && i < count; i++ )
253 	    {
254 	    	result.protectedAdd( list.get( i ) );
255 	    }
256 	    return result;
257     }
258 
259 	/***
260 	* Returns an enumeration over the the elements of the array.
261 	*/
262     public Enumeration objectEnumerator ()
263     {
264     	//TODO: Test this logic.
265     	return new Enumeration()
266 		{
267 	    	Iterator it = NSArray.this.iterator(); 
268 	    	public boolean hasMoreElements()
269 		    {
270 				return it.hasNext();
271 		    }
272 		    public Object nextElement()
273 		    {
274 		    	return it.next();
275 		    }
276 	    };
277 	}
278 
279 	/***
280 	* Returns an enumeration over the elements of the array in reverse order.
281 	*/
282     public java.util.Enumeration reverseObjectEnumerator ()
283     {
284 	    return new java.util.Enumeration()
285 	    {
286 	    	ListIterator it = null;
287 		    public ListIterator getIterator()
288 		    {
289 		    	if ( it == null )
290 			    {
291                     it = NSArray.this.listIterator();
292                     // zoom to end
293                     while ( it.hasNext() ) it.next();
294 				}
295 				return it;
296 		    }
297 	    	public boolean hasMoreElements()
298 		    {
299 				return getIterator().hasPrevious();
300 		    }
301 		    public Object nextElement()
302 		    {
303 		    	return getIterator().previous();
304 		    }
305 	    };
306 	}
307 
308 	/***
309 	* Copies the elements of this array into the specified object array
310 	* as the array's capacity permits.
311 	*/
312     public void getObjects (Object[] anArray)
313     {
314     	getObjects(anArray,null);
315     }
316 
317 	/***
318 	* Copies the elements of this array that fall within the specified range
319 	* into the specified object array as the array's capacity permits. This
320 	* method must not overflow, even in the face of a null range, an over or
321 	* under-sized array, or a bad range.  It may underflow and fail to 
322 	* entirely populate the array, if the array is larger than the data to 
323 	* be copied.
324 	* 
325 	* TODO: Check whether in WebObjects the range supposed to be measured against the parameter or the NSArray itself??? -ceg
326 	* 
327 	* @param anArray An object array to be filled by this method.
328 	* @param range An NSRange object representing the range of data in the NSArray to be copied.
329 	*/
330     public void getObjects (Object[] array, NSRange range)
331     {
332     	if ( array == null ) return;
333 	    if ( range == null) range = new NSRange(0,array.length);
334 	    int limit = Math.min(Math.min(array.length,range.length()),(count()-range.location()));
335 	    for ( int i = 0; i < limit ; i++ ) {
336 	    	//anArray[ i-aRange.location() ] = objectAtIndex( i );
337 	    	array[ i ] = objectAtIndex( range.location() + i );
338 	    }
339     }
340 
341 	/***
342 	* Returns the index of the first object in the array equivalent
343 	* to the specified object.  Returns NotFound if the item is not found.
344 	*/
345     public int indexOfObject (Object anObject)
346     {
347     	int result = list.indexOf( anObject );
348 	    if ( result == -1 ) return NotFound; // in case this changes
349 	    return result;
350     }
351 
352 	/***
353 	* Returns the index of the first object in the array 
354 	* within the specified range equivalent to the specified object.
355 	* Returns NotFound if the item is not found.
356 	*/
357     public int indexOfObject (Object anObject, NSRange aRange)
358     {
359     	if ( ( anObject == null ) || ( aRange == null ) ) return NotFound;
360 	    
361 	    int loc = aRange.location();
362 	    int max = aRange.maxRange();
363 	    for ( int i = loc; i < max; i++ )
364 	    {
365 	    	if ( anObject.equals( list.get(i) ) )
366 		    {
367 		    	return i;
368 		    }
369 	    }
370 	    return NotFound;
371     }
372 
373 	/***
374 	* Returns the index of the specified object if it exists
375 	* in the array, comparing by reference.
376 	* Returns NotFound if the item is not found.
377 	*/
378     public int indexOfIdenticalObject (Object anObject)
379     {
380         int size = list.size();
381 	    for ( int i = 0; i < size; i++ )
382 	    {
383 	    	if ( anObject == list.get(i) )
384 		    {
385 		    	return i;
386 		    }
387 	    }
388 	    return NotFound;
389     }
390 
391 	/***
392 	* Returns the index of the first object in the array 
393 	* within the specified range equivalent to the specified object.
394 	*/
395     public int indexOfIdenticalObject (Object anObject, NSRange aRange)
396     {
397     	if ( aRange == null ) return NotFound;
398 	    
399 	    int loc = aRange.location();
400 	    int max = aRange.maxRange();
401 	    for ( int i = loc; i < max; i++ )
402 	    {
403 	    	if ( anObject == list.get(i) )
404 		    {
405 		    	return i;
406 		    }
407 	    }
408 	    return NotFound;
409     }
410 
411 	/***
412 	* Returns the object at the specified index.  Throws an
413 	* IndexOutOfRange exception if the index is out of range.
414 	*/
415     public Object objectAtIndex (int anIndex)
416     {
417     	return list.get( anIndex );
418     }
419 
420 	/***
421 	* Returns an array consisting of strings within the specified string
422 	* as delimited by the specified separator characters. 
423 	*/
424     public static NSArray componentsSeparatedByString 
425     	(String aString, String aSeparator)
426     {
427     	NSArray result = new NSArray();
428 	    if ( aString == null ) return result;
429 	    if ( aSeparator == null ) return new NSArray( aString );
430 	    
431         //FIXME: The spec probably considers the whole
432         // string as a separator, unlike string tokenizer.
433 	    java.util.StringTokenizer tokens = 
434 	    	new java.util.StringTokenizer( aString, aSeparator );
435 	    while ( tokens.hasMoreTokens() )
436 	    {
437 	    	result.protectedAdd( tokens.nextToken() );
438 	    }
439 	    
440 	    return result;
441     }
442     
443     public Object clone()
444     {
445         return new NSArray( list );
446     }
447     
448     public NSArray immutableClone()
449     {
450         return this;
451     }
452 	
453     public NSMutableArray mutableClone()
454     {
455         return new NSMutableArray( this );
456     }
457     
458     public String toString() {
459         StringBuffer buf = new StringBuffer();
460         buf.append(NSPropertyListSerialization.TOKEN_BEGIN[NSPropertyListSerialization.PLIST_ARRAY]);
461         for (int i = 0; i < count(); i++) {
462             Object x = objectAtIndex(i);
463             buf.append(NSPropertyListSerialization.stringForPropertyList(x));
464             if (i < count() - 1)
465                 buf.append(", ");
466         }
467         buf.append(NSPropertyListSerialization.TOKEN_END[NSPropertyListSerialization.PLIST_ARRAY]);
468         return buf.toString();
469     }
470     
471     // interface List: accessors
472     
473     public boolean contains(Object o) { return list.contains(o); }
474     public boolean containsAll(Collection c) { return list.containsAll(c); }
475     public boolean equals(Object o) { return list.equals(o); }
476     public Object get(int index) { return list.get(index); }
477     public int hashCode() { 
478     	int code = 19;
479     	code *= getClass().hashCode();
480     	code *= list.hashCode();
481     	return code;
482     }
483     public int indexOf(Object o) { return list.indexOf(o); }
484     public boolean isEmpty() { return list.isEmpty(); }
485     public int lastIndexOf(Object o) { return list.lastIndexOf(o); }
486 	public int size() { return list.size(); }
487     public Object[] toArray() { return list.toArray(); }
488     public Object[] toArray(Object[] a) { return list.toArray(a); }
489     
490     // interface List: mutators
491 
492 
493 	public void add(int index, Object element) 
494     {
495 		this.list.add(index,element);
496     }
497     
498 	public boolean add(Object o)
499     {
500 		return this.list.add(o);
501     }
502 
503 	public boolean addAll(Collection coll) 
504     {
505 	    return this.list.addAll(coll);
506     }
507 
508 	public boolean addAll(int index, Collection c) 
509     {
510 		return this.list.addAll(index,c);
511     }
512     
513 	public void clear() 
514     {
515 		this.list.clear();
516     }
517 
518 	public Iterator iterator() 
519     {
520         // make a copy to avoid ConcurrentModificationExceptions
521         final Iterator i = new LinkedList( list ).iterator();
522 	    return new Iterator() 
523         {
524             public boolean hasNext() {return i.hasNext();}
525             public Object next() 	 {return i.next();}
526             public void remove() { throw new UnsupportedOperationException(); }
527         };
528     }
529 
530 	public ListIterator listIterator() { return listIterator(0); }
531 
532 	public ListIterator listIterator(final int index) 
533     {
534         // make a copy to avoid ConcurrentModificationExceptions
535         final ListIterator i = new LinkedList( list ).listIterator(index);
536 	    return new ListIterator() 
537         {
538             public boolean hasNext()     {return i.hasNext();}
539             public Object next()         {return i.next();}
540             public boolean hasPrevious() {return i.hasPrevious();}
541             public Object previous()     {return i.previous();}
542             public int nextIndex()       {return i.nextIndex();}
543             public int previousIndex()   {return i.previousIndex();}
544             public void remove() { throw new UnsupportedOperationException(); }
545             public void set(Object o) { throw new UnsupportedOperationException(); }
546             public void add(Object o) { throw new UnsupportedOperationException(); }
547 	    };
548 	}
549 
550 	public Object remove(int index) 
551     {
552 		return this.list.remove(index);
553     }
554     
555 	public boolean remove(Object o) 
556     {
557 		return this.list.remove(o);
558     }
559 
560 	public boolean removeAll(Collection coll) 
561     {
562 		return this.list.removeAll(coll);
563     }
564 
565 	public boolean retainAll(Collection coll) 
566     {
567 		return this.list.retainAll(coll);
568     }
569 
570     public Object set(int index, Object element) 
571     {
572     	return this.list.set(index,element);
573     }
574     
575     public List subList(int fromIndex, int toIndex) 
576     {
577         return Collections.unmodifiableList(list.subList(fromIndex, toIndex));
578     }
579     
580     /***
581     * Provided for the use of subclasses like ArrayFault.
582     */
583     protected boolean protectedAdd( Object o )
584     {
585         return list.add( o );
586     }
587     
588     /***
589     * Provided for the use of subclasses like ArrayFault.
590     */
591     protected boolean protectedAddAll( Collection coll )
592     {
593         return list.addAll( coll );
594     }
595 }
596 
597 /*
598  * $Log$
599  * Revision 1.2  2006/03/10 00:52:27  cgruber
600  * Add tests for NSArray and fix some problems that became obvious as a result.
601  *
602  * Revision 1.1  2006/02/16 12:47:16  cgruber
603  * Check in all sources in eclipse-friendly maven-enabled packages.
604  *
605  * Revision 1.16  2005/07/13 14:12:44  cgruber
606  * Add mutableClone() and immutableClone()  per. WebObjects 5.3 conformance.
607  *
608  * Revision 1.15  2003/08/06 23:07:52  chochos
609  * general code cleanup (mostly, removing unused imports)
610  *
611  * Revision 1.14  2003/08/05 00:48:56  chochos
612  * use NSPropertyListSerialization to get the opening and closing tokens for the string representation
613  *
614  * Revision 1.13  2003/08/04 20:26:10  chochos
615  * use NSPropertyListSerialization inside toString()
616  *
617  * Revision 1.12  2003/08/04 18:18:43  chochos
618  * toString() yields strings in the same format as Apple's NSArray
619  *
620  * Revision 1.11  2003/01/28 19:44:20  mpowers
621  * Fixed reverse enumerator.
622  *
623  * Revision 1.10  2003/01/18 23:49:55  mpowers
624  * Added mutableClone().
625  *
626  * Revision 1.9  2003/01/18 23:30:42  mpowers
627  * WODisplayGroup now compiles.
628  *
629  * Revision 1.8  2003/01/16 22:47:30  mpowers
630  * Compatibility changes to support compiling woextensions source.
631  * (34 out of 56 classes compile!)
632  *
633  * Revision 1.7  2003/01/10 19:16:40  mpowers
634  * Implemented support for page caching.
635  *
636  * Revision 1.6  2002/10/24 21:15:36  mpowers
637  * New implementations of NSArray and subclasses.
638  *
639  * Revision 1.5  2002/10/24 18:16:30  mpowers
640  * Now enforcing NSArray's immutable nature.
641  *
642  * Revision 1.4  2002/03/08 19:02:54  mpowers
643  * Long-overdue speed optimization of indexOfIdenticalObject.
644  *
645  * Revision 1.3  2002/02/13 22:02:56  mpowers
646  * Fixed: bug in componentsSeparatedByString when separator is null
647  * (thanks to Cedrik LIME).
648  *
649  * Revision 1.2  2001/01/11 20:34:26  mpowers
650  * Implemented EOSortOrdering and added support in framework.
651  * Added header-click to sort table columns.
652  *
653  * Revision 1.1.1.1  2000/12/21 15:47:26  mpowers
654  * Contributing wotonomy.
655  *
656  * Revision 1.3  2000/12/20 16:25:37  michael
657  * Added log to all files.
658  *
659  *
660  */
661