KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > access > IncrementalFaultList


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56 package org.objectstyle.cayenne.access;
57
58 import java.util.ArrayList JavaDoc;
59 import java.util.Collection JavaDoc;
60 import java.util.Collections JavaDoc;
61 import java.util.Iterator JavaDoc;
62 import java.util.List JavaDoc;
63 import java.util.ListIterator JavaDoc;
64 import java.util.Map JavaDoc;
65 import java.util.NoSuchElementException JavaDoc;
66
67 import org.objectstyle.cayenne.CayenneException;
68 import org.objectstyle.cayenne.CayenneRuntimeException;
69 import org.objectstyle.cayenne.DataObject;
70 import org.objectstyle.cayenne.exp.Expression;
71 import org.objectstyle.cayenne.exp.ExpressionFactory;
72 import org.objectstyle.cayenne.map.DbEntity;
73 import org.objectstyle.cayenne.map.ObjEntity;
74 import org.objectstyle.cayenne.query.GenericSelectQuery;
75 import org.objectstyle.cayenne.query.SelectQuery;
76 import org.objectstyle.cayenne.util.Util;
77
78 /**
79  * A synchronized list that serves as a container of DataObjects. It is returned
80  * when a paged query is performed by DataContext. On creation, only the first
81  * "page" is fully resolved, for the rest of the objects only their ObjectIds
82  * are read. Pages following the first page are resolved on demand only. On
83  * access to an element, the list would ensure that this element as well as all
84  * its siblings on the same page are fully resolved.
85  *
86  * <p>The list can hold DataRows or DataObjects. Attempts to
87  * add any other object types will result in an exception.</p>
88  *
89  * <p>Performance note: certain operations like <code>toArray</code> would
90  * trigger full list fetch.</p>
91  *
92  * @author Andrei Adamchik
93  */

