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.Date;
021: import java.util.Iterator;
022: import java.util.List;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026: import org.jfree.data.general.AbstractDataset;
027: import org.jfree.data.time.RegularTimePeriod;
028: import org.jfree.data.time.TimeSeries;
029: import org.jfree.data.time.TimeSeriesCollection;
030: import org.jfree.data.time.TimeSeriesDataItem;
031: import org.jfree.report.event.ReportEvent;
032: import org.jfree.report.function.Expression;
033: import org.jfree.report.function.FunctionUtilities;
034: import org.jfree.util.ObjectUtilities;
035: import org.pentaho.messages.Messages;
036:
037: /**
038: * Creation-Date: 02.07.2007
039: *
040: * @author Gretchen Moran
041: */
042: public class TimeSeriesCollectorFunction extends BaseCollectorFunction {
043:
044: private static final long serialVersionUID = -8138304452870844825L;
045:
046: private ArrayList seriesNames;
047: private ArrayList valueColumns;
048: private ArrayList timeValueColumns;
049: private String domainPeriodType = ChartConstants.MILLISECOND_PERIOD_TYPE_STR;
050:
051: private static final Log myLogger = LogFactory
052: .getLog(TimeSeriesCollectorFunction.class);
053:
054: public TimeSeriesCollectorFunction() {
055: super ();
056: this .seriesNames = new ArrayList();
057: this .valueColumns = new ArrayList();
058: this .timeValueColumns = new ArrayList();
059: }
060:
061: /* ------- Standard accessors - we use indexed properties for the series config.
062: */
063:
064: public void setSeriesName(final int index, final String field) {
065: if (seriesNames.size() == index) {
066: seriesNames.add(field);
067: } else {
068: seriesNames.set(index, field);
069: }
070: }
071:
072: public String getSeriesName(final int index) {
073: return (String) seriesNames.get(index);
074: }
075:
076: public int getSeriesNameCount() {
077: return seriesNames.size();
078: }
079:
080: public String[] getSeriesName() {
081: return (String[]) seriesNames.toArray(new String[seriesNames
082: .size()]);
083: }
084:
085: public void setSeriesName(final String[] fields) {
086: this .seriesNames.clear();
087: this .seriesNames.addAll(Arrays.asList(fields));
088: }
089:
090: public void setValueColumn(final int index, final String field) {
091: if (valueColumns.size() == index) {
092: valueColumns.add(field);
093: } else {
094: valueColumns.set(index, field);
095: }
096: }
097:
098: public void setTimeValueColumn(final int index, final String field) {
099: if (timeValueColumns.size() == index) {
100: timeValueColumns.add(field);
101: } else {
102: timeValueColumns.set(index, field);
103: }
104: }
105:
106: public String getValueColumn(final int index) {
107: return (String) valueColumns.get(index);
108: }
109:
110: public int getValueColumnCount() {
111: return valueColumns.size();
112: }
113:
114: public String[] getValueColumn() {
115: return (String[]) valueColumns.toArray(new String[valueColumns
116: .size()]);
117: }
118:
119: public void setValueColumn(final String[] fields) {
120: this .valueColumns.clear();
121: this .valueColumns.addAll(Arrays.asList(fields));
122: }
123:
124: public String getTimeValueColumn(final int index) {
125: return (String) timeValueColumns.get(index);
126: }
127:
128: public int getTimeValueColumnCount() {
129: return timeValueColumns.size();
130: }
131:
132: public String[] getTimeValueColumn() {
133: return (String[]) timeValueColumns
134: .toArray(new String[timeValueColumns.size()]);
135: }
136:
137: public void setTimeValueColumn(final String[] fields) {
138: this .timeValueColumns.clear();
139: this .timeValueColumns.addAll(Arrays.asList(fields));
140: }
141:
142: /*
143: * ---------------------------------------------------------------- Now the function implementation ...
144: */
145:
146: public void itemsAdvanced(ReportEvent reportEvent) {
147: logger
148: .debug(Messages
149: .getString("CATEGORYSETCOLL.USER_DEBUG_ITEMS_ADVANCED")); //$NON-NLS-1$
150: if (FunctionUtilities.isDefinedPrepareRunLevel(this ,
151: reportEvent) == false) {
152: // we do not modify the created dataset if this is not the function
153: // computation run. (FunctionLevel '0')
154: return;
155: }
156: if (!isSummaryOnly()) {
157: buildDataset();
158: }
159: }
160:
161: public void groupFinished(ReportEvent reportEvent) {
162: logger
163: .debug(Messages
164: .getString("CATEGORYSETCOLL.USER_DEBUG_GROUPS_FINISHED")); //$NON-NLS-1$
165: if (FunctionUtilities.isDefinedPrepareRunLevel(this ,
166: reportEvent) == false) {
167: // we do not modify the created dataset if this is not the function
168: // computation run. (FunctionLevel '0')
169: return;
170: }
171:
172: if (isSummaryOnly()) {
173: if (FunctionUtilities.isDefinedGroup(getGroup(),
174: reportEvent)) {
175: // we can be sure that everything has been computed here. So
176: // grab the values and add them to the dataset.
177: buildDataset();
178: }
179: }
180: }
181:
182: private void buildDataset() {
183:
184: TimeSeriesCollection timeSeriesDataset = null;
185: if (getDatasourceValue() instanceof TimeSeriesCollection) {
186: timeSeriesDataset = (TimeSeriesCollection) getDatasourceValue();
187: }
188:
189: final int maxIndex = Math.min(this .seriesNames.size(),
190: this .valueColumns.size());
191: for (int i = 0; i < maxIndex; i++) {
192: String seriesName = (String) seriesNames.get(i);
193: final String column = (String) valueColumns.get(i);
194: final String timeColumn = (String) timeValueColumns.get(i);
195: final Object valueObject = getDataRow().get(column);
196: final Object timeValueObject = getDataRow().get(timeColumn);
197:
198: if (isSeriesColumn()) {
199: Object tmp = getDataRow().get(seriesName);
200: if (tmp != null) {
201: seriesName = tmp.toString();
202: }
203: }
204:
205: Date timeValue = null;
206:
207: Number value = (valueObject instanceof Number) ? (Number) valueObject
208: : null;
209:
210: Class timePeriodClass = ChartConstants
211: .getTimePeriodClass(getDomainPeriodType());
212:
213: if (timeValueObject instanceof Date) {
214: timeValue = (Date) timeValueObject;
215: } else {
216: timeValue = new Date();
217: }
218:
219: boolean isThere = false;
220: Object series = null;
221: try {
222: for (Iterator iter = timeSeriesDataset.getSeries()
223: .iterator(); iter.hasNext();) {
224: TimeSeries element = (TimeSeries) iter.next();
225: isThere = element.getKey().equals(seriesName);
226: series = isThere ? element : null;
227: if (series != null)
228: break;
229: }
230: } catch (Exception ignored) {
231: }
232: if (!isThere) {
233: series = new TimeSeries(seriesName, timePeriodClass);
234: timeSeriesDataset.addSeries((TimeSeries) series);
235: }
236: RegularTimePeriod regularTimePeriod = RegularTimePeriod
237: .createInstance(timePeriodClass, timeValue,
238: RegularTimePeriod.DEFAULT_TIME_ZONE);
239: TimeSeriesDataItem timeSeriesDataItem = new TimeSeriesDataItem(
240: regularTimePeriod, value);
241: ((TimeSeries) series).add(timeSeriesDataItem);
242: }
243: }
244:
245: public AbstractDataset getNewDataset() {
246: return new TimeSeriesCollection() {
247: private static final long serialVersionUID = 2096209400748561882L;
248:
249: /** Superclass hashcode is WAY slow.
250: * @see org.jfree.data.time.TimeSeriesCollection#hashCode( )
251: */
252: public int hashCode() {
253: return this .getSeriesCount();
254: }
255:
256: };
257: }
258:
259: public String getDomainPeriodType() {
260: return domainPeriodType;
261: }
262:
263: public void setDomainPeriodType(String domainPeriodType) {
264: this .domainPeriodType = domainPeriodType;
265: }
266:
267: /**
268: * Return a completly separated copy of this function. The copy no longer
269: * shares any changeable objects with the original function.
270: * Also from Thomas:
271: * Should retain data from the report definition, but clear calculated data.
272: *
273: * @return a copy of this function.
274: */
275: public Expression getInstance() {
276: TimeSeriesCollectorFunction fn = (TimeSeriesCollectorFunction) super
277: .getInstance();
278: try {
279: fn.seriesNames = (ArrayList) seriesNames.clone();
280: fn.valueColumns = (ArrayList) valueColumns.clone();
281: fn.timeValueColumns = (ArrayList) timeValueColumns.clone();
282: } catch (Exception ex) {
283: myLogger.error(ex);
284: throw new IllegalStateException(ex);
285: }
286: return fn;
287: }
288:
289: }
|