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: ExpressionDataRow.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 java.util.HashMap; 034 035 import org.jfree.report.DataFlags; 036 import org.jfree.report.DataRow; 037 import org.jfree.report.DataSourceException; 038 import org.jfree.report.flow.ReportContext; 039 import org.jfree.util.Log; 040 041 /** 042 * A datarow for all expressions encountered in the report. This datarow is a 043 * stack-like structure, which allows easy adding and removing of expressions, 044 * even if these expressions have been cloned and or otherwisely modified. 045 * 046 * @author Thomas Morgner 047 */ 048 public final class ExpressionDataRow implements DataRow 049 { 050 private ExpressionSlot[] expressions; 051 private int length; 052 private HashMap nameCache; 053 private GlobalMasterRow masterRow; 054 private ReportContext reportContext; 055 056 public ExpressionDataRow(final GlobalMasterRow masterRow, 057 final ReportContext reportContext, 058 final int capacity) 059 { 060 this.masterRow = masterRow; 061 this.nameCache = new HashMap(capacity); 062 this.expressions = new ExpressionSlot[capacity]; 063 this.reportContext = reportContext; 064 } 065 066 private ExpressionDataRow(final GlobalMasterRow masterRow, 067 final ExpressionDataRow previousRow) 068 throws CloneNotSupportedException 069 { 070 this.reportContext = previousRow.reportContext; 071 this.masterRow = masterRow; 072 this.nameCache = (HashMap) previousRow.nameCache.clone(); 073 this.expressions = new ExpressionSlot[previousRow.expressions.length]; 074 this.length = previousRow.length; 075 for (int i = 0; i < length; i++) 076 { 077 final ExpressionSlot expression = previousRow.expressions[i]; 078 if (expression == null) 079 { 080 Log.debug("Error: Expression is null..."); 081 } 082 else 083 { 084 expressions[i] = (ExpressionSlot) expression.clone(); 085 } 086 } 087 } 088 089 private void ensureCapacity(final int requestedSize) 090 { 091 final int capacity = this.expressions.length; 092 if (capacity > requestedSize) 093 { 094 return; 095 } 096 final int newSize = Math.max(capacity * 2, requestedSize + 10); 097 098 final ExpressionSlot[] newExpressions = new ExpressionSlot[newSize]; 099 100 System.arraycopy(expressions, 0, newExpressions, 0, length); 101 102 this.expressions = newExpressions; 103 } 104 105 /** 106 * This adds the expression to the data-row and queries the expression for the 107 * first time. 108 * 109 * @param ex 110 * @param rd 111 * @throws DataSourceException 112 */ 113 public synchronized void pushExpression(final ExpressionSlot expressionSlot) 114 throws DataSourceException 115 { 116 if (expressionSlot == null) 117 { 118 throw new NullPointerException(); 119 } 120 121 ensureCapacity(length + 1); 122 123 this.expressions[length] = expressionSlot; 124 final String name = expressionSlot.getName(); 125 if (name != null) 126 { 127 nameCache.put(name, expressionSlot); 128 } 129 length += 1; 130 131 expressionSlot.updateDataRow(masterRow.getGlobalView()); 132 // A manual advance to initialize the function. 133 expressionSlot.advance(); 134 if (name != null) 135 { 136 final Object value = expressionSlot.getValue(); 137 final MasterDataRowChangeEvent chEvent = new MasterDataRowChangeEvent 138 (MasterDataRowChangeEvent.COLUMN_ADDED, name, value); 139 masterRow.dataRowChanged(chEvent); 140 } 141 } 142 143 public synchronized void pushExpressions(final ExpressionSlot[] expressionSlots) 144 throws DataSourceException 145 { 146 if (expressionSlots == null) 147 { 148 throw new NullPointerException(); 149 } 150 151 ensureCapacity(length + expressionSlots.length); 152 for (int i = 0; i < expressionSlots.length; i++) 153 { 154 final ExpressionSlot expression = expressionSlots[i]; 155 if (expression == null) 156 { 157 continue; 158 } 159 pushExpression(expression); 160 } 161 } 162 163 public synchronized void popExpressions(final int counter) throws 164 DataSourceException 165 { 166 for (int i = 0; i < counter; i++) 167 { 168 popExpression(); 169 } 170 } 171 172 public synchronized void popExpression() throws DataSourceException 173 { 174 if (length == 0) 175 { 176 return; 177 } 178 final String originalName = expressions[length - 1].getName(); 179 final boolean preserve = expressions[length - 1].isPreserve(); 180 this.expressions[length - 1] = null; 181 this.length -= 1; 182 if (originalName != null) 183 { 184 int otherIndex = -1; 185 for (int i = length - 1; i >= 0; i -= 1) 186 { 187 final ExpressionSlot expression = expressions[i]; 188 if (originalName.equals(expression.getName())) 189 { 190 otherIndex = i; 191 break; 192 } 193 } 194 if (otherIndex == -1) 195 { 196 nameCache.remove(originalName); 197 } 198 else 199 { 200 nameCache.put(originalName, expressions[otherIndex]); 201 } 202 203 if (preserve == false) 204 { 205 final MasterDataRowChangeEvent chEvent = new MasterDataRowChangeEvent 206 (MasterDataRowChangeEvent.COLUMN_REMOVED, originalName, null); 207 masterRow.dataRowChanged(chEvent); 208 } 209 // for preserved elements we do not send an remove-event. 210 } 211 212 } 213 214 /** 215 * Returns the value of the expressions or column in the tablemodel using the 216 * given column number as index. For functions and expressions, the 217 * <code>getValue()</code> method is called and for columns from the 218 * tablemodel the tablemodel method <code>getValueAt(row, column)</code> gets 219 * called. 220 * 221 * @param col the item index. 222 * @return the value. 223 * @throws IllegalStateException if the datarow detected a deadlock. 224 */ 225 public Object get(final int col) throws DataSourceException 226 { 227 return expressions[col].getValue(); 228 } 229 230 /** 231 * Returns the value of the function, expressions or column using its specific 232 * name. The given name is translated into a valid column number and the the 233 * column is queried. For functions and expressions, the 234 * <code>getValue()</code> method is called and for columns from the 235 * tablemodel the tablemodel method <code>getValueAt(row, column)</code> gets 236 * called. 237 * 238 * @param col the item index. 239 * @return the value. 240 * @throws IllegalStateException if the datarow detected a deadlock. 241 */ 242 public Object get(final String col) throws DataSourceException 243 { 244 final ExpressionSlot es = (ExpressionSlot) nameCache.get(col); 245 if (es == null) 246 { 247 return null; 248 } 249 250 return es.getValue(); 251 } 252 253 /** 254 * Returns the name of the column, expressions or function. For columns from 255 * the tablemodel, the tablemodels <code>getColumnName</code> method is 256 * called. For functions, expressions and report properties the assigned name 257 * is returned. 258 * 259 * @param col the item index. 260 * @return the name. 261 */ 262 public String getColumnName(final int col) 263 { 264 return expressions[col].getName(); 265 } 266 267 /** 268 * Returns the number of columns, expressions and functions and marked 269 * ReportProperties in the report. 270 * 271 * @return the item count. 272 */ 273 public int getColumnCount() 274 { 275 return length; 276 } 277 278 public DataFlags getFlags(final String col) 279 { 280 throw new UnsupportedOperationException(); 281 } 282 283 public DataFlags getFlags(final int col) 284 { 285 throw new UnsupportedOperationException(); 286 } 287 288 /** 289 * Advances to the next row and attaches the given master row to the objects 290 * contained in that client data row. 291 * 292 * @param master 293 * @param deepTraversing only advance expressions that have been marked as 294 * deeply traversing 295 * @return 296 */ 297 public ExpressionDataRow advance(final GlobalMasterRow master, 298 final boolean deepTraversing) 299 throws DataSourceException 300 { 301 try 302 { 303 final ExpressionDataRow edr = new ExpressionDataRow(master, this); 304 305 // It is defined, that new expressions get evaluated before any older 306 // expression. 307 for (int i = edr.length - 1; i >= 0; i--) 308 { 309 final ExpressionSlot expressionSlot = edr.expressions[i]; 310 expressionSlot.updateDataRow(master.getGlobalView()); 311 if (deepTraversing == false || 312 (expressionSlot.isDeepTraversing())) 313 { 314 expressionSlot.advance(); 315 } 316 // Query the value (once per advance) .. 317 final Object value = expressionSlot.getValue(); 318 final String name = expressionSlot.getName(); 319 if (name != null) 320 { 321 final MasterDataRowChangeEvent chEvent = new MasterDataRowChangeEvent 322 (MasterDataRowChangeEvent.COLUMN_UPDATED, name, value); 323 master.dataRowChanged(chEvent); 324 } 325 } 326 return edr; 327 } 328 catch (CloneNotSupportedException e) 329 { 330 throw new DataSourceException("Cloning failed", e); 331 } 332 } 333 334 public ExpressionDataRow derive(final GlobalMasterRow master) 335 throws DataSourceException 336 { 337 try 338 { 339 return new ExpressionDataRow(master, this); 340 } 341 catch (CloneNotSupportedException e) 342 { 343 throw new DataSourceException("Cloning failed", e); 344 } 345 } 346 347 public ExpressionSlot[] getSlots() 348 { 349 // to be totally safe from any tampering, we would have to do some sort of 350 // deep-copy here. 351 final ExpressionSlot[] slots = new ExpressionSlot[length]; 352 System.arraycopy(expressions, 0, slots, 0, length); 353 return slots; 354 } 355 }