50207c7a12fc4ec732578695c57081610b2938e7
[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 mainTablePrefix;
63   private List extraTables;
64   private String definition;
65   private String whereClause;
66   private String orderByClause;
67   private String nrEntitiesToSkipExpression;
68   private String nrEntitiesPerBatchExpression;
69   private String minNrEntitiesInFirstBatchExpression;
70   private String nrBatchesToProcessExpression;
71   private ProducerNode batchSubNode;
72   private ProducerNode batchListSubNode;
73
74   public EntityBatchingProducerNode(
75         String aBatchDataKey,
76         String aBatchInfoKey,
77         EntityAdapterModel aModel,
78         String aMainTablePrefix,
79         List   someExtraTables,
80         String aDefinition,
81         String aWhereClause,
82         String anOrderByClause,
83         String anrEntitiesPerBatchExpression,
84         String aminNrEntitiesInFirstBatchExpression,
85         String anrEntitiesToSkipExpression,
86         String aNrBatchesToProcessExpression,
87         ProducerNode aBatchSubNode,
88         ProducerNode aBatchListSubNode) {
89
90     batchSubNode = aBatchSubNode;
91     batchListSubNode = aBatchListSubNode;
92
93     batchDataKey = aBatchDataKey;
94     batchInfoKey = aBatchInfoKey;
95     model = aModel;
96     mainTablePrefix = aMainTablePrefix;
97     extraTables = someExtraTables;
98     definition = aDefinition;
99     whereClause = aWhereClause;
100     orderByClause = anOrderByClause;
101     nrEntitiesToSkipExpression = anrEntitiesToSkipExpression;
102     nrEntitiesPerBatchExpression = anrEntitiesPerBatchExpression;
103     minNrEntitiesInFirstBatchExpression = aminNrEntitiesInFirstBatchExpression;
104     nrBatchesToProcessExpression = aNrBatchesToProcessExpression;
105   }
106  
107   protected boolean isAborted(Map aValueMap) {
108     Object producerValue = aValueMap.get(NodedProducer.PRODUCER_KEY);
109     return (
110        (producerValue instanceof NodedProducer) &&
111       ((NodedProducer) producerValue).getIsAborted());
112   }
113
114   public void produce(Map aValueMap, String aVerb, LoggerWrapper aLogger) throws ProducerFailure {
115     Iterator browser;
116     int nrEntities;
117     int nrBatchesAfterFirst;
118     int nrEntitiesInFirstBatch;
119     int nrBatchesToProcess;
120     List batchesData;
121     int i;
122     int position;
123     Map batchData;
124     String expandedWhereClause;
125     String expandedOrderByClause;
126
127     List batchLocations;
128     BatchLocation location;
129
130     int nrEntitiesToSkip;
131     int nrEntitiesPerBatch;
132     int minNrEntitiesInFirstBatch;
133
134     try {
135       nrBatchesToProcess = ParameterExpander.evaluateIntegerExpressionWithDefault( aValueMap, nrBatchesToProcessExpression, -1 );
136
137       expandedWhereClause = ParameterExpander.expandExpression( aValueMap, whereClause );
138       expandedOrderByClause = ParameterExpander.expandExpression( aValueMap, orderByClause );
139
140       nrEntitiesToSkip = ParameterExpander.evaluateIntegerExpression( aValueMap, nrEntitiesToSkipExpression);
141       nrEntitiesPerBatch = ParameterExpander.evaluateIntegerExpression( aValueMap, nrEntitiesPerBatchExpression);
142       minNrEntitiesInFirstBatch = ParameterExpander.evaluateIntegerExpression( aValueMap, minNrEntitiesInFirstBatchExpression);
143
144       batchesData = new Vector();
145       batchLocations = new Vector();
146
147       nrEntities = model.getMappingForName(definition).getStorage().getSize(mainTablePrefix, extraTables, expandedWhereClause)-nrEntitiesToSkip;
148       nrEntitiesInFirstBatch = nrEntities % nrEntitiesPerBatch;
149       while (nrEntitiesInFirstBatch<minNrEntitiesInFirstBatch && nrEntities-nrEntitiesInFirstBatch>=nrEntitiesPerBatch)
150         nrEntitiesInFirstBatch = nrEntitiesInFirstBatch + nrEntitiesPerBatch;
151       nrBatchesAfterFirst = (nrEntities-nrEntitiesInFirstBatch)/nrEntitiesPerBatch;
152
153       batchLocations.add(new BatchLocation(nrBatchesAfterFirst*nrEntitiesPerBatch, nrEntitiesInFirstBatch));
154       batchData = new HashMap();
155       batchData.put("identifier", "");
156       batchData.put("index", new Integer(nrBatchesAfterFirst+1));
157       batchData.put("size", new Integer(nrEntitiesInFirstBatch));
158       batchesData.add(batchData);
159
160       for (i=0; i<nrBatchesAfterFirst; i++) {
161         batchLocations.add(1, new BatchLocation(i*nrEntitiesPerBatch, nrEntitiesPerBatch));
162         batchData = new HashMap();
163         batchData.put("identifier", new Integer(i));
164         batchData.put("index", new Integer(i+1));
165         batchData.put("size", new Integer(nrEntitiesPerBatch));
166         batchesData.add(1, batchData);
167       }
168
169       batchData = new HashMap();
170       ParameterExpander.setValueForKey(aValueMap, batchInfoKey, batchData);
171       batchData.put("all", batchesData);
172       batchData.put("first", batchesData.get(0));
173       batchData.put("last", batchesData.get(batchesData.size()-1));
174       batchData.put("count", new Integer(batchesData.size()));
175
176       if (batchListSubNode!=null && (!isAborted(aValueMap))) {
177         batchListSubNode.produce(aValueMap, aVerb, aLogger);
178       }
179
180       if (nrBatchesToProcess<0 || nrBatchesToProcess>nrBatchesAfterFirst+1) {
181         nrBatchesToProcess = nrBatchesAfterFirst+1;
182       }
183
184       if (batchSubNode!=null) {
185         for (i=0; i<nrBatchesToProcess && !isAborted(aValueMap); i++) {
186           location = (BatchLocation) batchLocations.get(i);
187
188           batchData.put("current", batchesData.get(i));
189           if (i>0)
190             batchData.put("previous", batchesData.get(i-1));
191           else
192             batchData.put("previous", null);
193
194           if (i<batchesData.size()-1)
195             batchData.put("next", batchesData.get(i+1));
196           else
197             batchData.put("next", null);
198
199           Iterator j = new EntityIteratorAdapter(mainTablePrefix, extraTables, expandedWhereClause, expandedOrderByClause,
200                     location.nrEntities, model, definition, location.nrEntities, location.firstEntity);
201           List entities = new Vector();
202
203           while (j.hasNext())
204             entities.add(0, j.next());
205
206           ParameterExpander.setValueForKey(aValueMap, batchDataKey, entities );
207
208           batchSubNode.produce(aValueMap, aVerb, aLogger);
209         }
210       }
211     }
212     catch (Throwable t) {
213       aLogger.error("EntityBatchingProducerNode caused an exception: " + t.getMessage());
214     }
215   };
216
217   private class BatchLocation {
218     int nrEntities;
219     int firstEntity;
220
221     public BatchLocation(int aFirstEntity, int aNrEntities) {
222       firstEntity = aFirstEntity;
223       nrEntities = aNrEntities;
224     }
225   }
226 }
227