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