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 2890 2007-06-10 15:54:22Z taqua $ 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 }