001 /**
002 * ========================================
003 * JFreeReport : a free Java report library
004 * ========================================
005 *
006 * Project Info: http://reporting.pentaho.org/
007 *
008 * (C) Copyright 2000-2007, by Object Refinery Limited, 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 * $Id: CachingReportDataFactory.java,v 1.5 2007/06/10 15:54:21 taqua Exp $
027 * ------------
028 * (C) Copyright 2000-2005, by Object Refinery Limited.
029 * (C) Copyright 2005-2007, by Pentaho Corporation.
030 */
031
032 package org.jfree.report.data;
033
034 import java.util.Arrays;
035 import java.util.HashMap;
036 import java.util.Iterator;
037
038 import org.jfree.report.DataSet;
039 import org.jfree.report.DataSourceException;
040 import org.jfree.report.ReportData;
041 import org.jfree.report.ReportDataFactory;
042 import org.jfree.report.ReportDataFactoryException;
043
044 /**
045 * Creation-Date: 19.11.2006, 13:35:45
046 *
047 * @author Thomas Morgner
048 */
049 public class CachingReportDataFactory implements ReportDataFactory
050 {
051 private static class Parameters implements DataSet
052 {
053 private Object[] dataStore;
054 private String[] nameStore;
055 private Integer hashCode;
056
057 protected Parameters(final DataSet dataSet) throws DataSourceException
058 {
059 final int columnCount = dataSet.getColumnCount();
060 dataStore = new Object[columnCount];
061 nameStore = new String[columnCount];
062
063 for (int i = 0; i < columnCount; i++)
064 {
065 nameStore[i] = dataSet.getColumnName(i);
066 dataStore[i] = dataSet.get(i);
067 }
068 }
069
070 public int getColumnCount() throws DataSourceException
071 {
072 return dataStore.length;
073 }
074
075 public String getColumnName(final int column) throws DataSourceException
076 {
077 return nameStore[column];
078 }
079
080 public Object get(final int column) throws DataSourceException
081 {
082 return dataStore[column];
083 }
084
085 public boolean equals(final Object o)
086 {
087 if (this == o)
088 {
089 return true;
090 }
091 if (o == null || getClass() != o.getClass())
092 {
093 return false;
094 }
095
096 final Parameters that = (Parameters) o;
097
098 if (!Arrays.equals(dataStore, that.dataStore))
099 {
100 return false;
101 }
102 if (!Arrays.equals(nameStore, that.nameStore))
103 {
104 return false;
105 }
106
107 return true;
108 }
109
110 public synchronized int hashCode()
111 {
112 if (hashCode != null)
113 {
114 return hashCode.intValue();
115 }
116 int hashCode = 0;
117 for (int i = 0; i < dataStore.length; i++)
118 {
119 final Object o = dataStore[i];
120 if (o != null)
121 {
122 hashCode = hashCode * 23 + o.hashCode();
123 }
124 else
125 {
126 hashCode = hashCode * 23;
127 }
128 }
129 for (int i = 0; i < nameStore.length; i++)
130 {
131 final Object o = nameStore[i];
132 if (o != null)
133 {
134 hashCode = hashCode * 23 + o.hashCode();
135 }
136 else
137 {
138 hashCode = hashCode * 23;
139 }
140 }
141 this.hashCode = new Integer(hashCode);
142 return hashCode;
143 }
144 }
145
146 private HashMap queryCache;
147
148 private ReportDataFactory backend;
149
150 public CachingReportDataFactory(final ReportDataFactory backend)
151 {
152 if (backend == null)
153 {
154 throw new NullPointerException();
155 }
156 this.backend = backend;
157 this.queryCache = new HashMap();
158 }
159
160 public void open()
161 {
162 backend.open();
163 }
164
165 /**
166 * Queries a datasource. The string 'query' defines the name of the query. The Parameterset given here may contain
167 * more data than actually needed.
168 * <p/>
169 * The dataset may change between two calls, do not assume anything!
170 *
171 * @param query
172 * @param parameters
173 * @return
174 */
175 public ReportData queryData(final String query, final DataSet parameters)
176 throws ReportDataFactoryException
177 {
178 try
179 {
180 final HashMap parameterCache = (HashMap) queryCache.get(query);
181 if (parameterCache == null)
182 {
183 // totally new query here.
184 final HashMap newParams = new HashMap();
185 queryCache.put(query, newParams);
186
187 final Parameters params = new Parameters(parameters);
188 final ReportData newData = backend.queryData(query, params);
189 newParams.put(params, newData);
190 newData.setCursorPosition(ReportData.BEFORE_FIRST_ROW);
191 return newData;
192 }
193 else
194 {
195 // Lookup the parameters ...
196 final Parameters params = new Parameters(parameters);
197 final ReportData data = (ReportData) parameterCache.get(params);
198 if (data != null)
199 {
200 data.setCursorPosition(ReportData.BEFORE_FIRST_ROW);
201 return data;
202 }
203
204 final ReportData newData = backend.queryData(query, params);
205 parameterCache.put(params, newData);
206 newData.setCursorPosition(ReportData.BEFORE_FIRST_ROW);
207 return newData;
208 }
209 }
210 catch (DataSourceException e)
211 {
212 e.printStackTrace();
213 throw new ReportDataFactoryException("Failed to query data", e);
214 }
215 }
216
217 /**
218 * Closes the report data factory and all report data instances that have been returned by this instance.
219 */
220 public void close()
221 {
222 final Iterator queries = queryCache.values().iterator();
223 while (queries.hasNext())
224 {
225 final HashMap map = (HashMap) queries.next();
226 final Iterator dataSets = map.values().iterator();
227 while (dataSets.hasNext())
228 {
229 final ReportData data = (ReportData) dataSets.next();
230 try
231 {
232 data.close();
233 }
234 catch (DataSourceException e)
235 {
236 // ignore, we'll finish up anyway ..
237 }
238 }
239 }
240 backend.close();
241 }
242
243 /**
244 * Derives a freshly initialized report data factory, which is independend of the original data factory. Opening or
245 * Closing one data factory must not affect the other factories.
246 *
247 * @return
248 */
249 public ReportDataFactory derive()
250 {
251 // If you see that exception, then you've probably tried to use that
252 // datafactory from outside of the report processing. You deserve the
253 // exception in that case ..
254 throw new UnsupportedOperationException
255 ("The CachingReportDataFactory cannot be derived.");
256 }
257 }