94 public class IncrementalFaultList implements List JavaDoc {
95
96     protected int pageSize;
97     protected List JavaDoc elements;
98     protected DataContext dataContext;
99     protected ObjEntity rootEntity;
100     protected SelectQuery internalQuery;
101     protected int unfetchedObjects;
102
103     /**
104      * Stores a hint allowing to distinguish data rows from unfetched ids
105      * when the query fetches data rows.
106      */

107     protected int rowWidth;
108
109     private IncrementalListHelper helper;
110
111     /**
112      * Defines the upper limit on the size of fetches. This is needed to avoid where clause size limitations.
113      */

114     protected int maxFetchSize = 10000;
115     // Don't confuse this with the JDBC ResultSet fetch size setting - this controls
116
// the where clause generation that is necessary to fetch specific records a page
117
// at a time. Some JDBC Drivers/Databases may have limits on statement length
118
// or complexity of the where clause - e.g., PostgreSQL having a default limit of 10,000
119
// nested expressions.
120

121     /**
122      * Creates a new list copying settings from another list.
123      * Elements WILL NOT be copied or fetched.
124      */

125     public IncrementalFaultList(IncrementalFaultList list) {
126         this.pageSize = list.pageSize;
127         this.internalQuery = list.internalQuery;
128         this.dataContext = list.dataContext;
129         this.rootEntity = list.rootEntity;
130         this.maxFetchSize = list.maxFetchSize;
131         this.rowWidth = list.rowWidth;
132         this.helper = list.helper;
133         elements = Collections.synchronizedList(new ArrayList JavaDoc());
134     }
135
136     /**
137      * Creates a new IncrementalFaultList using a given DataContext and query.
138      *
139      * @param dataContext DataContext used by IncrementalFaultList to fill itself with objects.
140      * @param query Main query used to retrieve data. Must have "pageSize" property set to a
141      * value greater than zero.
142      */

143     public IncrementalFaultList(DataContext dataContext, GenericSelectQuery query) {
144         if (query.getPageSize() <= 0) {
145             throw new CayenneRuntimeException(
146                     "IncrementalFaultList does not support unpaged queries. Query page size is "
147                             + query.getPageSize());
148         }
149
150         this.elements = Collections.synchronizedList(new ArrayList JavaDoc());
151         this.dataContext = dataContext;
152         this.pageSize = query.getPageSize();
153         this.rootEntity = dataContext.getEntityResolver().lookupObjEntity(query);
154
155         // create an internal query, it is a partial replica of
156
// the original query and will serve as a value holder for
157
// various parameters
158
this.internalQuery = new SelectQuery();
159         this.internalQuery.setRoot(query.getRoot());
160         this.internalQuery.setLoggingLevel(query.getLoggingLevel());
161         this.internalQuery.setFetchingDataRows(query.isFetchingDataRows());
162         this.internalQuery.setResolvingInherited(query.isResolvingInherited());
163
164         if (query.isFetchingDataRows()) {
165             helper = new DataRowListHelper();
166         }
167         else {
168             helper = new DataObjectListHelper();
169         }
170
171         if (!query.isFetchingDataRows() && (query instanceof SelectQuery)) {
172             SelectQuery select = (SelectQuery) query;
173
174             this.internalQuery.addPrefetches(select.getPrefetches());
175
176             // prefetching will blow iterated result, so strip prefetches... this is
177
// a bit of a hack
178
if (!select.getPrefetches().isEmpty()) {
179                 SelectQuery clone = select.queryWithParameters(
180                         Collections.EMPTY_MAP,
181                         true);
182
183                 Iterator JavaDoc it = select.getPrefetches().iterator();
184                 while (it.hasNext()) {
185                     clone.removePrefetch((String JavaDoc) it.next());
186                 }
187
188                 query = clone;
189             }
190         }
191
192         fillIn(query);
193     }
194
195     private boolean resolvesFirstPage() {
196         return internalQuery.getPrefetches().isEmpty();
197     }
198
199     /**
200      * Performs initialization of the internal list of objects.
201      * Only the first page is fully resolved. For the rest of
202      * the list, only ObjectIds are read.
203      *
204      * @since 1.0.6
205      */

206     protected void fillIn(GenericSelectQuery query) {
207         synchronized (elements) {
208
209             boolean fetchesDataRows = internalQuery.isFetchingDataRows();
210
211             // start fresh
212
elements.clear();
213             rowWidth = 0;
214
215             try {
216                 long t1 = System.currentTimeMillis();
217                 ResultIterator it = dataContext.performIteratedQuery(query);
218                 try {
219
220                     rowWidth = it.getDataRowWidth();
221
222                     // resolve first page if we can
223
if (resolvesFirstPage()) {
224                         // read first page completely, the rest as ObjectIds
225
List JavaDoc firstPage =
226                             (fetchesDataRows) ? elements : new ArrayList JavaDoc(pageSize);
227                         for (int i = 0; i < pageSize && it.hasNextRow(); i++) {
228                             firstPage.add(it.nextDataRow());
229                         }
230
231                         // convert rows to objects
232
if (!fetchesDataRows) {
233                             elements.addAll(
234                                 dataContext.objectsFromDataRows(
235                                     rootEntity,
236                                     firstPage,
237                                     query.isRefreshingObjects(),
238                                     query.isResolvingInherited()));
239                         }
240                     }
241
242                     // continue reading ids
243
DbEntity entity = rootEntity.getDbEntity();
244                     while (it.hasNextRow()) {
245                         elements.add(it.nextObjectId(entity));
246                     }
247
248                     QueryLogger.logSelectCount(
249                         query.getLoggingLevel(),
250                         elements.size(),
251                         System.currentTimeMillis() - t1);
252
253                 }
254                 finally {
255                     it.close();
256                 }
257             }
258             catch (CayenneException e) {
259                 throw new CayenneRuntimeException(
260                     "Error performing query.",
261                     Util.unwindException(e));
262             }
263
264             unfetchedObjects =
265                 (resolvesFirstPage()) ? elements.size() - pageSize : elements.size();
266         }
267     }
268
269     /**
270      * Will resolve all unread objects.
271      */

272     public void resolveAll() {
273         resolveInterval(0, size());
274     }
275
276     /**
277      * @param object
278      * @return <code>true</code> if the object corresponds to an unresolved
279      * state and doesn require a fetch before being returned to the user.
280      */

281     private boolean isUnresolved(Object JavaDoc object) {
282         if (object instanceof DataObject) {
283             return false;
284         }
285
286         if (internalQuery.isFetchingDataRows()) {
287             // both unresolved and resolved objects are represented
288
// as Maps, so no instanceof check is possible.
289
Map JavaDoc map = (Map JavaDoc) object;
290             int size = map.size();
291             return size < rowWidth;
292         }
293
294         return true;
295     }
296
297     /**
298      * Checks that an object is of the same type as
299      * the rest of objects (DataObject or DataRows depending on the query type).
300      */

301     private void validateListObject(Object JavaDoc object) throws IllegalArgumentException JavaDoc {
302
303         // I am not sure if such a check makes sense???
304

305         if (internalQuery.isFetchingDataRows()) {
306             if (!(object instanceof Map JavaDoc)) {
307                 throw new IllegalArgumentException JavaDoc("Only Map objects can be stored in this list.");
308             }
309         }
310         else {
311             if (!(object instanceof DataObject)) {
312                 throw new IllegalArgumentException JavaDoc("Only DataObjects can be stored in this list.");
313             }
314         }
315     }
316
317     /**
318      * Resolves a sublist of objects starting at <code>fromIndex</code>
319      * up to but not including <code>toIndex</code>. Internally performs
320      * bound checking and trims indexes accordingly.
321      */

322     protected void resolveInterval(int fromIndex, int toIndex) {
323         if (fromIndex >= toIndex) {
324             return;
325         }
326
327         synchronized (elements) {
328             if (elements.size() == 0) {
329                 return;
330             }
331
332             // perform bound checking
333
if (fromIndex < 0) {
334                 fromIndex = 0;
335             }
336
337             if (toIndex > elements.size()) {
338                 toIndex = elements.size();
339             }
340
341             List JavaDoc quals = new ArrayList JavaDoc(pageSize);
342             List JavaDoc ids = new ArrayList JavaDoc(pageSize);
343             for (int i = fromIndex; i < toIndex; i++) {
344                 Object JavaDoc obj = elements.get(i);
345                 if (isUnresolved(obj)) {
346                     ids.add(obj);
347
348                     Map JavaDoc map = (Map JavaDoc) obj;
349                     if (map.isEmpty()) {
350                         throw new CayenneRuntimeException("Empty id map at index " + i);
351                     }
352
353                     quals.add(ExpressionFactory.matchAllDbExp(map, Expression.EQUAL_TO));
354                 }
355             }
356
357             int qualsSize = quals.size();
358             if (qualsSize == 0) {
359                 return;
360             }
361
362             // fetch the range of objects in fetchSize chunks
363
boolean fetchesDataRows = internalQuery.isFetchingDataRows();
364             List JavaDoc objects = new ArrayList JavaDoc(qualsSize);
365             int fetchEnd = Math.min(qualsSize, maxFetchSize);
366             int fetchBegin = 0;
367             while (fetchBegin < qualsSize) {
368                 SelectQuery query =
369                     new SelectQuery(
370                         rootEntity,
371                         ExpressionFactory.joinExp(
372                             Expression.OR,
373                             quals.subList(fetchBegin, fetchEnd)));
374
375                 query.setFetchingDataRows(fetchesDataRows);
376
377                 if (!query.isFetchingDataRows()) {
378                     query.addPrefetches(internalQuery.getPrefetches());
379                 }
380
381                 objects.addAll(dataContext.performQuery(query));
382                 fetchBegin = fetchEnd;
383                 fetchEnd += Math.min(maxFetchSize, qualsSize - fetchEnd);
384             }
385
386             // sanity check - database data may have changed
387
if (objects.size() < ids.size()) {
388                 // find missing ids
389
StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
390                 buf.append("Some ObjectIds are missing from the database. ");
391                 buf.append("Expected ").append(ids.size()).append(", fetched ").append(
392                     objects.size());
393
394                 Iterator JavaDoc idsIt = ids.iterator();
395                 boolean first = true;
396                 while (idsIt.hasNext()) {
397                     boolean found = false;
398                     Object JavaDoc id = idsIt.next();
399                     Iterator JavaDoc oIt = objects.iterator();
400                     while (oIt.hasNext()) {
401                         if (((DataObject) oIt.next())
402                             .getObjectId()
403                             .getIdSnapshot()
404                             .equals(id)) {
405                             found = true;
406                             break;
407                         }
408                     }
409
410                     if (!found) {
411                         if (first) {
412                             first = false;
413                         }
414                         else {
415                             buf.append(", ");
416                         }
417
418                         buf.append(id.toString());
419                     }
420                 }
421
422                 throw new CayenneRuntimeException(buf.toString());
423             }
424             else if (objects.size() > ids.size()) {
425                 throw new CayenneRuntimeException(
426                     "Expected " + ids.size() + " objects, retrieved " + objects.size());
427             }
428
429             // replace ids in the list with objects
430
Iterator JavaDoc it = objects.iterator();
431             while (it.hasNext()) {
432                 helper.updateWithResolvedObjectInRange(it.next(), fromIndex, toIndex);
433             }
434
435             unfetchedObjects -= objects.size();
436         }
437     }
438
439     /**
440      * Returns zero-based index of the virtual "page" for a given
441      * array element index.
442      */

443     public int pageIndex(int elementIndex) {
444         if (elementIndex < 0 || elementIndex > size()) {
445             throw new IndexOutOfBoundsException JavaDoc("Index: " + elementIndex);
446         }
447
448         if (pageSize <= 0 || elementIndex < 0) {
449             return -1;
450         }
451
452         return elementIndex / pageSize;
453     }
454
455     /**
456      * Get the upper bound on the number of records to resolve in one round
457      * trip to the database. This setting governs the size/complexity of
458      * the where clause generated to retrieve the next page of records.
459      * If the fetch size is less than the page size, then multiple fetches
460      * will be made to resolve a page.
461      * @return int
462      */

463     public int getMaxFetchSize() {
464         return maxFetchSize;
465     }
466
467     public void setMaxFetchSize(int fetchSize) {
468         this.maxFetchSize = fetchSize;
469     }
470
471     /**
472      * Returns the dataContext.
473      * @return DataContext
474      */

475     public DataContext getDataContext() {
476         return dataContext;
477     }
478
479     /**
480      * Returns the pageSize.
481      * @return int
482      */

483     public int getPageSize() {
484         return pageSize;
485     }
486
487     /**
488      * Returns a list iterator for this list. DataObjects are resolved a page
489      * (according to getPageSize()) at a time as necessary - when retrieved
490      * with next() or previous().
491      */

492     public ListIterator JavaDoc listIterator() {
493         return new IncrementalListIterator(0);
494     }
495
496     /**
497      * Returns a list iterator of the elements in this list (in proper
498      * sequence), starting at the specified position in this list. The
499      * specified index indicates the first element that would be returned
500      * by an initial call to the next method. An initial call to the
501      * previous method would return the element with the specified index
502      * minus one.
503      *
504      * DataObjects are resolved a page at a time (according to getPageSize())
505      * as necessary - when retrieved with next() or previous().
506      */

507     public ListIterator JavaDoc listIterator(int index) {
508         if (index < 0 || index > size()) {
509             throw new IndexOutOfBoundsException JavaDoc("Index: " + index);
510         }
511
512         return new IncrementalListIterator(index);
513     }
514
515     /**
516      * Return an iterator for this list. DataObjects are resolved a page
517      * (according to getPageSize()) at a time as necessary - when retrieved
518      * with next().
519      */

520     public Iterator JavaDoc iterator() {
521         // by virtue of get(index)'s implementation, resolution of ids into
522
// objects will occur on pageSize boundaries as necessary.
523
return new Iterator JavaDoc() {
524             int listIndex = 0;
525
526             public boolean hasNext() {
527                 return (listIndex < elements.size());
528             }
529
530             public Object JavaDoc next() {
531                 if (listIndex >= elements.size())
532                     throw new NoSuchElementException JavaDoc("no more elements");
533
534                 return get(listIndex++);
535             }
536
537             public void remove() {
538                 throw new UnsupportedOperationException JavaDoc("remove not supported.");
539             }
540         };
541     }
542
543     /**
544      * @see java.util.List#add(int, Object)
545      */

546     public void add(int index, Object JavaDoc element) {
547         validateListObject(element);
548
549         synchronized (elements) {
550             elements.add(index, element);
551         }
552     }
553
554     /**
555      * @see java.util.Collection#add(Object)
556      */

557     public boolean add(Object JavaDoc o) {
558         validateListObject(o);
559
560         synchronized (elements) {
561             return elements.add(o);
562         }
563     }
564
565     /**
566      * @see java.util.Collection#addAll(Collection)
567      */

568     public boolean addAll(Collection JavaDoc c) {
569         synchronized (elements) {
570             return elements.addAll(c);
571         }
572     }
573
574     /**
575      * @see java.util.List#addAll(int, Collection)
576      */

577     public boolean addAll(int index, Collection JavaDoc c) {
578         synchronized (elements) {
579             return elements.addAll(index, c);
580         }
581     }
582
583     /**
584      * @see java.util.Collection#clear()
585      */

586     public void clear() {
587         synchronized (elements) {
588             elements.clear();
589         }
590     }
591
592     /**
593      * @see java.util.Collection#contains(Object)
594      */

595     public boolean contains(Object JavaDoc o) {
596         synchronized (elements) {
597             return elements.contains(o);
598         }
599     }
600
601     /**
602      * @see java.util.Collection#containsAll(Collection)
603      */

604     public boolean containsAll(Collection JavaDoc c) {
605         synchronized (elements) {
606             return elements.containsAll(c);
607         }
608     }
609
610     /**
611      * @see java.util.List#get(int)
612      */

613     public Object JavaDoc get(int index) {
614         synchronized (elements) {
615             Object JavaDoc o = elements.get(index);
616
617             if (isUnresolved(o)) {
618                 // read this page
619
int pageStart = pageIndex(index) * pageSize;
620                 resolveInterval(pageStart, pageStart + pageSize);
621
622                 return elements.get(index);
623             }
624             else {
625                 return o;
626             }
627         }
628     }
629
630     /**
631      * @see java.util.List#indexOf(Object)
632      */

633     public int indexOf(Object JavaDoc o) {
634         return helper.indexOfObject(o);
635     }
636
637     /**
638      * @see java.util.Collection#isEmpty()
639      */

640     public boolean isEmpty() {
641         synchronized (elements) {
642             return elements.isEmpty();
643         }
644     }
645
646     /**
647      * @see java.util.List#lastIndexOf(Object)
648      */

649     public int lastIndexOf(Object JavaDoc o) {
650         return helper.lastIndexOfObject(o);
651     }
652
653     /**
654      * @see java.util.List#remove(int)
655      */

656     public Object JavaDoc remove(int index) {
657         synchronized (elements) {
658             return elements.remove(index);
659         }
660     }
661
662     /**
663      * @see java.util.Collection#remove(Object)
664      */

665     public boolean remove(Object JavaDoc o) {
666         synchronized (elements) {
667             return elements.remove(o);
668         }
669     }
670
671     /**
672      * @see java.util.Collection#removeAll(Collection)
673      */

674     public boolean removeAll(Collection JavaDoc c) {
675         synchronized (elements) {
676             return elements.removeAll(c);
677         }
678     }
679
680     /**
681      * @see java.util.Collection#retainAll(Collection)
682      */

683     public boolean retainAll(Collection JavaDoc c) {
684         synchronized (elements) {
685             return elements.retainAll(c);
686         }
687     }
688
689     /**
690      * @see java.util.List#set(int, Object)
691      */

692     public Object JavaDoc set(int index, Object JavaDoc element) {
693         validateListObject(element);
694
695         synchronized (elements) {
696             return elements.set(index, element);
697         }
698     }
699
700     /**
701      * @see java.util.Collection#size()
702      */

703     public int size() {
704         synchronized (elements) {
705             return elements.size();
706         }
707     }
708
709     public List JavaDoc subList(int fromIndex, int toIndex) {
710         synchronized (elements) {
711             resolveInterval(fromIndex, toIndex);
712             return elements.subList(fromIndex, toIndex);
713         }
714     }
715
716     public Object JavaDoc[] toArray() {
717         resolveAll();
718
719         return elements.toArray();
720     }
721
722     /**
723      * @see java.util.Collection#toArray(Object[])
724      */

725     public Object JavaDoc[] toArray(Object JavaDoc[] a) {
726         resolveAll();
727
728         return elements.toArray(a);
729     }
730
731     /**
732      * Returns a total number of objects that are not resolved yet.
733      */

734     public int getUnfetchedObjects() {
735         return unfetchedObjects;
736     }
737
738     abstract class IncrementalListHelper {
739         int indexOfObject(Object JavaDoc object) {
740             if (incorrectObjectType(object)) {
741                 return -1;
742             }
743
744             synchronized (elements) {
745                 for (int i = 0; i < elements.size(); i++) {
746                     if (objectsAreEqual(object, elements.get(i))) {
747                         return i;
748                     }
749                 }
750             }
751             return -1;
752         }
753
754         int lastIndexOfObject(Object JavaDoc object) {
755             if (incorrectObjectType(object)) {
756                 return -1;
757             }
758
759             synchronized (elements) {
760                 for (int i = elements.size() - 1; i >= 0; i--) {
761                     if (objectsAreEqual(object, elements.get(i))) {
762                         return i;
763                     }
764                 }
765             }
766
767             return -1;
768         }
769
770         void updateWithResolvedObjectInRange(Object JavaDoc object, int from, int to) {
771             boolean found = false;
772
773             synchronized (elements) {
774
775                 for (int i = from; i < to; i++) {
776                     if (replacesObject(object, elements.get(i))) {
777                         elements.set(i, object);
778                         found = true;
779                         break;
780                     }
781                 }
782             }
783
784             if (!found) {
785                 throw new CayenneRuntimeException("Can't find id for " + object);
786             }
787         }
788
789         abstract boolean incorrectObjectType(Object JavaDoc object);
790
791         abstract boolean objectsAreEqual(Object JavaDoc object, Object JavaDoc objectInTheList);
792
793         abstract boolean replacesObject(Object JavaDoc object, Object JavaDoc objectInTheList);
794     }
795
796     class DataObjectListHelper extends IncrementalListHelper {
797         boolean incorrectObjectType(Object JavaDoc object) {
798             if (!(object instanceof DataObject)) {
799
800                 return true;
801             }
802
803             DataObject dataObj = (DataObject) object;
804             if (dataObj.getDataContext() != dataContext) {
805                 return true;
806             }
807
808             if (!dataObj
809                 .getObjectId()
810                 .getObjectClass()
811                 .getName()
812                 .equals(rootEntity.getClassName())) {
813                 return true;
814             }
815
816             return false;
817         }
818
819         boolean objectsAreEqual(Object JavaDoc object, Object JavaDoc objectInTheList) {
820
821             if (objectInTheList instanceof DataObject) {
822                 // due to object uniquing this should be sufficient
823
return object == objectInTheList;
824             }
825             else {
826                 return ((DataObject) object).getObjectId().getIdSnapshot().equals(
827                     objectInTheList);
828             }
829         }
830
831         boolean replacesObject(Object JavaDoc object, Object JavaDoc objectInTheList) {
832             if (objectInTheList instanceof DataObject) {
833                 return false;
834             }
835
836             DataObject dataObject = (DataObject) object;
837             return dataObject.getObjectId().getIdSnapshot().equals(objectInTheList);
838         }
839     }
840
841     class DataRowListHelper extends IncrementalListHelper {
842         boolean incorrectObjectType(Object JavaDoc object) {
843             if (!(object instanceof Map JavaDoc)) {
844                 return true;
845             }
846
847             Map JavaDoc map = (Map JavaDoc) object;
848             return map.size() != rowWidth;
849         }
850
851         boolean objectsAreEqual(Object JavaDoc object, Object JavaDoc objectInTheList) {
852             if (object == null && objectInTheList == null) {
853                 return true;
854             }
855
856             if (object != null && objectInTheList != null) {
857
858                 Map JavaDoc id = (Map JavaDoc) objectInTheList;
859                 Map JavaDoc map = (Map JavaDoc) object;
860
861                 // id must be a subset of this map
862
Iterator JavaDoc it = id.keySet().iterator();
863
864                 while (it.hasNext()) {
865                     Object JavaDoc key = it.next();
866                     Object JavaDoc value = id.get(key);
867                     if (!Util.nullSafeEquals(value, map.get(key))) {
868                         return false;
869                     }
870                 }
871
872                 return true;
873             }
874
875             return false;
876         }
877
878         boolean replacesObject(Object JavaDoc object, Object JavaDoc objectInTheList) {
879
880             Map JavaDoc id = (Map JavaDoc) objectInTheList;
881             if (id.size() == rowWidth) {
882                 return false;
883             }
884
885             // id must be a subset of this map
886
Map JavaDoc map = (Map JavaDoc) object;
887             Iterator JavaDoc it = id.keySet().iterator();
888
889             while (it.hasNext()) {
890                 Object JavaDoc key = it.next();
891                 Object JavaDoc value = id.get(key);
892                 if (!Util.nullSafeEquals(value, map.get(key))) {
893                     return false;
894                 }
895             }
896
897             return true;
898         }
899     }
900
901     class IncrementalListIterator implements ListIterator JavaDoc {
902         // by virtue of get(index)'s implementation, resolution of ids into
903
// objects will occur on pageSize boundaries as necessary.
904

905         int listIndex;
906
907         public IncrementalListIterator(int startIndex) {
908             this.listIndex = startIndex;
909         }
910
911         public void add(Object JavaDoc o) {
912             throw new UnsupportedOperationException JavaDoc("add operation not supported");
913         }
914
915         public boolean hasNext() {
916             return (listIndex < elements.size());
917         }
918
919         public boolean hasPrevious() {
920             return (listIndex > 0);
921         }
922
923         public Object JavaDoc next() {
924             if (listIndex >= elements.size())
925                 throw new NoSuchElementException JavaDoc("at the end of the list");
926
927             return get(listIndex++);
928         }
929
930         public int nextIndex() {
931             return listIndex;
932         }
933
934         public Object JavaDoc previous() {
935             if (listIndex < 1)
936                 throw new NoSuchElementException JavaDoc("at the beginning of the list");
937
938             return get(--listIndex);
939         }
940
941         public int previousIndex() {
942             return (listIndex - 1);
943         }
944
945         public void remove() {
946             throw new UnsupportedOperationException JavaDoc("remove operation not supported");
947         }
948
949         public void set(Object JavaDoc o) {
950             throw new UnsupportedOperationException JavaDoc("set operation not supported");
951         }
952     }
953 }
954
Popular Tags