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    }