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 }