001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * ItemAvgFunction.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.function;
030:
031: import java.math.BigDecimal;
032:
033: import org.jfree.report.Group;
034: import org.jfree.report.event.ReportEvent;
035: import org.jfree.util.Log;
036:
037: /**
038: * A report function that calculates the average of one field (column) from the TableModel. This function produces a
039: * running total, no global total. The function can be used in two ways: <ul> <li>to calculate an average value for the
040: * entire report;</li> <li>to calculate an average value within a particular group;</li> </ul> This function expects its
041: * input values to be either java.lang.Number instances or Strings that can be parsed to java.lang.Number instances
042: * using a java.text.DecimalFormat.
043: * <p/>
044: * The function undestands two parameters, the <code>field</code> parameter is required and denotes the name of an
045: * ItemBand-field which gets summed up.
046: * <p/>
047: * The parameter <code>group</code> denotes the name of a group. When this group is started, the counter gets reseted to
048: * null.
049: *
050: * @author Thomas Morgner
051: */
052: public class ItemAvgFunction extends AbstractFunction {
053: /**
054: * Useful constant for zero.
055: */
056: private static final BigDecimal ZERO = new BigDecimal(0.0);
057:
058: /**
059: * Useful constant for one.
060: */
061: private static final BigDecimal ONE = new BigDecimal(1.0);
062:
063: /**
064: * The item sum.
065: */
066: private transient BigDecimal sum;
067:
068: /**
069: * The item count.
070: */
071: private transient BigDecimal itemCount;
072:
073: /**
074: * The name of the group on which to reset the count. This can be set to null to compute the average for the whole
075: * report.
076: */
077: private String group;
078: /**
079: * The name of the field from where to read the values.
080: */
081: private String field;
082: /**
083: * The scale-property defines the precission of the divide-operation.
084: */
085: private int scale;
086: /**
087: * The rounding-property defines the precission of the divide-operation.
088: */
089: private int roundingMode;
090:
091: /**
092: * Constructs an unnamed function. Make sure to set a Name or function initialisation will fail.
093: */
094: public ItemAvgFunction() {
095: sum = ZERO;
096: itemCount = ZERO;
097: scale = 14;
098: roundingMode = BigDecimal.ROUND_HALF_UP;
099: }
100:
101: /**
102: * Constructs a named function. <P> The field must be defined before using the function.
103: *
104: * @param name The function name.
105: */
106: public ItemAvgFunction(final String name) {
107: this ();
108: setName(name);
109: }
110:
111: /**
112: * Receives notification that a new report is about to start. <P> Does nothing.
113: *
114: * @param event Information about the event.
115: */
116: public void reportInitialized(final ReportEvent event) {
117: this .sum = ZERO;
118: itemCount = ZERO;
119: }
120:
121: /**
122: * Receives notification that a new group is about to start. If this is the group defined for the function, then the
123: * running total is reset to zero.
124: *
125: * @param event Information about the event.
126: */
127: public void groupStarted(final ReportEvent event) {
128: final String mygroup = getGroup();
129: if (mygroup == null) {
130: return;
131: }
132:
133: final Group group = event.getReport().getGroup(
134: event.getState().getCurrentGroupIndex());
135: if (getGroup().equals(group.getName())) {
136: this .sum = ZERO;
137: itemCount = ZERO;
138: }
139: }
140:
141: /**
142: * Returns the group name.
143: *
144: * @return The group name.
145: */
146: public String getGroup() {
147: return group;
148: }
149:
150: /**
151: * Sets the group name. <P> If a group is defined, the functions value is reset to zero at the start of every instance
152: * of this group.
153: *
154: * @param name The group name (null permitted).
155: */
156: public void setGroup(final String name) {
157: this .group = name;
158: }
159:
160: /**
161: * Returns the field used by the function. The field name corresponds to a column name in the report's data-row.
162: *
163: * @return The field name.
164: */
165: public String getField() {
166: return field;
167: }
168:
169: /**
170: * Sets the field name for the function. The field name corresponds to a column name in the report's data-row.
171: *
172: * @param field the field name.
173: */
174: public void setField(final String field) {
175: this .field = field;
176: }
177:
178: /**
179: * Returns the defined rounding mode. This influences the precision of the divide-operation.
180: *
181: * @return the rounding mode.
182: * @see java.math.BigDecimal#divide(java.math.BigDecimal,int)
183: */
184: public int getRoundingMode() {
185: return roundingMode;
186: }
187:
188: /**
189: * Defines the rounding mode. This influences the precision of the divide-operation.
190: *
191: * @param roundingMode the rounding mode.
192: * @see java.math.BigDecimal#divide(java.math.BigDecimal,int)
193: */
194: public void setRoundingMode(final int roundingMode) {
195: this .roundingMode = roundingMode;
196: }
197:
198: /**
199: * Returns the scale for the divide-operation. The scale influences the precision of the division.
200: *
201: * @return the scale.
202: */
203: public int getScale() {
204: return scale;
205: }
206:
207: /**
208: * Defines the scale for the divide-operation. The scale influences the precision of the division.
209: *
210: * @param scale the scale.
211: */
212: public void setScale(final int scale) {
213: this .scale = scale;
214: }
215:
216: /**
217: * Receives notification that a row of data is being processed.
218: *
219: * @param event Information about the event.
220: */
221: public void itemsAdvanced(final ReportEvent event) {
222: final Object fieldValue = event.getDataRow().get(getField());
223:
224: final Number n = (Number) fieldValue;
225: try {
226: sum = sum.add(new BigDecimal(n.toString()));
227: itemCount = itemCount.add(ONE);
228: } catch (Exception e) {
229: Log
230: .error("ItemAvgFunction.advanceItems(): problem adding number.");
231: }
232: }
233:
234: /**
235: * Returns the function value, in this case the average of all values of a specific column in the report's
236: * TableModel.
237: *
238: * @return The function value.
239: */
240: public Object getValue() {
241: if (itemCount.longValue() == 0) {
242: return null;
243: } else {
244: return sum.divide(itemCount, scale, roundingMode);
245: }
246: }
247:
248: /**
249: * Return a completly separated copy of this function. The copy does no longer share any changeable objects with the
250: * original function.
251: *
252: * @return a copy of this function.
253: */
254: public Expression getInstance() {
255: final ItemAvgFunction function = (ItemAvgFunction) super
256: .getInstance();
257: function.sum = ZERO;
258: function.itemCount = ZERO;
259: return function;
260: }
261: }
|