merged with 1.1
[mir.git] / source / mir / producer / EntityBatchingProducerNode.java
1 /*
2  * Copyright (C) 2001, 2002 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with  any library licensed under the Apache Software License,
22  * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23  * (or with modified versions of the above that use the same license as the above),
24  * and distribute linked combinations including the two.  You must obey the
25  * GNU General Public License in all respects for all of the code used other than
26  * the above mentioned libraries.  If you modify this file, you may extend this
27  * exception to your version of the file, but you are not obligated to do so.
28  * If you do not wish to do so, delete this exception statement from your version.
29  */
30 package mir.producer;
31
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Vector;
37
38 import mir.entity.adapter.EntityAdapterModel;
39 import mir.entity.adapter.EntityIteratorAdapter;
40 import mir.log.LoggerWrapper;
41 import mir.util.ParameterExpander;
42
43 /**
44  * <p>Title: EntityBatchingProducerNode</p>
45  * <p>Description:
46  *     This producer makes it possible to show articles in batches, like on archive
47  *     pages.
48  *
49  *     <emph> The order by clause should lead to a result set in <b>reverse order<b>:
50  *         the first row will be the last entity in the last batch
51  * </p>
52  * <p>Copyright: Copyright (c) 2003</p>
53  * <p>Company: </p>
54  * @author not attributable
55  * @version 1.0
56  */
57
58 public class EntityBatchingProducerNode implements ProducerNode {
59   private String batchInfoKey;
60   private String batchDataKey;
61   private EntityAdapterModel model;
62   private String definition;
63   private String whereClause;
64   private String orderByClause;
65   private String nrEntitiesToSkipExpression;
66   private String nrEntitiesPerBatchExpression;
67   private String minNrEntitiesInFirstBatchExpression;
68   private String nrBatchesToProcessExpression;
69   private ProducerNode batchSubNode;
70   private ProducerNode batchListSubNode;
71
72   public EntityBatchingProducerNode(
73         String aBatchDataKey,
74         String aBatchInfoKey,
75         EntityAdapterModel aModel,
76         String aDefinition,
77         String aWhereClause,
78         String anOrderByClause,
79         String anrEntitiesPerBatchExpression,
80         String aminNrEntitiesInFirstBatchExpression,
81         String anrEntitiesToSkipExpression,
82         String aNrBatchesToProcessExpression,
83         ProducerNode aBatchSubNode,
84         ProducerNode aBatchListSubNode) {
85
86     batchSubNode = aBatchSubNode;
87     batchListSubNode = aBatchListSubNode;
88
89     batchDataKey = aBatchDataKey;
90     batchInfoKey = aBatchInfoKey;
91     model = aModel;
92     definition = aDefinition;
93     whereClause = aWhereClause;
94     orderByClause = anOrderByClause;
95     nrEntitiesToSkipExpression = anrEntitiesToSkipExpression;
96     nrEntitiesPerBatchExpression = anrEntitiesPerBatchExpression;
97     minNrEntitiesInFirstBatchExpression = aminNrEntitiesInFirstBatchExpression;
98     nrBatchesToProcessExpression = aNrBatchesToProcessExpression;
99   }
100
101   protected boolean isAborted(Map aValueMap) {
102     Object producerValue = aValueMap.get(NodedProducer.PRODUCER_KEY);
103     return (
104        (producerValue instanceof NodedProducer) &&
105       ((NodedProducer) producerValue).getIsAborted());
106   }
107
108   public void produce(Map aValueMap, String aVerb, LoggerWrapper aLogger) throws ProducerFailure {
109     Iterator browser;
110     int nrEntities;
111     int nrBatchesAfterFirst;
112     int nrEntitiesInFirstBatch;
113     int nrBatchesToProcess;
114     List batchesData;
115     int i;
116     int position;
117     Map batchData;
118     String expandedWhereClause;
119     String expandedOrderByClause;
120
121     List batchLocations;
122     BatchLocation location;
123
124     int nrEntitiesToSkip;
125     int nrEntitiesPerBatch;
126     int minNrEntitiesInFirstBatch;
127
128     try {
129       nrBatchesToProcess = ParameterExpander.evaluateIntegerExpressionWithDefault( aValueMap, nrBatchesToProcessExpression, -1 );
130
131       expandedWhereClause = ParameterExpander.expandExpression( aValueMap, whereClause );
132       expandedOrderByClause = ParameterExpander.expandExpression( aValueMap, orderByClause );
133
134       nrEntitiesToSkip = ParameterExpander.evaluateIntegerExpression( aValueMap, nrEntitiesToSkipExpression);
135       nrEntitiesPerBatch = ParameterExpander.evaluateIntegerExpression( aValueMap, nrEntitiesPerBatchExpression);
136       minNrEntitiesInFirstBatch = ParameterExpander.evaluateIntegerExpression( aValueMap, minNrEntitiesInFirstBatchExpression);
137
138       batchesData = new Vector();
139       batchLocations = new Vector();
140
141       nrEntities = model.getMappingForName(definition).getStorage().getSize(expandedWhereClause)-nrEntitiesToSkip;
142       nrEntitiesInFirstBatch = nrEntities % nrEntitiesPerBatch;
143       while (nrEntitiesInFirstBatch<minNrEntitiesInFirstBatch && nrEntities-nrEntitiesInFirstBatch>=nrEntitiesPerBatch)
144         nrEntitiesInFirstBatch = nrEntitiesInFirstBatch + nrEntitiesPerBatch;
145       nrBatchesAfterFirst = (nrEntities-nrEntitiesInFirstBatch)/nrEntitiesPerBatch;
146
147       batchLocations.add(new BatchLocation(nrBatchesAfterFirst*nrEntitiesPerBatch, nrEntitiesInFirstBatch));
148       batchData = new HashMap();
149       batchData.put("identifier", "");
150       batchData.put("index", new Integer(nrBatchesAfterFirst+1));
151       batchData.put("size", new Integer(nrEntitiesInFirstBatch));
152       batchesData.add(batchData);
153
154       for (i=0; i<nrBatchesAfterFirst; i++) {
155         batchLocations.add(1, new BatchLocation(i*nrEntitiesPerBatch, nrEntitiesPerBatch));
156         batchData = new HashMap();
157         batchData.put("identifier", new Integer(i));
158         batchData.put("index", new Integer(i+1));
159         batchData.put("size", new Integer(nrEntitiesPerBatch));
160         batchesData.add(1, batchData);
161       }
162
163       batchData = new HashMap();
164       ParameterExpander.setValueForKey(aValueMap, batchInfoKey, batchData);
165       batchData.put("all", batchesData);
166       batchData.put("first", batchesData.get(0));
167       batchData.put("last", batchesData.get(batchesData.size()-1));
168       batchData.put("count", new Integer(batchesData.size()));
169
170       if (batchListSubNode!=null && (!isAborted(aValueMap))) {
171         batchListSubNode.produce(aValueMap, aVerb, aLogger);
172       }
173
174       if (nrBatchesToProcess<0 || nrBatchesToProcess>nrBatchesAfterFirst+1) {
175         nrBatchesToProcess = nrBatchesAfterFirst+1;
176       }
177
178       if (batchSubNode!=null) {
179         for (i=0; i<nrBatchesToProcess && !isAborted(aValueMap); i++) {
180           location = (BatchLocation) batchLocations.get(i);
181
182           batchData.put("current", batchesData.get(i));
183           if (i>0)
184             batchData.put("previous", batchesData.get(i-1));
185           else
186             batchData.put("previous", null);
187
188           if (i<batchesData.size()-1)
189             batchData.put("next", batchesData.get(i+1));
190           else
191             batchData.put("next", null);
192
193           Iterator j = new EntityIteratorAdapter(expandedWhereClause, expandedOrderByClause,
194                     location.nrEntities, model, definition, location.nrEntities, location.firstEntity);
195           List entities = new Vector();
196
197           while (j.hasNext())
198             entities.add(0, j.next());
199
200           ParameterExpander.setValueForKey(aValueMap, batchDataKey, entities );
201
202           batchSubNode.produce(aValueMap, aVerb, aLogger);
203         }
204       }
205     }
206     catch (Throwable t) {
207       aLogger.error("EntityBatchingProducerNode caused an exception: " + t.getMessage());
208     }
209   };
210
211   private class BatchLocation {
212     int nrEntities;
213     int firstEntity;
214
215     public BatchLocation(int aFirstEntity, int aNrEntities) {
216       firstEntity = aFirstEntity;
217       nrEntities = aNrEntities;
218     }
219   }
220 }
221