001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: *
013: * Created Oct 31, 2005
014: * @author mbatchel
015: */
016: package org.pentaho.plugin.jfreereport.reportcharts;
017:
018: import java.util.ArrayList;
019: import java.util.Arrays;
020: import java.util.List;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.jfree.data.category.DefaultCategoryDataset;
025: import org.jfree.data.general.AbstractDataset;
026: import org.jfree.report.DataRow;
027: import org.jfree.report.event.ReportEvent;
028: import org.jfree.report.function.Expression;
029: import org.jfree.report.function.FunctionUtilities;
030: import org.jfree.util.ObjectUtilities;
031: import org.pentaho.messages.Messages;
032:
033: /**
034: * Creation-Date: 22.09.2005, 18:30:22
035: *
036: * @author Thomas Morgner
037: */
038: public class CategorySetCollectorFunction extends BaseCollectorFunction {
039: private static final long serialVersionUID = -8138304452870844825L;
040:
041: private ArrayList seriesNames;
042:
043: private String categoryColumn;
044:
045: private ArrayList valueColumns;
046:
047: private int categoryStartColumn;
048:
049: protected ArrayList ignoreColumns;
050:
051: private boolean generatedReport = false;
052:
053: private static final Log myLogger = LogFactory
054: .getLog(CategorySetCollectorFunction.class);
055:
056: public CategorySetCollectorFunction() {
057: super ();
058: this .seriesNames = new ArrayList();
059: this .valueColumns = new ArrayList();
060: this .ignoreColumns = new ArrayList();
061:
062: }
063:
064: public void setGeneratedReport(boolean value) {
065: generatedReport = value;
066: }
067:
068: public boolean isGeneratedReport() {
069: return generatedReport;
070: }
071:
072: public void setIgnoreColumn(final int index, final String field) {
073: if (ignoreColumns.size() == index) {
074: ignoreColumns.add(field);
075: } else {
076: ignoreColumns.set(index, field);
077: }
078: }
079:
080: public String getIgnoreColumn(final int index) {
081: return (String) ignoreColumns.get(index);
082: }
083:
084: public int getIgnoreColumnCount() {
085: return ignoreColumns.size();
086: }
087:
088: public String[] getIgnoreColumn() {
089: return (String[]) ignoreColumns
090: .toArray(new String[ignoreColumns.size()]);
091: }
092:
093: public void setIgnoreColumn(final String[] fields) {
094: this .ignoreColumns.clear();
095: this .ignoreColumns.addAll(Arrays.asList(fields));
096: }
097:
098: public void setCategoryStartColumn(int value) {
099: categoryStartColumn = value;
100: }
101:
102: public int getCategoryStartColumn() {
103: return categoryStartColumn;
104: }
105:
106: /*
107: * ---------------------------------------------------------------- Standard accessors - we use indexed properties for the series config.
108: */
109:
110: public void setSeriesName(final int index, final String field) {
111: if (seriesNames.size() == index) {
112: seriesNames.add(field);
113: } else {
114: seriesNames.set(index, field);
115: }
116: }
117:
118: public String getSeriesName(final int index) {
119: return (String) seriesNames.get(index);
120: }
121:
122: public int getSeriesNameCount() {
123: return seriesNames.size();
124: }
125:
126: public String[] getSeriesName() {
127: return (String[]) seriesNames.toArray(new String[seriesNames
128: .size()]);
129: }
130:
131: public void setSeriesName(final String[] fields) {
132: this .seriesNames.clear();
133: this .seriesNames.addAll(Arrays.asList(fields));
134: }
135:
136: public void setValueColumn(final int index, final String field) {
137: if (valueColumns.size() == index) {
138: valueColumns.add(field);
139: } else {
140: valueColumns.set(index, field);
141: }
142: }
143:
144: public String getValueColumn(final int index) {
145: return (String) valueColumns.get(index);
146: }
147:
148: public int getValueColumnCount() {
149: return valueColumns.size();
150: }
151:
152: public String[] getValueColumn() {
153: return (String[]) valueColumns.toArray(new String[valueColumns
154: .size()]);
155: }
156:
157: public void setValueColumn(final String[] fields) {
158: this .valueColumns.clear();
159: this .valueColumns.addAll(Arrays.asList(fields));
160: }
161:
162: public String getCategoryColumn() {
163: return categoryColumn;
164: }
165:
166: public void setCategoryColumn(final String categoryColumn) {
167: this .categoryColumn = categoryColumn;
168: }
169:
170: /*
171: * ---------------------------------------------------------------- Now the function implementation ...
172: */
173:
174: public AbstractDataset getNewDataset() {
175: return new DefaultCategoryDataset();
176: }
177:
178: public void itemsAdvanced(ReportEvent reportEvent) {
179:
180: logger
181: .debug(Messages
182: .getString("CATEGORYSETCOLL.USER_DEBUG_ITEMS_ADVANCED")); //$NON-NLS-1$
183:
184: if (FunctionUtilities.isDefinedPrepareRunLevel(this ,
185: reportEvent) == false) {
186: // we do not modify the created dataset if this is not the function
187: // computation run. (FunctionLevel '0')
188: return;
189: }
190:
191: DefaultCategoryDataset categoryDataset = null;
192: if (getDatasourceValue() instanceof DefaultCategoryDataset) {
193: categoryDataset = (DefaultCategoryDataset) getDatasourceValue();
194: }
195:
196: if (!this .isSummaryOnly()) {
197: final Object categoryObject = getDataRow().get(
198: getCategoryColumn());
199: final Comparable categoryComparable;
200: if (categoryObject instanceof Comparable) {
201: categoryComparable = (Comparable) categoryObject;
202: } else {
203: // ok, we need some better error management here. Its a
204: // prototype :)
205: categoryComparable = Messages
206: .getString("CATEGORYSETCOLL.USER_ERROR_CATEGORY_NOT_COMPARABLE"); //$NON-NLS-1$
207: }
208: // I love to be paranoid!
209: final int maxIndex = Math.min(this .seriesNames.size(),
210: this .valueColumns.size());
211: for (int i = 0; i < maxIndex; i++) {
212: String seriesName = (String) seriesNames.get(i);
213: final String column = (String) valueColumns.get(i);
214: final Object valueObject = getDataRow().get(column);
215: if (isSeriesColumn()) {
216: Object tmp = getDataRow().get(seriesName);
217: if (tmp != null) {
218: seriesName = tmp.toString();
219: }
220: }
221:
222: Number value = (valueObject instanceof Number) ? (Number) valueObject
223: : null;
224:
225: Object isThere = null;
226: try {
227: isThere = categoryDataset.getValue(seriesName,
228: categoryComparable);
229: } catch (Exception ignored) {
230: }
231: if (isThere != null) {
232: double val = (value != null) ? value.doubleValue()
233: : 0;
234: value = new Double(val
235: + ((Number) isThere).doubleValue());
236: categoryDataset.setValue(value, seriesName,
237: categoryComparable);
238: } else {
239: categoryDataset.addValue(value, seriesName,
240: categoryComparable);
241: }
242: }
243: }
244: }
245:
246: public void groupFinished(ReportEvent reportEvent) {
247:
248: logger
249: .debug(Messages
250: .getString("CATEGORYSETCOLL.USER_DEBUG_GROUPS_FINISHED")); //$NON-NLS-1$
251:
252: if (FunctionUtilities.isDefinedPrepareRunLevel(this ,
253: reportEvent) == false) {
254: // we do not modify the created dataset if this is not the function
255: // computation run. (FunctionLevel '0')
256: return;
257: }
258:
259: DefaultCategoryDataset categoryDataset = null;
260: if (getDatasourceValue() instanceof DefaultCategoryDataset) {
261: categoryDataset = (DefaultCategoryDataset) getDatasourceValue();
262: }
263:
264: if (this .isSummaryOnly()) {
265: if (FunctionUtilities.isDefinedGroup(getGroup(),
266: reportEvent)) {
267:
268: // we can be sure that everything has been computed here. So
269: // grab the values and add them to the dataset.
270:
271: if (!this .isGeneratedReport()) {
272:
273: final Object categoryObject = getDataRow().get(
274: getCategoryColumn());
275: final Comparable categoryComparable;
276: if (categoryObject instanceof Comparable) {
277: categoryComparable = (Comparable) categoryObject;
278: } else {
279: // ok, we need some better error management here. Its a
280: // prototype :)
281: categoryComparable = Messages
282: .getString("CATEGORYSETCOLL.USER_ERROR_CATEGORY_NOT_COMPARABLE"); //$NON-NLS-1$
283: }
284:
285: // I love to be paranoid!
286: final int maxIndex = Math.min(this .seriesNames
287: .size(), this .valueColumns.size());
288: for (int i = 0; i < maxIndex; i++) {
289: String seriesName = (String) seriesNames.get(i);
290: final String column = (String) valueColumns
291: .get(i);
292: final Object valueObject = getDataRow().get(
293: column);
294: if (isSeriesColumn()) {
295: Object tmp = getDataRow().get(seriesName);
296: if (tmp != null) {
297: seriesName = tmp.toString();
298: }
299: }
300: Number value = (valueObject instanceof Number) ? (Number) valueObject
301: : null;
302: categoryDataset.addValue(value, seriesName,
303: categoryComparable);
304: }
305: } else { // generatedReport == true
306:
307: // if the generatedReport flag is true, then we are dynamically
308: // generating the number of columns in the report ...
309:
310: // We expect additional information in order to get the dataset
311: // built properly:
312: // ignoreColumns and categoryStartColumn
313:
314: DataRow theRow = getDataRow();
315: int colCount = theRow.getColumnCount();
316: for (int i = categoryStartColumn; i < colCount; i++) {
317: String seriesName = theRow.getColumnName(i);
318:
319: // TODO What is this all about??
320: if (seriesName.startsWith("Summary_")) { //$NON-NLS-1$
321: if (ignoreColumns.indexOf(seriesName) >= 0) {
322: continue;
323: }
324: seriesName = seriesName.substring(8,
325: seriesName.length() - 10);
326: Object valueObject = theRow.get(i);
327:
328: Number value = (valueObject instanceof Number) ? (Number) valueObject
329: : null;
330: categoryDataset.addValue(value, seriesName,
331: seriesName);
332: }
333: }
334: }
335: }
336: }
337: }
338:
339: /**
340: * Return a completly separated copy of this function. The copy no longer shares any changeable objects with the original function.
341: * Also from Thomas:
342: * Should retain data from the report definition, but clear calculated data.
343: *
344: * @return a copy of this function.
345: */
346: public Expression getInstance() {
347: final CategorySetCollectorFunction fn = (CategorySetCollectorFunction) super
348: .getInstance();
349: try {
350: fn.seriesNames = (ArrayList) seriesNames.clone();
351: fn.valueColumns = (ArrayList) valueColumns.clone();
352: fn.ignoreColumns = (ArrayList) ignoreColumns.clone();
353: } catch (Exception ex) {
354: myLogger.error(ex);
355: throw new IllegalStateException(ex);
356: }
357: return fn;
358: }
359:
360: }
|