Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }