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: * CachingDataFactory.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.states;
030:
031: import java.util.Arrays;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import javax.swing.table.DefaultTableModel;
035: import javax.swing.table.TableModel;
036:
037: import org.jfree.report.DataFactory;
038: import org.jfree.report.DataRow;
039: import org.jfree.report.JFreeReportBoot;
040: import org.jfree.report.ReportDataFactoryException;
041: import org.jfree.report.util.CloseableTableModel;
042: import org.jfree.util.Log;
043: import org.jfree.util.ObjectUtilities;
044:
045: /**
046: * Creation-Date: 19.11.2006, 13:35:45
047: *
048: * @author Thomas Morgner
049: */
050: public class CachingDataFactory implements DataFactory {
051: private static class Parameters implements DataRow {
052: private Object[] dataStore;
053: private String[] nameStore;
054: private Integer hashCode;
055:
056: public Parameters(final DataRow dataSet) {
057: final int columnCount = dataSet.getColumnCount();
058: dataStore = new Object[columnCount];
059: nameStore = new String[columnCount];
060:
061: for (int i = 0; i < columnCount; i++) {
062: nameStore[i] = dataSet.getColumnName(i);
063: dataStore[i] = dataSet.get(i);
064: }
065: }
066:
067: public boolean isChanged(final String name) {
068: return true;
069: }
070:
071: public boolean isChanged(final int index) {
072: return true;
073: }
074:
075: public int getColumnCount() {
076: return dataStore.length;
077: }
078:
079: public String getColumnName(final int column) {
080: return nameStore[column];
081: }
082:
083: public Object get(final int column) {
084: return dataStore[column];
085: }
086:
087: public boolean equals(final Object o) {
088: if (this == o) {
089: return true;
090: }
091: if (o == null || getClass() != o.getClass()) {
092: return false;
093: }
094:
095: final Parameters that = (Parameters) o;
096:
097: if (!Arrays.equals(dataStore, that.dataStore)) {
098: return false;
099: }
100: if (!Arrays.equals(nameStore, that.nameStore)) {
101: return false;
102: }
103:
104: return true;
105: }
106:
107: public synchronized int hashCode() {
108: if (hashCode != null) {
109: return hashCode.intValue();
110: }
111: int hashCode = 0;
112: for (int i = 0; i < dataStore.length; i++) {
113: final Object o = dataStore[i];
114: if (o != null) {
115: hashCode = hashCode * 23 + o.hashCode();
116: } else {
117: hashCode = hashCode * 23;
118: }
119: }
120: for (int i = 0; i < nameStore.length; i++) {
121: final Object o = nameStore[i];
122: if (o != null) {
123: hashCode = hashCode * 23 + o.hashCode();
124: } else {
125: hashCode = hashCode * 23;
126: }
127: }
128: this .hashCode = new Integer(hashCode);
129: return hashCode;
130: }
131:
132: /**
133: * Returns the value of the function, expression or column using its specific name. The given name is translated
134: * into a valid column number and the the column is queried. For functions and expressions, the
135: * <code>getValue()</code> method is called and for columns from the tablemodel the tablemodel method
136: * <code>getValueAt(row, column)</code> gets called.
137: *
138: * @param col the item index.
139: * @return the value.
140: * @throws IllegalStateException if the datarow detected a deadlock.
141: */
142: public Object get(final String col)
143: throws IllegalStateException {
144: final int idx = findColumn(col);
145: if (idx == -1) {
146: return null;
147: }
148: return get(idx);
149: }
150:
151: /**
152: * Returns the column position of the column, expression or function with the given name or -1 if the given name
153: * does not exist in this DataRow.
154: *
155: * @param name the item name.
156: * @return the item index.
157: */
158: public int findColumn(final String name) {
159: for (int i = 0; i < nameStore.length; i++) {
160: final String storedName = nameStore[i];
161: if (ObjectUtilities.equal(storedName, name)) {
162: return i;
163: }
164: }
165: return -1;
166: }
167: }
168:
169: private HashMap queryCache;
170: private DataFactory backend;
171: private boolean closed;
172: private boolean debugDataSources;
173: private boolean profileDataSources;
174:
175: public CachingDataFactory(final DataFactory backend) {
176: if (backend == null) {
177: throw new NullPointerException();
178: }
179: this .backend = backend;
180: this .queryCache = new HashMap();
181: this .debugDataSources = "true".equals(JFreeReportBoot
182: .getInstance().getGlobalConfig().getConfigProperty(
183: "org.jfree.report.DebugDataSources"));
184: this .profileDataSources = "true".equals(JFreeReportBoot
185: .getInstance().getGlobalConfig().getConfigProperty(
186: "org.jfree.report.ProfileDataSources"));
187: }
188:
189: public void open() {
190: backend.open();
191: closed = false;
192: }
193:
194: /**
195: * Queries a datasource. The string 'query' defines the name of the query. The Parameterset given here may contain
196: * more data than actually needed.
197: * <p/>
198: * The dataset may change between two calls, do not assume anything!
199: *
200: * @param query
201: * @param parameters
202: * @return
203: */
204: public TableModel queryData(final String query,
205: final DataRow parameters) throws ReportDataFactoryException {
206: if (profileDataSources && Log.isDebugEnabled()) {
207: Log.debug(System.identityHashCode(Thread.currentThread())
208: + ": Query processing time: Starting");
209: }
210: final long startTime = System.currentTimeMillis();
211: try {
212: final HashMap parameterCache = (HashMap) queryCache
213: .get(query);
214: if (parameterCache == null) {
215: // totally new query here.
216: final HashMap newParams = new HashMap();
217: queryCache.put(query, newParams);
218:
219: final Parameters params = new Parameters(parameters);
220: final TableModel newData = backend.queryData(query,
221: params);
222: if (newData == null) {
223: final DefaultTableModel value = new DefaultTableModel();
224: if (debugDataSources && Log.isDebugEnabled()) {
225: Log.debug("Query failed for query '" + query
226: + '\'');
227: }
228: newParams.put(params, value);
229: return value;
230: } else {
231: if (debugDataSources && Log.isDebugEnabled()) {
232: printTableModelContents(newData);
233: }
234: newParams.put(params, newData);
235: return newData;
236: }
237: } else {
238: // Lookup the parameters ...
239: final Parameters params = new Parameters(parameters);
240: final TableModel data = (TableModel) parameterCache
241: .get(params);
242: if (data != null) {
243: return data;
244: }
245:
246: final TableModel newData = backend.queryData(query,
247: params);
248: if (newData == null) {
249: final DefaultTableModel value = new DefaultTableModel();
250: if (debugDataSources && Log.isDebugEnabled()) {
251: Log.debug("Query failed for query '" + query
252: + '\'');
253: }
254: parameterCache.put(params, value);
255: return value;
256: } else {
257: if (debugDataSources && Log.isDebugEnabled()) {
258: printTableModelContents(newData);
259: }
260: parameterCache.put(params, newData);
261: return newData;
262: }
263: }
264: } finally {
265: final long queryTime = System.currentTimeMillis();
266: if (profileDataSources && Log.isDebugEnabled()) {
267: Log.debug(System.identityHashCode(Thread
268: .currentThread())
269: + ": Query processing time: "
270: + ((queryTime - startTime) / 1000.0));
271: }
272: }
273: }
274:
275: /**
276: * Closes the report data factory and all report data instances that have been returned by this instance.
277: */
278: public synchronized void close() {
279: if (closed == false) {
280: final Iterator queries = queryCache.values().iterator();
281: while (queries.hasNext()) {
282: final HashMap map = (HashMap) queries.next();
283: final Iterator dataSets = map.values().iterator();
284: while (dataSets.hasNext()) {
285: final TableModel data = (TableModel) dataSets
286: .next();
287: if (data instanceof CloseableTableModel) {
288: final CloseableTableModel ct = (CloseableTableModel) data;
289: ct.close();
290: }
291: }
292: }
293: backend.close();
294: closed = true;
295: }
296: }
297:
298: /**
299: * Derives a freshly initialized report data factory, which is independend of the original data factory. Opening or
300: * Closing one data factory must not affect the other factories.
301: *
302: * @return
303: */
304: public DataFactory derive() {
305: // If you see that exception, then you've probably tried to use that
306: // datafactory from outside of the report processing. You deserve the
307: // exception in that case ..
308: throw new UnsupportedOperationException(
309: "The CachingReportDataFactory cannot be derived.");
310: }
311:
312: /**
313: * Prints a table model to standard output.
314: *
315: * @param mod the model.
316: */
317: public static void printTableModelContents(final TableModel mod) {
318: Log
319: .debug("Tablemodel contains " + mod.getRowCount() + " rows."); //$NON-NLS-1$ //$NON-NLS-2$
320: for (int i = 0; i < mod.getColumnCount(); i++) {
321: Log
322: .debug("Column: " + i + " Name = " + mod.getColumnName(i) + "; DataType = " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
323: + mod.getColumnClass(i));
324: }
325:
326: Log.debug("Checking the data inside"); //$NON-NLS-1$
327: for (int rows = 0; rows < mod.getRowCount(); rows++) {
328: for (int i = 0; i < mod.getColumnCount(); i++) {
329: final Object value = mod.getValueAt(rows, i);
330: Log
331: .debug("ValueAt (" + rows + ", " + i + ") is " + value); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
332: }
333: }
334: }
335:
336: }
|