KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.access;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.ListIterator JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.NoSuchElementException JavaDoc;
30
31 import org.apache.cayenne.CayenneException;
32 import org.apache.cayenne.CayenneRuntimeException;
33 import org.apache.cayenne.Persistent;
34 import org.apache.cayenne.exp.Expression;
35 import org.apache.cayenne.exp.ExpressionFactory;
36 import org.apache.cayenne.map.DbAttribute;
37 import org.apache.cayenne.map.DbEntity;
38 import org.apache.cayenne.map.ObjEntity;
39 import org.apache.cayenne.query.Query;
40 import org.apache.cayenne.query.QueryMetadata;
41 import org.apache.cayenne.query.SelectQuery;
42 import org.apache.cayenne.util.Util;
43
44 /**
45  * A synchronized list that serves as a container of DataObjects. It is returned when a
46  * paged query is performed by DataContext. On creation, only the first "page" is fully
47  * resolved, for the rest of the objects only their ObjectIds are read. Pages following
48  * the first page are resolved on demand only. On access to an element, the list would
49  * ensure that this element as well as all its siblings on the same page are fully
50  * resolved.
51  * <p>
52  * The list can hold DataRows or DataObjects. Attempts to add any other object types will
53  * result in an exception.
54  * </p>
55  * <p>
56  * Performance note: certain operations like <code>toArray</code> would trigger full
57  * list fetch.
58  * </p>
59  *
60  * @author Andrus Adamchik
61  */

