Source for org.jfree.report.data.GlobalView

   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: GlobalView.java 3525 2007-10-16 11:43:48Z tmorgner $
  27:  * ------------
  28:  * (C) Copyright 2000-2005, by Object Refinery Limited.
  29:  * (C) Copyright 2005-2007, by Pentaho Corporation.
  30:  */
  31: package org.jfree.report.data;
  32: 
  33: import org.jfree.report.DataFlags;
  34: import org.jfree.report.DataRow;
  35: import org.jfree.report.DataSourceException;
  36: import org.jfree.report.util.LazyNameMap;
  37: import org.jfree.util.ObjectUtilities;
  38: 
  39: /**
  40:  * The global view holds all *named* data columns. Expressions which have no
  41:  * name will not appear here. There is a slot for each name - if expressions
  42:  * share the same name, the last name wins.
  43:  * <p/>
  44:  * This acts as some kind of global variables heap - which allows named
  45:  * functions to export their values to a global space.
  46:  * <p/>
  47:  * This datarow is optimized for named access - the sequential access is only
  48:  * generated when absolutly needed.
  49:  *
  50:  * @author Thomas Morgner
  51:  */
  52: public final class GlobalView implements DataRow
  53: {
  54:   private static final DataFlags[] EMPTY_DATA_FLAGS = new DataFlags[0];
  55:   private DataFlags[] oldData;
  56:   private LazyNameMap oldCache;
  57:   private DataFlags[] data;
  58:   private LazyNameMap nameCache;
  59:   private int length;
  60: 
  61:   private GlobalView()
  62:   {
  63:   }
  64: 
  65:   public static GlobalView createView()
  66:   {
  67:     final GlobalView gv = new GlobalView();
  68:     gv.nameCache = new LazyNameMap();
  69:     gv.oldCache = new LazyNameMap();
  70:     gv.data = new DataFlags[10];
  71:     gv.oldData = EMPTY_DATA_FLAGS;
  72:     return gv;
  73:   }
  74: 
  75: 
  76:   private void ensureCapacity(final int requestedSize)
  77:   {
  78:     final int capacity = this.data.length;
  79:     if (capacity > requestedSize)
  80:     {
  81:       return;
  82:     }
  83:     final int newSize = Math.max(capacity * 2, requestedSize + 10);
  84: 
  85:     final DataFlags[] newData = new DataFlags[newSize];
  86:     System.arraycopy(data, 0, newData, 0, length);
  87: 
  88:     this.data = newData;
  89:   }
  90: 
  91:   /**
  92:    * This adds the expression to the data-row and queries the expression for the
  93:    * first time.
  94:    *
  95:    * @param name  the name of the field (cannot be null)
  96:    * @param value the value of that field (may be null)
  97:    * @throws DataSourceException
  98:    */
  99:   public synchronized void putField(final String name,
 100:                                     final Object value,
 101:                                     final boolean update)
 102:       throws DataSourceException
 103:   {
 104:     if (name == null)
 105:     {
 106:       throw new NullPointerException("Name must not be null.");
 107:     }
 108: 
 109:     final LazyNameMap.NameCarrier nc = nameCache.get(name);
 110:     final DefaultDataFlags flagedValue = new DefaultDataFlags
 111:         (name, value, computeChange(name, value));
 112:     if (nc != null)
 113:     {
 114:       this.data[nc.getValue()] = flagedValue;
 115:       if (update == false)
 116:       {
 117:         nc.increase();
 118:       }
 119:       return;
 120:     }
 121: 
 122:     // oh fine, a new one ...
 123:     // step 1: Search for a free slot
 124:     for (int i = 0; i < length; i++)
 125:     {
 126:       final DataFlags dataFlags = data[i];
 127:       if (dataFlags == null)
 128:       {
 129:         data[i] = flagedValue;
 130:         nameCache.setValue(name, i);
 131:         return;
 132:       }
 133:     }
 134: 
 135:     // step 2: No Free Slot, so add
 136:     ensureCapacity(length + 1);
 137:     data[length] = flagedValue;
 138:     nameCache.setValue(name, length);
 139:     this.length += 1;
 140:   }
 141: 
 142:   private boolean computeChange(final String name, final Object newValue)
 143:       throws DataSourceException
 144:   {
 145:     final LazyNameMap.NameCarrier onc = oldCache.get(name);
 146:     if (onc == null)
 147:     {
 148:       // A new data item, not known before ...
 149:       return true;
 150:     }
 151: 
 152:     final DataFlags dataFlags = oldData[onc.getValue()];
 153:     if (dataFlags == null)
 154:     {
 155:       return true;
 156:     }
 157:     return ObjectUtilities.equal(dataFlags.getValue(), newValue) == false;
 158:   }
 159: 
 160:   /**
 161:    * Returns the value of the expression or column in the tablemodel using the
 162:    * given column number as index. For functions and expressions, the
 163:    * <code>getValue()</code> method is called and for columns from the
 164:    * tablemodel the tablemodel method <code>getValueAt(row, column)</code> gets
 165:    * called.
 166:    *
 167:    * @param col the item index.
 168:    * @return the value.
 169:    * @throws IllegalStateException if the datarow detected a deadlock.
 170:    */
 171:   public Object get(final int col) throws DataSourceException
 172:   {
 173:     final DataFlags flag = getFlags(col);
 174:     if (flag == null)
 175:     {
 176:       return null;
 177:     }
 178:     return flag.getValue();
 179:   }
 180: 
 181:   /**
 182:    * Returns the value of the function, expression or column using its specific
 183:    * name. The given name is translated into a valid column number and the the
 184:    * column is queried. For functions and expressions, the
 185:    * <code>getValue()</code> method is called and for columns from the
 186:    * tablemodel the tablemodel method <code>getValueAt(row, column)</code> gets
 187:    * called.
 188:    *
 189:    * @param col the item index.
 190:    * @return the value.
 191:    * @throws IllegalStateException if the datarow detected a deadlock.
 192:    */
 193:   public Object get(final String col) throws DataSourceException
 194:   {
 195:     final DataFlags flag = getFlags(col);
 196:     if (flag == null)
 197:     {
 198:       return null;
 199:     }
 200:     return flag.getValue();
 201:   }
 202: 
 203:   /**
 204:    * Returns the name of the column, expression or function. For columns from
 205:    * the tablemodel, the tablemodels <code>getColumnName</code> method is
 206:    * called. For functions, expressions and report properties the assigned name
 207:    * is returned.
 208:    *
 209:    * @param col the item index.
 210:    * @return the name.
 211:    */
 212:   public String getColumnName(final int col)
 213:   {
 214:     final DataFlags flag = getFlags(col);
 215:     if (flag == null)
 216:     {
 217:       return null;
 218:     }
 219:     return flag.getName();
 220:   }
 221: 
 222:   /**
 223:    * Returns the number of columns, expressions and functions and marked
 224:    * ReportProperties in the report.
 225:    *
 226:    * @return the item count.
 227:    */
 228:   public int getColumnCount()
 229:   {
 230:     return length;
 231:   }
 232: 
 233:   public DataFlags getFlags(final String col)
 234:   {
 235:     final LazyNameMap.NameCarrier idx = nameCache.get(col);
 236:     if (idx != null)
 237:     {
 238:       final int idxVal = idx.getValue();
 239:       final DataFlags df = data[idxVal];
 240:       if (df != null)
 241:       {
 242:         return df;
 243:       }
 244:     }
 245: 
 246:     final LazyNameMap.NameCarrier oidx = oldCache.get(col);
 247:     if (oidx == null)
 248:     {
 249:       return null;
 250:     }
 251: 
 252:     final int oidxVal = oidx.getValue();
 253:     if (oidxVal < oldData.length)
 254:     {
 255:       return oldData[oidxVal];
 256:     }
 257:     return null;
 258:   }
 259: 
 260:   public DataFlags getFlags(final int col)
 261:   {
 262:     final DataFlags df = data[col];
 263:     if (df != null)
 264:     {
 265:       return df;
 266:     }
 267:     return oldData[col];
 268:   }
 269: 
 270:   public GlobalView derive()
 271:   {
 272:     final GlobalView gv = new GlobalView();
 273:     gv.oldCache = (LazyNameMap) oldCache.clone();
 274:     gv.data = (DataFlags[]) data.clone();
 275:     gv.oldData = (DataFlags[]) oldData.clone();
 276:     gv.length = length;
 277:     gv.nameCache = (LazyNameMap) nameCache.clone();
 278:     return gv;
 279:   }
 280: 
 281:   public GlobalView advance()
 282:   {
 283:     final GlobalView gv = new GlobalView();
 284:     gv.oldCache = (LazyNameMap) nameCache.clone();
 285:     gv.oldData = (DataFlags[]) data.clone();
 286:     gv.data = new DataFlags[gv.oldData.length];
 287:     gv.length = length;
 288:     gv.nameCache = new LazyNameMap();
 289:     return gv;
 290:   }
 291: 
 292:   /**
 293:    * Note: Dont remove the column. It will stay around here as long as the
 294:    * process lives.
 295:    *
 296:    * @param name
 297:    */
 298:   public synchronized void removeColumn(final String name)
 299:   {
 300:     final LazyNameMap.NameCarrier idx = nameCache.get(name);
 301:     if (idx == null)
 302:     {
 303:       return;
 304:     }
 305:     idx.decrease();
 306:     if (idx.getInstanceCount() < 1)
 307:     {
 308:       nameCache.remove(name);
 309:       data[idx.getValue()] = null;
 310: 
 311:       // todo: In a sane world, we would now start to reindex the whole thing.
 312:     }
 313:   }
 314: 
 315: }