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 3525 2007-10-16 11:43:48Z tmorgner $ 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 static final DataFlags[] EMPTY_DATA_FLAGS = new DataFlags[0]; 055 private DataFlags[] oldData; 056 private LazyNameMap oldCache; 057 private DataFlags[] data; 058 private LazyNameMap nameCache; 059 private int length; 060 061 private GlobalView() 062 { 063 } 064 065 public static GlobalView createView() 066 { 067 final GlobalView gv = new GlobalView(); 068 gv.nameCache = new LazyNameMap(); 069 gv.oldCache = new LazyNameMap(); 070 gv.data = new DataFlags[10]; 071 gv.oldData = EMPTY_DATA_FLAGS; 072 return gv; 073 } 074 075 076 private void ensureCapacity(final int requestedSize) 077 { 078 final int capacity = this.data.length; 079 if (capacity > requestedSize) 080 { 081 return; 082 } 083 final int newSize = Math.max(capacity * 2, requestedSize + 10); 084 085 final DataFlags[] newData = new DataFlags[newSize]; 086 System.arraycopy(data, 0, newData, 0, length); 087 088 this.data = newData; 089 } 090 091 /** 092 * This adds the expression to the data-row and queries the expression for the 093 * first time. 094 * 095 * @param name the name of the field (cannot be null) 096 * @param value the value of that field (may be null) 097 * @throws DataSourceException 098 */ 099 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 }