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 }