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,v 1.10 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 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 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(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 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 String originalName = expressions[length - 1].getName();
179 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 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(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(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(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(String col)
279 {
280 throw new UnsupportedOperationException();
281 }
282
283 public DataFlags getFlags(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 ExpressionSlot expressionSlot = edr.expressions[i];
310 expressionSlot.updateDataRow(master.getGlobalView());
311 if (deepTraversing == false ||
312 (deepTraversing && 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 ExpressionSlot[] slots = new ExpressionSlot[length];
352 System.arraycopy(expressions, 0, slots, 0, length);
353 return slots;
354 }
355 }