Source for org.jfree.report.data.CachingReportDataFactory

   1: /**
   2:  * ========================================
   3:  * JFreeReport : a free Java report library
   4:  * ========================================
   5:  *
   6:  * Project Info:  http://reporting.pentaho.org/
   7:  *
   8:  * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
   9:  *
  10:  * This library is free software; you can redistribute it and/or modify it under the terms
  11:  * of the GNU Lesser General Public License as published by the Free Software Foundation;
  12:  * either version 2.1 of the License, or (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  15:  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16:  * See the GNU Lesser General Public License for more details.
  17:  *
  18:  * You should have received a copy of the GNU Lesser General Public License along with this
  19:  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  20:  * Boston, MA 02111-1307, USA.
  21:  *
  22:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  23:  * in the United States and other countries.]
  24:  *
  25:  * ------------
  26:  * $Id: CachingReportDataFactory.java 2890 2007-06-10 15:54:22Z taqua $
  27:  * ------------
  28:  * (C) Copyright 2000-2005, by Object Refinery Limited.
  29:  * (C) Copyright 2005-2007, by Pentaho Corporation.
  30:  */
  31: 
  32: package org.jfree.report.data;
  33: 
  34: import java.util.Arrays;
  35: import java.util.HashMap;
  36: import java.util.Iterator;
  37: 
  38: import org.jfree.report.DataSet;
  39: import org.jfree.report.DataSourceException;
  40: import org.jfree.report.ReportData;
  41: import org.jfree.report.ReportDataFactory;
  42: import org.jfree.report.ReportDataFactoryException;
  43: 
  44: /**
  45:  * Creation-Date: 19.11.2006, 13:35:45
  46:  *
  47:  * @author Thomas Morgner
  48:  */
  49: public class CachingReportDataFactory implements ReportDataFactory
  50: {
  51:   private static class Parameters implements DataSet
  52:   {
  53:     private Object[] dataStore;
  54:     private String[] nameStore;
  55:     private Integer hashCode;
  56: 
  57:     protected Parameters(final DataSet dataSet) throws DataSourceException
  58:     {
  59:       final int columnCount = dataSet.getColumnCount();
  60:       dataStore = new Object[columnCount];
  61:       nameStore = new String[columnCount];
  62: 
  63:       for (int i = 0; i < columnCount; i++)
  64:       {
  65:         nameStore[i] = dataSet.getColumnName(i);
  66:         dataStore[i] = dataSet.get(i);
  67:       }
  68:     }
  69: 
  70:     public int getColumnCount() throws DataSourceException
  71:     {
  72:       return dataStore.length;
  73:     }
  74: 
  75:     public String getColumnName(final int column) throws DataSourceException
  76:     {
  77:       return nameStore[column];
  78:     }
  79: 
  80:     public Object get(final int column) throws DataSourceException
  81:     {
  82:       return dataStore[column];
  83:     }
  84: 
  85:     public boolean equals(final Object o)
  86:     {
  87:       if (this == o)
  88:       {
  89:         return true;
  90:       }
  91:       if (o == null || getClass() != o.getClass())
  92:       {
  93:         return false;
  94:       }
  95: 
  96:       final Parameters that = (Parameters) o;
  97: 
  98:       if (!Arrays.equals(dataStore, that.dataStore))
  99:       {
 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: }