62 public class IncrementalFaultList implements List JavaDoc {
63
64     protected int pageSize;
65     protected List JavaDoc elements;
66     protected DataContext dataContext;
67     protected ObjEntity rootEntity;
68     protected SelectQuery internalQuery;
69     protected int unfetchedObjects;
70
71     /**
72      * Stores a hint allowing to distinguish data rows from unfetched ids when the query
73      * fetches data rows.
74      */

75     protected int rowWidth;
76
77     private IncrementalListHelper helper;
78
79     /**
80      * Defines the upper limit on the size of fetches. This is needed to avoid where
81      * clause size limitations.
82      */

83     protected int maxFetchSize = 10000;
84
85     // Don't confuse this with the JDBC ResultSet fetch size setting - this controls
86
// the where clause generation that is necessary to fetch specific records a page
87
// at a time. Some JDBC Drivers/Databases may have limits on statement length
88
// or complexity of the where clause - e.g., PostgreSQL having a default limit of
89
// 10,000 nested expressions.
90

91     /**
92      * Creates a new list copying settings from another list. Elements WILL NOT be copied
93      * or fetched.
94      */

95     public IncrementalFaultList(IncrementalFaultList list) {
96         this.pageSize = list.pageSize;
97         this.internalQuery = list.internalQuery;
98         this.dataContext = list.dataContext;
99         this.rootEntity = list.rootEntity;
100         this.maxFetchSize = list.maxFetchSize;
101         this.rowWidth = list.rowWidth;
102         this.helper = list.helper;
103         elements = Collections.synchronizedList(new ArrayList JavaDoc());
104     }
105
106     /**
107      * Creates a new IncrementalFaultList using a given DataContext and query.
108      *
109      * @param dataContext DataContext used by IncrementalFaultList to fill itself with
110      * objects.
111      * @param query Main query used to retrieve data. Must have "pageSize" property set to
112      * a value greater than zero.
113      */

114     public IncrementalFaultList(DataContext dataContext, Query query) {
115         QueryMetadata metadata = query.getMetaData(dataContext.getEntityResolver());
116         if (metadata.getPageSize() <= 0) {
117             throw new CayenneRuntimeException(
118                     "IncrementalFaultList does not support unpaged queries. Query page size is "
119                             + metadata.getPageSize());
120         }
121
122         this.dataContext = dataContext;
123         this.pageSize = metadata.getPageSize();
124         this.rootEntity = metadata.getObjEntity();
125
126         // create an internal query, it is a partial replica of
127
// the original query and will serve as a value holder for
128
// various parameters
129
this.internalQuery = new SelectQuery(rootEntity);
130         this.internalQuery.setFetchingDataRows(metadata.isFetchingDataRows());
131         this.internalQuery.setResolvingInherited(metadata.isResolvingInherited());
132
133         if (metadata.isFetchingDataRows()) {
134             helper = new DataRowListHelper();
135         }
136         else {
137             helper = new PersistentListHelper();
138         }
139
140         boolean resolvesFirstPage = true;
141
142         if (!metadata.isFetchingDataRows() && (query instanceof SelectQuery)) {
143             SelectQuery select = (SelectQuery) query;
144
145             this.internalQuery.setPrefetchTree(select.getPrefetchTree());
146
147             // optimize SelectQuery:
148
// * just select ID columns - this gives a 5-10x speedup
149
// * strip prefetches as they blow the iterated result, and are actually not
150
// needed
151

152             SelectQuery clone = select.queryWithParameters(Collections.EMPTY_MAP, true);
153             clone.clearPrefetches();
154
155             // I guess this check is redundant, as custom attributes warrant data rows
156
if (!select.isFetchingCustomAttributes()) {
157                 Iterator JavaDoc pk = rootEntity.getDbEntity().getPrimaryKey().iterator();
158                 while (pk.hasNext()) {
159                     DbAttribute attribute = (DbAttribute) pk.next();
160                     clone.addCustomDbAttribute(attribute.getName());
161                 }
162             }
163
164             query = clone;
165             resolvesFirstPage = false;
166         }
167
168         List JavaDoc elementsUnsynced = new ArrayList JavaDoc();
169         fillIn(query, elementsUnsynced, resolvesFirstPage);
170         this.elements = Collections.synchronizedList(elementsUnsynced);
171     }
172
173     /**
174      * @since 1.2
175      */

176     SelectQuery getInternalQuery() {
177         return internalQuery;
178     }
179
180     /**
181      * Performs initialization of the internal list of objects. Only the first page is
182      * fully resolved. For the rest of the list, only ObjectIds are read.
183      *
184      * @deprecated since 3.0 this method is not called and is deprecated in favor of
185      * {@link #fillIn(Query, List)}, as this method performed unneeded
186      * synchronization.
187      * @since 1.0.6
188      */

189     protected void fillIn(Query query) {
190         synchronized (elements) {
191             fillIn(query, elements, true);
192         }
193     }
194
195     /**
196      * Performs initialization of the list of objects. Only the first page is fully
197      * resolved. For the rest of the list, only ObjectIds are read.
198      *
199      * @since 3.0
200      */

201     protected void fillIn(Query query, List JavaDoc elementsList, boolean resolvesFirstPage) {
202         QueryMetadata info = query.getMetaData(dataContext.getEntityResolver());
203         boolean fetchesDataRows = internalQuery.isFetchingDataRows();
204
205         // start fresh
206
elementsList.clear();
207         rowWidth = 0;
208
209         try {
210             int lastResolved = 0;
211             long t1 = System.currentTimeMillis();
212             ResultIterator it = dataContext.performIteratedQuery(query);
213             try {
214
215                 rowWidth = it.getDataRowWidth();
216
217                 // resolve first page if we can
218
if (resolvesFirstPage) {
219                     // read first page completely, the rest as ObjectIds
220
for (int i = 0; i < pageSize && it.hasNextRow(); i++) {
221                         elementsList.add(it.nextDataRow());
222                         lastResolved++;
223                     }
224
225                     // defer DataRows -> Objects conversion till we are completely done.
226
}
227
228                 // continue reading ids
229
DbEntity entity = rootEntity.getDbEntity();
230                 while (it.hasNextRow()) {
231                     elementsList.add(it.nextObjectId(entity));
232                 }
233
234                 QueryLogger.logSelectCount(elementsList.size(), System
235                         .currentTimeMillis()
236                         - t1);
237             }
238             finally {
239                 it.close();
240             }
241
242             // fill in the first page AFTER the iterator was closed, otherwise we may
243
// cause an (unobvious) deadlock due to connection pool exhaustion
244
if (!fetchesDataRows && lastResolved > 0) {
245                 List JavaDoc objects = dataContext.objectsFromDataRows(rootEntity, elementsList
246                         .subList(0, lastResolved), info.isRefreshingObjects(), info
247                         .isResolvingInherited());
248
249                 for (int i = 0; i < lastResolved; i++) {
250                     elementsList.set(i, objects.get(i));
251                 }
252             }
253         }
254         catch (CayenneException e) {
255             throw new CayenneRuntimeException("Error performing query.", Util
256                     .unwindException(e));
257         }
258
259         unfetchedObjects = (resolvesFirstPage)
260                 ? elementsList.size() - pageSize
261                 : elementsList.size();
262     }
263
264     /**
265      * Will resolve all unread objects.
266      */

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

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

296     private void validateListObject(Object JavaDoc object) throws IllegalArgumentException JavaDoc {
297
298         // I am not sure if such a check makes sense???
299

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

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

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

457     public int getMaxFetchSize() {
458         return maxFetchSize;
459     }
460
461     public void setMaxFetchSize(int fetchSize) {
462         this.maxFetchSize = fetchSize;
463     }
464
465     /**
466      * Returns the dataContext.
467      *
468      * @return DataContext
469      */

470     public DataContext getDataContext() {
471         return dataContext;
472     }
473
474     /**
475      * Returns the pageSize.
476      *
477      * @return int
478      */

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

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

500     public ListIterator JavaDoc listIterator(int index) {
501         if (index < 0 || index > size()) {
502             throw new IndexOutOfBoundsException JavaDoc("Index: " + index);
503         }
504
505         return new IncrementalListIterator(index);
506     }
507
508     /**
509      * Return an iterator for this list. DataObjects are resolved a page (according to
510      * getPageSize()) at a time as necessary - when retrieved with next().
511      */

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

539     public void add(int index, Object JavaDoc element) {
540         validateListObject(element);
541
542         synchronized (elements) {
543             elements.add(index, element);
544         }
545     }
546
547     /**
548      * @see java.util.Collection#add(Object)
549      */

550     public boolean add(Object JavaDoc o) {
551         validateListObject(o);
552
553         synchronized (elements) {
554             return elements.add(o);
555         }
556     }
557
558     /**
559      * @see java.util.Collection#addAll(Collection)
560      */

561     public boolean addAll(Collection JavaDoc c) {
562         synchronized (elements) {
563             return elements.addAll(c);
564         }
565     }
566
567     /**
568      * @see java.util.List#addAll(int, Collection)
569      */

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

579     public void clear() {
580         synchronized (elements) {
581             elements.clear();
582         }
583     }
584
585     /**
586      * @see java.util.Collection#contains(Object)
587      */

588     public boolean contains(Object JavaDoc o) {
589         synchronized (elements) {
590             return elements.contains(o);
591         }
592     }
593
594     /**
595      * @see java.util.Collection#containsAll(Collection)
596      */

597     public boolean containsAll(Collection JavaDoc c) {
598         synchronized (elements) {
599             return elements.containsAll(c);
600         }
601     }
602
603     /**
604      * @see java.util.List#get(int)
605      */

606     public Object JavaDoc get(int index) {
607         synchronized (elements) {
608             Object JavaDoc o = elements.get(index);
609
610             if (isUnresolved(o)) {
611                 // read this page
612
int pageStart = pageIndex(index) * pageSize;
613                 resolveInterval(pageStart, pageStart + pageSize);
614
615                 return elements.get(index);
616             }
617             else {
618                 return o;
619             }
620         }
621     }
622
623     /**
624      * @see java.util.List#indexOf(Object)
625      */

626     public int indexOf(Object JavaDoc o) {
627         return helper.indexOfObject(o);
628     }
629
630     /**
631      * @see java.util.Collection#isEmpty()
632      */

633     public boolean isEmpty() {
634         synchronized (elements) {
635             return elements.isEmpty();
636         }
637     }
638
639     /**
640      * @see java.util.List#lastIndexOf(Object)
641      */

642     public int lastIndexOf(Object JavaDoc o) {
643         return helper.lastIndexOfObject(o);
644     }
645
646     /**
647      * @see java.util.List#remove(int)
648      */

649     public Object JavaDoc remove(int index) {
650         synchronized (elements) {
651             return elements.remove(index);
652         }
653     }
654
655     /**
656      * @see java.util.Collection#remove(Object)
657      */

658     public boolean remove(Object JavaDoc o) {
659         synchronized (elements) {
660             return elements.remove(o);
661         }
662     }
663
664     /**
665      * @see java.util.Collection#removeAll(Collection)
666      */

667     public boolean removeAll(Collection JavaDoc c) {
668         synchronized (elements) {
669             return elements.removeAll(c);
670         }
671     }
672
673     /**
674      * @see java.util.Collection#retainAll(Collection)
675      */

676     public boolean retainAll(Collection JavaDoc c) {
677         synchronized (elements) {
678             return elements.retainAll(c);
679         }
680     }
681
682     /**
683      * @see java.util.List#set(int, Object)
684      */

685     public Object JavaDoc set(int index, Object JavaDoc element) {
686         validateListObject(element);
687
688         synchronized (elements) {
689             return elements.set(index, element);
690         }
691     }
692
693     /**
694      * @see java.util.Collection#size()
695      */

696     public int size() {
697         synchronized (elements) {
698             return elements.size();
699         }
700     }
701
702     public List JavaDoc subList(int fromIndex, int toIndex) {
703         synchronized (elements) {
704             resolveInterval(fromIndex, toIndex);
705             return elements.subList(fromIndex, toIndex);
706         }
707     }
708
709     public Object JavaDoc[] toArray() {
710         resolveAll();
711
712         return elements.toArray();
713     }
714
715     /**
716      * @see java.util.Collection#toArray(Object[])
717      */

718     public Object JavaDoc[] toArray(Object JavaDoc[] a) {
719         resolveAll();
720
721         return elements.toArray(a);
722     }
723
724     /**
725      * Returns a total number of objects that are not resolved yet.
726      */

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

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