View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Michael Powers
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.datastore;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Iterator;
24  import java.util.LinkedList;
25  import java.util.List;
26  import java.util.ListIterator;
27  import java.util.Observable;
28  
29  public class DefaultDataView extends Observable 
30  	implements DataView
31  {
32      protected DataSoup backingSoup;
33      protected List objectList;
34      protected List keyList;
35      protected List addedObjectList;
36      protected List removedObjectList;
37      protected List addedKeyList;
38      protected List removedKeyList;
39      protected Collection updatedObjects;
40      protected boolean fullyLoaded;
41      
42  	DefaultDataView( DataSoup aSoup, Collection aKeyList )
43  	{
44  		backingSoup = aSoup;
45  		objectList = new ArrayList();
46  		keyList = new ArrayList();
47  		addedObjectList = new ArrayList();
48  		removedObjectList = new ArrayList();
49  		addedKeyList = new ArrayList();
50  		removedKeyList = new ArrayList();
51  		updatedObjects = new LinkedList();
52  		fullyLoaded = false;
53  
54  		setKeyList( aKeyList );
55  	}
56  	
57  	void setKeyList( Collection aCollection )
58  	{
59  		fullyLoaded = false;
60  		addedObjectList.clear();
61  		removedObjectList.clear();
62  		addedKeyList.clear();
63  		removedKeyList.clear();
64  		updatedObjects.clear();
65  		keyList.clear();
66  		objectList.clear();
67  		if ( ( aCollection == null ) || ( aCollection.size() == 0 ) )
68  		{
69  			return;
70  		}
71  		keyList.addAll( aCollection );
72  		for ( int i = 0; i < keyList.size(); i++ )
73  		{
74  			objectList.add( null );
75  		}
76  	}
77  
78  	public Object get( int i )
79  	{
80  		if ( i > keyList.size() ) return null;
81  
82  		Object o = objectList.get( i );
83  		if ( o == null )
84  		{
85  			Object key = keyList.get( i );
86  			if ( key == null ) return null; // FIXME!!
87  
88              //NOTE: this is the gateway for getting object from the soup
89  			o = backingSoup.getObjectByKey( (DataKey) key );
90  
91  			objectList.set( i, o );
92  		}
93  		return o;
94  	}
95  
96      public int indexOf( Object o )
97      {
98          if ( o == null ) return -1;
99          for ( int i = 0; i < size(); i++ )
100         {
101             if ( o.equals( get( i ) ) )
102             {
103                 return i;
104             }
105         }
106         return -1;
107     }
108 
109     private int indexOfIdenticalObject( Object o )
110     {
111         if ( o == null ) return -1;
112         for ( int i = 0; i < size(); i++ )
113         {
114             if ( o == get( i ) )
115             {
116                 return i;
117             }
118         }
119         return -1;
120     }
121 
122     public int lastIndexOf( Object o )
123     {
124         if ( o == null ) return -1;
125         int lastIndex = -1;
126         for ( int i = 0; i < size(); i++ )
127         {
128             if ( o.equals( get( i ) ) )
129             {
130                 lastIndex = i;
131             }
132         }
133         return lastIndex;
134     }
135 
136 	protected void loadAllObjects()
137 	{
138 		if ( fullyLoaded ) return;
139 		for ( int i = 0; i < keyList.size(); i++ )
140 		{
141 			get( i );
142 		}
143 		fullyLoaded = true;
144 	}
145 	
146     // convenience to return the first object, or null.
147     public Object getObject()
148     {
149     	return get( 0 );
150 	} 
151 	
152 	// marked object as updated
153 	public void update( Object anObject )
154 	{		
155 		if ( contains( anObject ) )
156 		{
157 			if ( ! addedObjectList.contains( anObject ) )
158 			{
159 				updatedObjects.add( anObject );
160         	}
161 		}
162 
163         // notification
164         setChanged();
165         notifyObservers( anObject );
166 	}
167 
168     // DefaultDataViews know their parent soup to perform the query
169 	//	and take the subset
170     public DataView query(
171     	String aProperty, Object beginKey, Object endKey ) { return this; }
172 
173 	public boolean commit()
174 	{
175         int index;
176         Object o;
177         DataKey key;
178 		for ( int i = 0; i < addedObjectList.size(); i++ )
179 		{
180             o = addedObjectList.get(i);
181 			key = backingSoup.addObject( o );
182             index = indexOfIdenticalObject( o );
183             keyList.set( index, key );
184 		}
185 		addedObjectList.clear();
186 		addedKeyList.clear();
187 		for ( int i = 0; i < removedObjectList.size(); i++ )
188 		{
189     		backingSoup.removeObject( (DataKey) removedKeyList.get(i) );
190 		}
191 		removedObjectList.clear();
192 		removedKeyList.clear();
193 
194 		int i;
195 		Iterator it = updatedObjects.iterator();
196 		while ( it.hasNext() )
197 		{
198 			i = objectList.indexOf( it.next() );
199 			backingSoup.updateObject(
200 				(DataKey) keyList.get(i), objectList.get(i) );
201 		}
202 		updatedObjects.clear();
203 
204         // notification
205         setChanged();
206         notifyObservers( this );
207 
208 		return true;
209 	}
210 
211     public DataKey getKeyForObject( Object anObject )
212     {
213         int index = indexOfIdenticalObject( anObject );
214         if ( index == -1 ) return null;
215         return (DataKey) keyList.get( index );
216     }
217 
218     public Object getObjectForKey( DataKey aKey )
219     {
220         int index = keyList.indexOf( aKey );
221         if ( index == -1 ) return null;
222         return get( index );
223     }
224 
225 	// interface Collection
226 
227     public int size () { return keyList.size(); }
228     public boolean isEmpty () { return keyList.isEmpty(); }
229     public void clear () { setKeyList( null ); };
230     public int hashCode() { return keyList.hashCode(); };
231 
232     public boolean equals (Object o)
233     {
234     	if ( ! ( o instanceof DefaultDataView ) ) return false;
235 	    return keyList.equals( ((DefaultDataView)o).keyList );
236 	}
237 
238     public boolean contains (Object o)
239 	{
240 		loadAllObjects();
241 		return objectList.contains(o);
242 	}
243 
244     public boolean containsAll (Collection c)
245     {
246     	loadAllObjects();
247 	    return objectList.containsAll( c );
248     }
249 
250     public boolean add (Object o)
251     {
252     	// if previously removed, restore to list
253     	if ( removedObjectList.contains( o ) )
254 	    {
255 	    	int index = removedObjectList.indexOf( o );
256 		    removedObjectList.remove( index );
257 		    Object key = removedKeyList.remove( index );
258 		    objectList.add( o );
259 		    keyList.add( key );
260 
261             // notification
262             setChanged();
263             notifyObservers( o );
264 		    return true;
265 	    }
266 
267 	    // register and add to lists
268 	    Object key = backingSoup.registerTemporaryObject( o );
269 	    addedObjectList.add( o );
270 	    addedKeyList.add( key );
271 	    objectList.add( o );
272 	    keyList.add( key );
273 
274         // notification
275         setChanged();
276         notifyObservers( o );
277 	    return true;
278     }
279 
280     public Object remove( int index )
281     {
282         Object result = get( index );
283         if ( remove( result ) )
284         {
285             return result;
286         }
287         return null;
288     }
289 
290     public boolean remove (Object o)
291     {
292     	loadAllObjects();
293 
294 	    int index = objectList.indexOf( o );
295 	    if ( index == -1 ) return false;
296 
297 	    objectList.remove( index );
298 	    Object key = keyList.remove( index );
299 
300 	    if ( updatedObjects.contains( o ) )
301 	    {
302 	    	updatedObjects.remove( o );
303 	    }
304 
305     	// if not previously added, track removal
306 	    if ( ! ( removedObjectList.contains( o ) ) )
307 	    {
308 	    	removedObjectList.add( o );
309 		    removedKeyList.add( key );
310 	    }
311 
312         // notification
313         setChanged();
314         notifyObservers( o );
315 
316 	    return true;
317     }
318 
319     /***
320     * Set completely replaces the object at the specified
321     * index with the specified object.  The new object is not
322     * marked as inserted, and the old object is not marked
323     * as deleted: the new object will be stored in the soup
324     * with the same key.  The old object is returned.
325     */ 
326     public Object set( int index, Object element )
327     {
328         Object result = objectList.set( index, element );
329         update( element );
330         return result;
331     }
332 
333     public void add( int index, Object o )
334     {
335     	// if previously removed, restore to list
336     	if ( removedObjectList.contains( o ) )
337 	    {
338 	    	int i = removedObjectList.indexOf( o );
339 		    removedObjectList.remove( i );
340 		    Object key = removedKeyList.remove( i );
341 		    objectList.add( index, o );
342 		    keyList.add( index, key );
343 
344             // notification
345             setChanged();
346             notifyObservers( o );
347 		    return;
348 	    }
349 
350 	    // register and add to lists
351 	    Object key = backingSoup.registerTemporaryObject( o );
352 	    addedObjectList.add( o );
353 	    addedKeyList.add( key );
354 	    objectList.add( index, o );
355 	    keyList.add( index, key );
356 
357         // notification
358         setChanged();
359         notifyObservers( o );
360     }
361 
362     public boolean addAll (Collection c)
363     {
364     	boolean result = true;
365     	Iterator it = c.iterator();
366 	    while ( it.hasNext() )
367 	    {
368 	    	result = result && add( it.next() );
369 	    }
370 	    return result;
371     }
372 
373     public boolean addAll (int index, Collection c)
374     {
375         int originalSize = size();
376     	boolean result = true;
377     	Iterator it = c.iterator();
378 	    while ( it.hasNext() )
379 	    {
380 	    	add( index, it.next() );
381 	    }
382 	    return ( originalSize + c.size() == size() );
383     }
384 
385     public boolean removeAll (Collection c)
386     {
387     	boolean result = true;
388     	Iterator it = c.iterator();
389 	    while ( it.hasNext() )
390 	    {
391 	    	result = result && remove( it.next() );
392 	    }
393 	    return result;
394     }
395 
396     public boolean retainAll (Collection c)
397     {
398     	removeAll( new ArrayList( objectList ) );
399 	    return addAll( c );
400 	}
401 
402     public List subList( int fromIndex, int toIndex )
403     {
404         List result = new LinkedList();
405         for ( int i = fromIndex; i < toIndex; i++ )
406         {
407             result.add( get( i ) );
408         }
409         return result;
410     }
411 
412     public Iterator iterator()
413     {
414     	loadAllObjects();
415 	    return objectList.iterator();
416 
417 /*	// uncomment to enable on-demand loading
418 		return new Iterator()
419         {
420 			int index = 0;
421             public boolean hasNext() { return ( index + 1 < keyList.size() ); }
422             public Object next() {
423 	    		return get( index++ ); }
424             public void remove()
425             {
426                 Object o = get( index );
427             	if ( o != null ) DefaultDataView.this.remove( o );
428             }
429         };
430 */
431     }
432 
433     public ListIterator listIterator()
434     {
435         return new DataViewIterator( this );
436     }
437 
438     public ListIterator listIterator( int index )
439     {
440         return new DataViewIterator( this, index );
441     }
442 
443     public Object[] toArray ()
444     {
445     	loadAllObjects();
446 	    return objectList.toArray();
447     }
448     public java.lang.Object[] toArray (Object[] array)
449     {
450     	loadAllObjects();
451 	    return objectList.toArray( array );
452     }
453 
454     protected class DataViewIterator implements ListIterator
455     {
456         DataView theView;
457         int currentIndex;
458 
459         //TODO: should track current object in addition to index
460         // to track external changes to the view. (or should be listener)
461         Object currentObject;
462 
463         public DataViewIterator( DataView aView )
464         {
465             this( aView, 0 );
466         }
467 
468         public DataViewIterator( DataView aView, int index )
469         {
470             theView = aView;
471             if ( theView.size() > index )
472             {
473                 currentIndex = index;
474                 currentObject = theView.get( currentIndex );
475             }
476             else
477             {
478                 index = -1;
479                 currentObject = null;
480             }
481         }
482 
483         public void add( Object o )
484         {
485             currentIndex++;
486             theView.add( currentIndex, o );
487         }
488 
489         public boolean hasNext()
490         {
491             return ( theView.size() > currentIndex + 1 );
492         }
493 
494         public boolean hasPrevious()
495         {
496             return ( currentIndex > -1 );
497         }
498 
499         public Object next()
500         {
501             return theView.get( ++currentIndex );
502         }
503 
504         public int nextIndex()
505         {
506             return currentIndex + 1;
507         }
508 
509         public Object previous()
510         {
511             return theView.get( currentIndex-- );
512         }
513 
514         public int previousIndex()
515         {
516             return currentIndex;
517         }
518 
519         public void remove()
520         {
521             theView.remove( currentIndex-- );
522         }
523 
524         public void set( Object o )
525         {
526             theView.set( currentIndex, o );
527         }
528 
529     }
530 }
531 
532 /*
533  * $Log$
534  * Revision 1.2  2006/02/19 16:26:19  cgruber
535  * Move non-unit-test code to tests project
536  * Fix up code to work with proper imports
537  * Fix maven dependencies.
538  *
539  * Revision 1.1  2006/02/16 13:18:56  cgruber
540  * Check in all sources in eclipse-friendly maven-enabled packages.
541  *
542  * Revision 1.2  2001/02/15 21:12:41  mpowers
543  * Added accessors for key throughout the api.  This breaks compatibility.
544  * insertObject now returns the permanent key for the newly created object.
545  * The old way returned a copy of the object which was an additional read
546  * that was often ignored.  Now you can read it only if you need it.
547  * Furthermore, there was not other way of getting the permanent key.
548  *
549  * Revision 1.1.1.1  2000/12/21 15:47:14  mpowers
550  * Contributing wotonomy.
551  *
552  * Revision 1.2  2000/12/20 16:25:36  michael
553  * Added log to all files.
554  *
555  *
556  */
557