KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > bridge > util > HugeNodeListIterator


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10
11 package org.mmbase.bridge.util;
12
13 import org.mmbase.bridge.*;
14 import org.mmbase.storage.search.*;
15 import org.mmbase.cache.CachePolicy;
16 import org.mmbase.util.logging.*;
17
18 import java.util.*;
19
20 /**
21  * Iterates the big result of a query. It avoids using a lot of memory (which you would need if you
22  * get the complete NodeList first), and pollution of the (node) cache. In this current
23  * implementation the Query is 'batched' to avoid reading in all nodes in memory, and the queries
24  * are marked with {@link CachePolicy#NEVER}.
25  *
26  * @author Michiel Meeuwissen
27  * @version $Id: HugeNodeListIterator.java,v 1.5 2005/12/10 14:30:09 michiel Exp $
28  * @since MMBase-1.8
29  */

30
31 public class HugeNodeListIterator implements NodeIterator {
32
33     public static final int DEFAULT_BATCH_SIZE = 10000;
34
35     // log
36
private static final Logger log = Logging.getLoggerInstance(HugeNodeListIterator.class);
37
38     protected NodeIterator nodeIterator;
39     protected Node nextNode;
40     protected Node previousNode;
41
42     protected Query originalQuery;
43     protected int batchSize = DEFAULT_BATCH_SIZE;
44
45     protected int nextIndex = 0;
46
47     /**
48      * Constructor for this Iterator.
49      *
50      * @param query The query which is used as a base for the querie(s) to be executed.
51      * @param batchSize The (approximate) size of the sub-queries, should be a reasonably large
52      * number, like 10000 or so.
53      */

54     public HugeNodeListIterator(Query query, int batchSize) {
55         this.batchSize = batchSize;
56         init(query);
57     }
58
59     /**
60      * Constructor for this Iterator. The 'batchSize' is taken from the query's 'maxnumber'
61      * properties, or, it that is not set, it is defaulted to 10000.
62      *
63      * @param query The query which is used as a base for the querie(s) to be executed.
64      */

65     public HugeNodeListIterator(Query query) {
66         if (query.getMaxNumber() != SearchQuery.DEFAULT_MAX_NUMBER) {
67             batchSize = query.getMaxNumber();
68         } // else leave on default;
69
init(query);
70     }
71
72     /**
73      * Called by constructors only
74      */

75     private void init(Query query) {
76         if (query.getOffset() > 0) {
77             throw new UnsupportedOperationException JavaDoc("Not implemented for queries with offset");
78         }
79         Queries.sortUniquely(query);
80         originalQuery = query;
81         executeNextQuery((Query) originalQuery.clone());
82     }
83
84
85     /**
86      * Executes the given query, taking into account the fact wether it is NodeQuery or not, and
87      * applying the 'batchSize'. The result is available in the 'nodeIterator' member.
88      */

89     protected void executeQuery(Query currentQuery) {
90         currentQuery.setMaxNumber(batchSize);
91         if (log.isDebugEnabled()) {
92             log.trace("Running query: " + currentQuery);
93         }
94         NodeList list;
95         currentQuery.setCachePolicy(CachePolicy.NEVER);
96         if (originalQuery instanceof NodeQuery) {
97             NodeQuery nq = (NodeQuery) currentQuery;
98             list = nq.getNodeManager().getList(nq);
99         } else {
100             list = currentQuery.getCloud().getList(currentQuery);
101         }
102         if (log.isDebugEnabled()) {
103             log.trace("Query result: " + list.size() + " nodes");
104         }
105         nodeIterator = list.nodeIterator();
106     }
107
108     /**
109      * Executes the given query, and prepares to do 'next', so setting 'nextNode' and 'previousNode'.
110      */

111     protected void executeNextQuery(Query q) {
112         executeQuery(q);
113         previousNode = nextNode;
114         if (nodeIterator.hasNext()) {
115             nextNode = nodeIterator.nextNode();
116         } else {
117             nextNode = null;
118         }
119     }
120     /**
121      * Executes the given query, and prepares to do 'previous', so setting 'nextNode' and
122      * 'previousNode', and winds the new nodeIterator to the end.
123      */

124     protected void executePreviousQuery(Query q) {
125         executeQuery(q);
126         nextNode = previousNode;
127         while (nodeIterator.hasNext()) {
128             nodeIterator.next();
129         }
130         if (nodeIterator.hasPrevious()) {
131             previousNode = nodeIterator.nextNode();
132         } else {
133             previousNode = null;
134         }
135     }
136
137
138     /**
139      * {@inheritDoc}
140      */

141     public boolean hasNext() {
142         return nextNode != null;
143     }
144
145     /**
146      * {@inheritDoc}
147      */

148     public boolean hasPrevious() {
149         return previousNode != null;
150     }
151
152
153     /**
154      * {@inheritDoc}
155      */

156     public Object JavaDoc next() {
157         return nextNode();
158     }
159
160     /**
161      * {@inheritDoc}
162      */

163     public Object JavaDoc previous() {
164         return previousNode();
165     }
166     /**
167      * {@inheritDoc}
168      */

169     public int previousIndex() {
170         return nextIndex - 1;
171     }
172     /**
173      * {@inheritDoc}
174      */

175     public int nextIndex() {
176         return nextIndex;
177     }
178
179     /**
180      * Used by nextNode and previousNode. Does a field-by-field compare of two Node objects, on
181      * the fields used to order the nodes.
182      * This is used to determine whether a node comes after or before another - allowing
183      * the node iterator to skip nodes it already 'had'.
184      * @return -1 if node1 is smaller than node 2, 0 if both nodes are equals, and +1 is node 1 is greater than node 2.
185      */

186     protected int compares(Node node1, Node node2) {
187         return Queries.compare(node1, node2, originalQuery.getSortOrders());
188     }
189
190     /**
191      * {@inheritDoc}
192      *
193      * Implementation calculates also the next next Node, and gives back the 'old' next Node, from
194      * now on known as 'previousNode'.
195      */

196     public Node nextNode() {
197         if (nextNode != null) {
198             nextIndex++;
199             previousNode = nextNode;
200             if (nodeIterator.hasNext()) {
201                 nextNode = nodeIterator.nextNode();
202             } else {
203                 Query currentQuery = (Query) originalQuery.clone();
204
205                 // We don't use offset to determin the 'next' batch of query results
206
// because there could have been deletions/insertions.
207
// We use the sort-order to apply a constraint.
208
SortOrder order = (SortOrder) originalQuery.getSortOrders().get(0);
209                 Object JavaDoc value = Queries.getSortOrderFieldValue(previousNode, order);
210                 Constraint cons;
211                 if (order.getDirection() == SortOrder.ORDER_ASCENDING) {
212                     cons = currentQuery.createConstraint(order.getField(), FieldCompareConstraint.GREATER_EQUAL, value);
213                 } else {
214                     cons = currentQuery.createConstraint(order.getField(), FieldCompareConstraint.LESS_EQUAL, value);
215                 }
216                 Queries.addConstraint(currentQuery, cons);
217
218                 executeNextQuery(currentQuery);
219
220                 // perhaps the sort-order did not find a unique result, skip some nodes in that case.
221
// XXX This goes wrong if (which is unlikely) there follow more nodes than 'batchSize'.
222
while(nextNode != null && compares(nextNode, previousNode) <= 0) {
223                     if (nodeIterator.hasNext()) {
224                         nextNode = nodeIterator.nextNode();
225                     } else {
226                         nextNode = null;
227                     }
228                 }
229             }
230
231             return previousNode; // looks odd, but really is wat is meant.
232
} else {
233             throw new NoSuchElementException("No next element");
234         }
235     }
236
237     /**
238      * {@inheritDoc}
239      *
240      * Implementation is analogous to nextNode.
241      */

242     public Node previousNode() {
243         if (previousNode != null) {
244             nextNode = previousNode;
245             nextIndex --;
246             if (nodeIterator.hasPrevious()) {
247                 previousNode = nodeIterator.previousNode();
248             } else {
249                 Query currentQuery = (Query) originalQuery.clone();
250                 SortOrder order = (SortOrder) originalQuery.getSortOrders().get(0);
251                 Object JavaDoc value = Queries.getSortOrderFieldValue(nextNode, order);
252                 Constraint cons;
253                 if (order.getDirection() == SortOrder.ORDER_ASCENDING) {
254                     cons = currentQuery.createConstraint(order.getField(), FieldCompareConstraint.LESS_EQUAL, value);
255                 } else {
256                     cons = currentQuery.createConstraint(order.getField(), FieldCompareConstraint.GREATER_EQUAL, value);
257                 }
258                 Queries.addConstraint(currentQuery, cons);
259                 executePreviousQuery(currentQuery);
260                 while(previousNode != null && compares(nextNode, previousNode) >= 0) {
261                     if (nodeIterator.hasPrevious()) {
262                         previousNode = nodeIterator.previousNode();
263                     } else {
264                         previousNode = null;
265                     }
266                 }
267
268             }
269             return nextNode;
270         } else {
271             throw new NoSuchElementException("No previous element");
272         }
273     }
274
275    /**
276      * @throws UnsupportedOperationException
277      */

278     public void remove() {
279         throw new UnsupportedOperationException JavaDoc("Optional operation 'remove' not implemented");
280     }
281
282     /**
283      * @throws UnsupportedOperationException
284      */

285     public void add(Object JavaDoc o) {
286         throw new UnsupportedOperationException JavaDoc("Optional operation 'add' not implemented");
287     }
288
289     /**
290      * @throws UnsupportedOperationException
291      */

292     public void set(Object JavaDoc o) {
293         throw new UnsupportedOperationException JavaDoc("Optional operation 'set' not implemented");
294     }
295
296     /**
297      * Main only for testing.
298      */

299     public static void main(String JavaDoc[] args) {
300         Cloud cloud = ContextProvider.getDefaultCloudContext().getCloud("mmbase");
301         NodeQuery q = cloud.getNodeManager("object").createQuery();
302         HugeNodeListIterator nodeIterator = new HugeNodeListIterator(q, 20);
303         int i = 0;
304         while (nodeIterator.hasNext()) {
305             Node node = nodeIterator.nextNode();
306             System.out.println("" + (i++) + ": " + node.getNumber() + " " + node.getNodeManager().getName() + " " + node.getFunctionValue("gui", null).toString());
307         }
308
309     }
310
311 }
312
Popular Tags