aa0bf413081dbc7759490eea9de25a322bf3c543
[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.ArrayList;
37
38 import mir.entity.adapter.EntityAdapterModel;
39 import mir.entity.adapter.EntityIteratorAdapter;
40 import mir.log.LoggerWrapper;
41 import mir.util.ParameterExpander;
42 import mir.util.StringRoutines;
43
44 /**
45  * <p>Title: EntityBatchingProducerNode</p>
46  * <p>Description:
47  *     This producer makes it possible to show articles in batches, like on archive
48  *     pages.
49  *
50  *     <emph> The order by clause should lead to a result set in <b>reverse order<b>:
51  *         the first row will be the last entity in the last batch
52  * </p>
53  * <p>Copyright: Copyright (c) 2003</p>
54  * <p>Company: </p>
55  * @author not attributable
56  * @version 1.0
57  */
58
59 public class EntityBatchingProducerNode implements ProducerNode {
60   private String batchInfoKey;
61   private String batchDataKey;
62   private EntityAdapterModel model;
63   private String mainTablePrefix;
64   private String extraTables;
65   private String definition;
66   private String whereClause;
67   private String orderByClause;
68   private String nrEntitiesToSkipExpression;
69   private String nrEntitiesPerBatchExpression;
70   private String minNrEntitiesInFirstBatchExpression;
71   private String nrBatchesToProcessExpression;
72   private ProducerNode batchSubNode;
73   private ProducerNode batchListSubNode;
74
75   public EntityBatchingProducerNode(
76         String aBatchDataKey,
77         String aBatchInfoKey,
78         EntityAdapterModel aModel,
79         String aMainTablePrefix,
80         String someExtraTables,
81         String aDefinition,
82         String aWhereClause,
83         String anOrderByClause,
84         String anrEntitiesPerBatchExpression,
85         String aminNrEntitiesInFirstBatchExpression,
86         String anrEntitiesToSkipExpression,
87         String aNrBatchesToProcessExpression,
88         ProducerNode aBatchSubNode,
89         ProducerNode aBatchListSubNode) {
90
91     batchSubNode = aBatchSubNode;
92     batchListSubNode = aBatchListSubNode;
93
94     batchDataKey = aBatchDataKey;
95     batchInfoKey = aBatchInfoKey;
96     model = aModel;
97     mainTablePrefix = aMainTablePrefix;
98     extraTables = someExtraTables;
99     definition = aDefinition;
100     whereClause = aWhereClause;
101     orderByClause = anOrderByClause;
102     nrEntitiesToSkipExpression = anrEntitiesToSkipExpression;
103     nrEntitiesPerBatchExpression = anrEntitiesPerBatchExpression;
104     minNrEntitiesInFirstBatchExpression = aminNrEntitiesInFirstBatchExpression;
105     nrBatchesToProcessExpression = aNrBatchesToProcessExpression;
106   }
107
108   protected boolean isAborted(Map aValueMap) {
109     Object producerValue = aValueMap.get(NodedProducer.PRODUCER_KEY);
110     return (
111        (producerValue instanceof NodedProducer) &&
112       ((NodedProducer) producerValue).getIsAborted());
113   }
114
115   public void produce(Map aValueMap, String aVerb, LoggerWrapper aLogger) throws ProducerFailure {
116     int nrEntities;
117     int nrBatchesAfterFirst;
118     int nrEntitiesInFirstBatch;
119     int nrBatchesToProcess;
120     List batchesData;
121     int i;
122     Map batchData;
123     String expandedWhereClause;
124     String expandedOrderByClause;
125
126     List batchLocations;
127     BatchLocation location;
128
129     int nrEntitiesToSkip;
130     int nrEntitiesPerBatch;
131     int minNrEntitiesInFirstBatch;
132
133     try {
134       nrBatchesToProcess = ParameterExpander.evaluateIntegerExpressionWithDefault( aValueMap, nrBatchesToProcessExpression, -1 );
135
136       expandedWhereClause = ParameterExpander.expandExpression( aValueMap, whereClause );
137       expandedOrderByClause = ParameterExpander.expandExpression( aValueMap, orderByClause );
138
139       nrEntitiesToSkip = ParameterExpander.evaluateIntegerExpression( aValueMap, nrEntitiesToSkipExpression);
140       nrEntitiesPerBatch = ParameterExpander.evaluateIntegerExpression( aValueMap, nrEntitiesPerBatchExpression);
141       minNrEntitiesInFirstBatch = ParameterExpander.evaluateIntegerExpression( aValueMap, minNrEntitiesInFirstBatchExpression);
142       List extraTableList = StringRoutines.splitString(ParameterExpander.expandExpression( aValueMap, extraTables).trim(), ",");
143
144       batchesData = new ArrayList();
145       batchLocations = new ArrayList();
146
147       nrEntities = model.getMappingForName(definition).getStorage().getSize(
148           mainTablePrefix, extraTableList, expandedWhereClause)-nrEntitiesToSkip;
149       nrEntitiesInFirstBatch = nrEntities % nrEntitiesPerBatch;
150       while (nrEntitiesInFirstBatch<minNrEntitiesInFirstBatch && nrEntities-nrEntitiesInFirstBatch>=nrEntitiesPerBatch)
151         nrEntitiesInFirstBatch = nrEntitiesInFirstBatch + nrEntitiesPerBatch;
152       nrBatchesAfterFirst = (nrEntities-nrEntitiesInFirstBatch)/nrEntitiesPerBatch;
153
154       batchLocations.add(new BatchLocation(nrBatchesAfterFirst*nrEntitiesPerBatch, nrEntitiesInFirstBatch));
155       batchData = new HashMap();
156       batchData.put("identifier", "");
157       batchData.put("index", new Integer(nrBatchesAfterFirst+1));
158       batchData.put("size", new Integer(nrEntitiesInFirstBatch));
159       batchesData.add(batchData);
160
161       for (i=0; i<nrBatchesAfterFirst; i++) {
162         batchLocations.add(1, new BatchLocation(i*nrEntitiesPerBatch, nrEntitiesPerBatch));
163         batchData = new HashMap();
164         batchData.put("identifier", new Integer(i));
165         batchData.put("index", new Integer(i+1));
166         batchData.put("size", new Integer(nrEntitiesPerBatch));
167         batchesData.add(1, batchData);
168       }
169
170       batchData = new HashMap();
171       ParameterExpander.setValueForKey(aValueMap, batchInfoKey, batchData);
172       batchData.put("all", batchesData);
173       batchData.put("first", batchesData.get(0));
174       batchData.put("last", batchesData.get(batchesData.size()-1));
175       batchData.put("count", new Integer(batchesData.size()));
176
177       if (batchListSubNode!=null && (!isAborted(aValueMap))) {
178         batchListSubNode.produce(aValueMap, aVerb, aLogger);
179       }
180
181       if (nrBatchesToProcess<0 || nrBatchesToProcess>nrBatchesAfterFirst+1) {
182         nrBatchesToProcess = nrBatchesAfterFirst+1;
183       }
184
185       if (batchSubNode!=null) {
186         for (i=0; i<nrBatchesToProcess && !isAborted(aValueMap); i++) {
187           location = (BatchLocation) batchLocations.get(i);
188
189           batchData.put("current", batchesData.get(i));
190           if (i>0)
191             batchData.put("previous", batchesData.get(i-1));
192           else
193             batchData.put("previous", null);
194
195           if (i<batchesData.size()-1)
196             batchData.put("next", batchesData.get(i+1));
197           else
198             batchData.put("next", null);
199
200           Iterator j = new EntityIteratorAdapter(mainTablePrefix, extraTableList, expandedWhereClause, expandedOrderByClause,
201                     location.nrEntities, model, definition, location.nrEntities, location.firstEntity);
202           List entities = new ArrayList();
203
204           while (j.hasNext())
205             entities.add(0, j.next());
206
207           ParameterExpander.setValueForKey(aValueMap, batchDataKey, entities );
208
209           batchSubNode.produce(aValueMap, aVerb, aLogger);
210         }
211       }
212     }
213     catch (Throwable t) {
214       aLogger.error("EntityBatchingProducerNode caused an exception: " + t.getMessage());
215     }
216   };
217
218   private class BatchLocation {
219     int nrEntities;
220     int firstEntity;
221
222     public BatchLocation(int aFirstEntity, int aNrEntities) {
223       firstEntity = aFirstEntity;
224       nrEntities = aNrEntities;
225     }
226   }
227 }
228