Source code: jbreport/data/Aggregate.java
1 /*
2 * $Id: Aggregate.java,v 1.1.1.1 2000/08/31 13:14:34 grantfin Exp $
3 *
4 * jbReport - A reporting library for Java
5 * Copyright (C) 2000 Grant Finnemore <grantfin@users.sourceforge.net>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 package jbreport.data;
22
23 import java.math.BigDecimal;
24 import java.math.BigInteger;
25 import java.util.HashMap;
26 import java.util.Map;
27 import org.xml.sax.Attributes;
28
29 import jbreport.ReportElement;
30 import jbreport.ReportException;
31 import jbreport.core.AbstractReportElement;
32 import jbreport.core.ReportVisitor;
33 import jbreport.core.ReportVisitorState;
34 import jbreport.data.QueryResult;
35
36 /**
37 * This performs operations on groups of data in the report, all of which are
38 * strongly typed. It provides an extensible interface whereby operations can
39 * be defined for Aggregates to perform.
40 *
41 * @author Grant Finnemore
42 * @version $Revision: 1.1.1.1 $
43 */
44 public
45 class Aggregate extends AbstractReportElement {
46
47 /** A constant representing the BigDecimal ZERO to two dp */
48 public static final BigDecimal BD_ZERO = new BigDecimal(BigInteger.ZERO, 2);
49
50 /** The currently known Aggregatable types */
51 private static Map aggTypes = new HashMap();
52
53 /** int data type */
54 private static final int AGG_INT = 1;
55
56 /** BigDecimal data type */
57 private static final int AGG_BD = 2;
58
59 /** The data type that will be used for aggregation */
60 private int aggType;
61
62 /** The aggregate operation name */
63 private String opName;
64
65 /** The aggregatable instance that will be used */
66 private Aggregatable aggregatable;
67
68 //
69 // Enclosed interfaces
70 //
71
72 /**
73 * This is the interface that actually does all the work during data
74 * expansion. We provide some default operations, and allow other operations
75 * to be defined.
76 *
77 * <p> Instances of this interface don't have to be thread-safe, as the
78 * framework should create new instances as necessary.
79 *
80 * <p> All concrete implementations of this interface should be public, and
81 * have a public no-args constructor available.
82 */
83 public
84 interface Aggregatable {
85
86 /**
87 * The name of the operation that is implemented by this instance.
88 */
89 public String getOpName();
90
91 /**
92 * Setup this instance with the type that will be used during the run.
93 */
94 public void setType(String type);
95
96 /**
97 * Reset the internal values of the instance.
98 */
99 public void reset();
100
101 /**
102 * Provides the next int value with which to update this instance.
103 */
104 public void update(int value);
105
106 /**
107 * Provides the next BigDecimal value with which to update this instance.
108 */
109 public void update(BigDecimal value);
110
111 /**
112 * Returns the current int value of this instance.
113 */
114 public int getInt();
115
116 /**
117 * Returns the current BigDecimal value of this instance.
118 */
119 public BigDecimal getBigDecimal();
120
121 }
122
123 //
124 // Constructors
125 //
126
127 /**
128 * Static initializer that will add the default Aggregatable instances to
129 * the class.
130 */
131 static {
132 addAggregatable(new AggregatableSum());
133 }
134
135 //
136 // Static initialization methods
137 //
138
139 /**
140 * This method will add the given Aggregatable type to the ones currently
141 * available.
142 *
143 * <p> This method accepts an Aggregatable instance, as the operation type
144 * is obtained from the appropriate method.
145 *
146 * @throws IllegalArgumentException if the operation type name already
147 * exists.
148 */
149 public static void addAggregatable(Aggregatable agg) {
150 if(agg.getOpName() == null) {
151 throw new IllegalArgumentException(agg + " returns a null op name");
152 }
153 if(aggTypes.containsKey(agg.getOpName())) {
154 throw new IllegalArgumentException(agg.getOpName()
155 + " is already defined");
156 }
157 aggTypes.put(agg.getOpName(), agg);
158 }
159
160 //
161 // Methods overloaded from AbstractReportElement
162 //
163
164 public void accept(ReportVisitor visitor, ReportVisitorState state)
165 throws ReportException {
166 visitor.visitAggregate(this, state);
167 }
168
169 public ReportElement copy() {
170 Aggregate result = (Aggregate)super.copy();
171 if(result.opName != null) {
172 result.aggregatable = fetchAggregatable(result.opName);
173 }
174 return result;
175 }
176
177 public ReportElement copy(boolean deep) {
178 Aggregate result = (Aggregate)super.copy(deep);
179 if(result.opName != null) {
180 result.aggregatable = fetchAggregatable(result.opName);
181 }
182 return result;
183 }
184
185 public void updateData(QueryResult queryResult) throws ReportException {
186 switch(aggType) {
187 case AGG_INT:
188 aggregatable.update(queryResult.getInt(fetchColumnName()));
189 break;
190 case AGG_BD:
191 aggregatable.update(queryResult.getBigDecimal(fetchColumnName()));
192 break;
193 default:
194 // Should never reach here
195 throw new IllegalArgumentException("Unknown type " + aggType);
196 }
197 }
198
199 public void xmlInitialize(String localName, Attributes attributes)
200 throws ReportException {
201 super.xmlInitialize(localName, attributes);
202 // Setup the data type
203 String value = attributes.getValue("type");
204 assertNotNull(value, "type");
205 if("int".equals(value)) {
206 aggType = AGG_INT;
207 }
208 else if("BigDecimal".equals(value)) {
209 aggType = AGG_BD;
210 }
211 else {
212 throw new IllegalArgumentException("Type " + value + " is unknown");
213 }
214 // Setup the aggregatable
215 value = attributes.getValue("op");
216 assertNotNull(value, "op");
217 aggregatable = fetchAggregatable(value);
218 opName = value;
219 }
220
221 //
222 // Implementation methods
223 //
224
225 /**
226 * This will fetch an Aggregatable instance for the given operation type.
227 * Although the implementation would suggest that a create semantic is
228 * needed, this might change to being a factory create/find semantic.
229 *
230 * @throws IllegalArgumentException if the given operation type is unknown.
231 */
232 private Aggregatable fetchAggregatable(String opName) {
233 if(!aggTypes.containsKey(opName)) {
234 throw new IllegalArgumentException(opName + " is not registered");
235 }
236 Class cls = aggTypes.get(opName).getClass();
237 try {
238 return (Aggregatable)cls.newInstance();
239 }
240 catch(Exception e) {
241 e.printStackTrace();
242 throw new RuntimeException(e.toString());
243 }
244 }
245
246 /**
247 * Returns the name of the column to which this is bound. This would break
248 * if we put aggregates outside a table.
249 */
250 private String fetchColumnName() throws ReportException {
251 return getParent().getString("id", "");
252 }
253
254 public String toString() {
255 switch(aggType) {
256 case AGG_INT:
257 return String.valueOf(aggregatable.getInt());
258 case AGG_BD:
259 return aggregatable.getBigDecimal().toString();
260 default:
261 return super.toString();
262 }
263 }
264
265 }
266
267 /**
268 * The summing Aggregatable class.
269 */
270 class AggregatableSum implements Aggregate.Aggregatable {
271
272 /** The sum of the int values */
273 private int intSum;
274
275 /** The sum of the BigDecimal values */
276 private BigDecimal bigDecimalSum;
277
278 //
279 // Constructors
280 //
281
282 public AggregatableSum() {
283 reset();
284 }
285
286 //
287 // Implementation of the Aggregatable interface
288 //
289
290 public String getOpName() {
291 return "sum";
292 }
293
294 public void setType(String type) {
295
296 }
297
298 public void reset() {
299 intSum = 0;
300 bigDecimalSum = Aggregate.BD_ZERO;
301 }
302
303 public void update(int value) {
304 intSum += value;
305 }
306
307 public void update(BigDecimal value) {
308 bigDecimalSum = bigDecimalSum.add(value);
309 }
310
311 public int getInt() {
312 return intSum;
313 }
314
315 public BigDecimal getBigDecimal() {
316 return bigDecimalSum;
317 }
318
319 }