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