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: GlobalView.java,v 1.6 2007/04/01 18:49:24 taqua Exp $ 027 * ------------ 028 * (C) Copyright 2000-2005, by Object Refinery Limited. 029 * (C) Copyright 2005-2007, by Pentaho Corporation. 030 */ 031 package org.jfree.report.data; 032 033 import org.jfree.report.DataFlags; 034 import org.jfree.report.DataRow; 035 import org.jfree.report.DataSourceException; 036 import org.jfree.report.util.LazyNameMap; 037 import org.jfree.util.ObjectUtilities; 038 039 /** 040 * The global view holds all *named* data columns. Expressions which have no 041 * name will not appear here. There is a slot for each name - if expressions 042 * share the same name, the last name wins. 043 * <p/> 044 * This acts as some kind of global variables heap - which allows named 045 * functions to export their values to a global space. 046 * <p/> 047 * This datarow is optimized for named access - the sequential access is only 048 * generated when absolutly needed. 049 * 050 * @author Thomas Morgner 051 */ 052 public final class GlobalView implements DataRow 053 { 054 private DataFlags[] oldData; 055 private LazyNameMap oldCache; 056 private DataFlags[] data; 057 private LazyNameMap nameCache; 058 private int length; 059 060 private GlobalView() 061 { 062 } 063 064 public static GlobalView createView() 065 { 066 GlobalView gv = new GlobalView(); 067 gv.nameCache = new LazyNameMap(); 068 gv.oldCache = new LazyNameMap(); 069 gv.data = new DataFlags[10]; 070 gv.oldData = new DataFlags[0]; 071 return gv; 072 } 073 074 075 private void ensureCapacity(int requestedSize) 076 { 077 final int capacity = this.data.length; 078 if (capacity > requestedSize) 079 { 080 return; 081 } 082 final int newSize = Math.max(capacity * 2, requestedSize + 10); 083 084 final DataFlags[] newData = new DataFlags[newSize]; 085 System.arraycopy(data, 0, newData, 0, length); 086 087 this.data = newData; 088 } 089 090 /** 091 * This adds the expression to the data-row and queries the expression for the 092 * first time. 093 * 094 * @param name the name of the field (cannot be null) 095 * @param value the value of that field (may be null) 096 * @throws DataSourceException 097 */ 098 public synchronized void putField(final String name, 099 final Object value, 100 final boolean update) 101 throws DataSourceException 102 { 103 if (name == null) 104 { 105 throw new NullPointerException("Name must not be null."); 106 } 107 108 final LazyNameMap.NameCarrier nc = nameCache.get(name); 109 final DefaultDataFlags flagedValue = new DefaultDataFlags 110 (name, value, computeChange(name, value)); 111 if (nc != null) 112 { 113 this.data[nc.getValue()] = flagedValue; 114 if (update == false) 115 { 116 nc.increase(); 117 } 118 return; 119 } 120 121 // oh fine, a new one ... 122 // step 1: Search for a free slot 123 for (int i = 0; i < length; i++) 124 { 125 DataFlags dataFlags = data[i]; 126 if (dataFlags == null) 127 { 128 data[i] = flagedValue; 129 nameCache.setValue(name, i); 130 return; 131 } 132 } 133 134 // step 2: No Free Slot, so add 135 ensureCapacity(length + 1); 136 data[length] = flagedValue; 137 nameCache.setValue(name, length); 138 this.length += 1; 139 } 140 141 private boolean computeChange(String name, Object newValue) 142 throws DataSourceException 143 { 144 final LazyNameMap.NameCarrier onc = oldCache.get(name); 145 if (onc == null) 146 { 147 // A new data item, not known before ... 148 return true; 149 } 150 151 final DataFlags dataFlags = oldData[onc.getValue()]; 152 if (dataFlags == null) 153 { 154 return true; 155 } 156 return ObjectUtilities.equal(dataFlags.getValue(), newValue) == false; 157 } 158 159 /** 160 * Returns the value of the expression or column in the tablemodel using the 161 * given column number as index. For functions and expressions, the 162 * <code>getValue()</code> method is called and for columns from the 163 * tablemodel the tablemodel method <code>getValueAt(row, column)</code> gets 164 * called. 165 * 166 * @param col the item index. 167 * @return the value. 168 * @throws IllegalStateException if the datarow detected a deadlock. 169 */ 170 public Object get(int col) throws DataSourceException 171 { 172 final DataFlags flag = getFlags(col); 173 if (flag == null) 174 { 175 return null; 176 } 177 return flag.getValue(); 178 } 179 180 /** 181 * Returns the value of the function, expression or column using its specific 182 * name. The given name is translated into a valid column number and the the 183 * column is queried. For functions and expressions, the 184 * <code>getValue()</code> method is called and for columns from the 185 * tablemodel the tablemodel method <code>getValueAt(row, column)</code> gets 186 * called. 187 * 188 * @param col the item index. 189 * @return the value. 190 * @throws IllegalStateException if the datarow detected a deadlock. 191 */ 192 public Object get(String col) throws DataSourceException 193 { 194 final DataFlags flag = getFlags(col); 195 if (flag == null) 196 { 197 return null; 198 } 199 return flag.getValue(); 200 } 201 202 /** 203 * Returns the name of the column, expression or function. For columns from 204 * the tablemodel, the tablemodels <code>getColumnName</code> method is 205 * called. For functions, expressions and report properties the assigned name 206 * is returned. 207 * 208 * @param col the item index. 209 * @return the name. 210 */ 211 public String getColumnName(int col) 212 { 213 final DataFlags flag = getFlags(col); 214 if (flag == null) 215 { 216 return null; 217 } 218 return flag.getName(); 219 } 220 221 /** 222 * Returns the number of columns, expressions and functions and marked 223 * ReportProperties in the report. 224 * 225 * @return the item count. 226 */ 227 public int getColumnCount() 228 { 229 return length; 230 } 231 232 public DataFlags getFlags(String col) 233 { 234 final LazyNameMap.NameCarrier idx = nameCache.get(col); 235 if (idx != null) 236 { 237 final int idxVal = idx.getValue(); 238 final DataFlags df = data[idxVal]; 239 if (df != null) 240 { 241 return df; 242 } 243 } 244 245 final LazyNameMap.NameCarrier oidx = oldCache.get(col); 246 if (oidx == null) 247 { 248 return null; 249 } 250 251 final int oidxVal = oidx.getValue(); 252 if (oidxVal < oldData.length) 253 { 254 return oldData[oidxVal]; 255 } 256 return null; 257 } 258 259 public DataFlags getFlags(int col) 260 { 261 final DataFlags df = data[col]; 262 if (df != null) 263 { 264 return df; 265 } 266 return oldData[col]; 267 } 268 269 public GlobalView derive() 270 { 271 GlobalView gv = new GlobalView(); 272 gv.oldCache = (LazyNameMap) oldCache.clone(); 273 gv.data = (DataFlags[]) data.clone(); 274 gv.oldData = (DataFlags[]) oldData.clone(); 275 gv.length = length; 276 gv.nameCache = (LazyNameMap) nameCache.clone(); 277 return gv; 278 } 279 280 public GlobalView advance() 281 { 282 GlobalView gv = new GlobalView(); 283 gv.oldCache = (LazyNameMap) nameCache.clone(); 284 gv.oldData = (DataFlags[]) data.clone(); 285 gv.data = new DataFlags[gv.oldData.length]; 286 gv.length = length; 287 gv.nameCache = new LazyNameMap(); 288 return gv; 289 } 290 291 /** 292 * Note: Dont remove the column. It will stay around here as long as the 293 * process lives. 294 * 295 * @param name 296 */ 297 public synchronized void removeColumn(String name) 298 { 299 final LazyNameMap.NameCarrier idx = nameCache.get(name); 300 if (idx == null) 301 { 302 return; 303 } 304 idx.decrease(); 305 if (idx.getInstanceCount() < 1) 306 { 307 nameCache.remove(name); 308 data[idx.getValue()] = null; 309 310 // todo: In a sane world, we would now start to reindex the whole thing. 311 } 312 } 313 314 }