KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > access > util > DistinctResultIterator


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.util;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.Set JavaDoc;
29
30 import org.apache.cayenne.CayenneException;
31 import org.apache.cayenne.access.ResultIterator;
32 import org.apache.cayenne.map.DbAttribute;
33 import org.apache.cayenne.map.DbEntity;
34
35 /**
36  * A ResultIterator that does in-memory filtering of rows to return only distinct rows.
37  * Distinct comparison is done by comparing ObjectIds created from each row. Internally
38  * DistinctResultIterator wraps another ResultIterator that provides the actual rows. The
39  * current limitation is that once switched to reading ids instead of rows (i.e. when
40  * "nextObjectId()" is called for the first time), it can't be used to read data rows
41  * again. This is pretty sensible for most things in Cayenne.
42  *
43  * @since 1.2
44  * @author Andrus Adamchik
45  */

46 public class DistinctResultIterator implements ResultIterator {
47
48     protected ResultIterator wrappedIterator;
49     protected Set JavaDoc fetchedIds;
50     protected Map JavaDoc nextDataRow;
51     protected DbEntity defaultEntity;
52     protected boolean compareFullRows;
53
54     protected boolean readingIds;
55
56     /**
57      * Creates new DistinctResultIterator wrapping another ResultIterator.
58      *
59      * @param wrappedIterator
60      * @param defaultEntity an entity needed to build ObjectIds for distinct comparison.
61      */

62     public DistinctResultIterator(ResultIterator wrappedIterator, DbEntity defaultEntity,
63             boolean compareFullRows) throws CayenneException {
64         if (wrappedIterator == null) {
65             throw new CayenneException("Null wrapped iterator.");
66         }
67
68         if (defaultEntity == null) {
69             throw new CayenneException("Null defaultEntity.");
70         }
71
72         this.wrappedIterator = wrappedIterator;
73         this.defaultEntity = defaultEntity;
74         this.fetchedIds = new HashSet JavaDoc();
75         this.compareFullRows = compareFullRows;
76
77         checkNextRow();
78     }
79
80     /**
81      * CLoses underlying ResultIterator.
82      */

83     public void close() throws CayenneException {
84         wrappedIterator.close();
85     }
86
87     /**
88      * Returns all data rows.
89      */

90     public List JavaDoc dataRows(boolean close) throws CayenneException {
91         List JavaDoc list = new ArrayList JavaDoc();
92
93         try {
94             while (this.hasNextRow()) {
95                 list.add(this.nextDataRow());
96             }
97             return list;
98         }
99         finally {
100             if (close) {
101                 this.close();
102             }
103         }
104     }
105
106     public int getDataRowWidth() {
107         return wrappedIterator.getDataRowWidth();
108     }
109
110     public boolean hasNextRow() throws CayenneException {
111         return nextDataRow != null;
112     }
113
114     public Map JavaDoc nextDataRow() throws CayenneException {
115         if (!hasNextRow()) {
116             throw new CayenneException(
117                     "An attempt to read uninitialized row or past the end of the iterator.");
118         }
119
120         Map JavaDoc row = nextDataRow;
121         checkNextRow();
122         return row;
123     }
124
125     /**
126      * Returns a Map for the next ObjectId. After calling this method, calls to
127      * "nextDataRow()" will result in exceptions.
128      */

129     public Map JavaDoc nextObjectId(DbEntity entity) throws CayenneException {
130         if (!hasNextRow()) {
131             throw new CayenneException(
132                     "An attempt to read uninitialized row or past the end of the iterator.");
133         }
134
135         Map JavaDoc row = nextDataRow;
136
137         // if we were previously reading full rows, we need to strip extra keys...
138
if (!readingIds) {
139             Iterator JavaDoc it = row.entrySet().iterator();
140             while (it.hasNext()) {
141                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
142                 String JavaDoc name = (String JavaDoc) entry.getKey();
143                 DbAttribute attribute = (DbAttribute) entity.getAttribute(name);
144                 if (attribute == null || !attribute.isPrimaryKey()) {
145                     it.remove();
146                 }
147             }
148         }
149
150         checkNextId(entity);
151         return row;
152     }
153
154     public void skipDataRow() throws CayenneException {
155         if (!hasNextRow()) {
156             throw new CayenneException(
157                     "An attempt to read uninitialized row or past the end of the iterator.");
158         }
159
160         if (readingIds) {
161             checkNextId(defaultEntity);
162         }
163         else {
164             checkNextRow();
165         }
166     }
167
168     void checkNextRow() throws CayenneException {
169         if (readingIds) {
170             throw new CayenneException(
171                     "Can't go back from reading ObjectIds to reading rows.");
172         }
173
174         if (this.compareFullRows) {
175             checkNextUniqueRow();
176         }
177         else {
178             checkNextRowWithUniqueId();
179         }
180     }
181
182     void checkNextUniqueRow() throws CayenneException {
183
184         nextDataRow = null;
185         while (wrappedIterator.hasNextRow()) {
186             Map JavaDoc next = wrappedIterator.nextDataRow();
187
188             if (fetchedIds.add(next)) {
189                 this.nextDataRow = next;
190                 break;
191             }
192         }
193     }
194
195     void checkNextRowWithUniqueId() throws CayenneException {
196
197         nextDataRow = null;
198         while (wrappedIterator.hasNextRow()) {
199             Map JavaDoc next = wrappedIterator.nextDataRow();
200
201             // create id map...
202
// TODO: this can be optimized by creating an array with id keys
203
// to avoid iterating over default entity attributes...
204

205             Map JavaDoc id = new HashMap JavaDoc();
206             Iterator JavaDoc it = defaultEntity.getPrimaryKey().iterator();
207             while (it.hasNext()) {
208                 DbAttribute pk = (DbAttribute) it.next();
209                 id.put(pk.getName(), next.get(pk.getName()));
210             }
211
212             if (fetchedIds.add(id)) {
213                 this.nextDataRow = next;
214                 break;
215             }
216         }
217     }
218
219     void checkNextId(DbEntity entity) throws CayenneException {
220         if (entity == null) {
221             throw new CayenneException("Null DbEntity, can't create id.");
222         }
223
224         this.readingIds = true;
225         this.nextDataRow = null;
226
227         while (wrappedIterator.hasNextRow()) {
228             Map JavaDoc next = wrappedIterator.nextObjectId(entity);
229
230             // if we are reading ids, we ignore "compareFullRows" setting
231
if (fetchedIds.add(next)) {
232                 this.nextDataRow = next;
233                 break;
234             }
235         }
236     }
237 }
238
Popular Tags