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: */package org.jfree.report.data;
031:
032: import org.jfree.report.DataFlags;
033: import org.jfree.report.DataRow;
034: import org.jfree.report.DataSourceException;
035: import org.jfree.report.util.LazyNameMap;
036: import org.jfree.util.ObjectUtilities;
037:
038: /**
039: * The global view holds all *named* data columns. Expressions which have no
040: * name will not appear here. There is a slot for each name - if expressions
041: * share the same name, the last name wins.
042: * <p/>
043: * This acts as some kind of global variables heap - which allows named
044: * functions to export their values to a global space.
045: * <p/>
046: * This datarow is optimized for named access - the sequential access is only
047: * generated when absolutly needed.
048: *
049: * @author Thomas Morgner
050: */
051: public final class GlobalView implements DataRow {
052: private static final DataFlags[] EMPTY_DATA_FLAGS = new DataFlags[0];
053: private DataFlags[] oldData;
054: private LazyNameMap oldCache;
055: private DataFlags[] data;
056: private LazyNameMap nameCache;
057: private int length;
058:
059: private GlobalView() {
060: }
061:
062: public static GlobalView createView() {
063: final GlobalView gv = new GlobalView();
064: gv.nameCache = new LazyNameMap();
065: gv.oldCache = new LazyNameMap();
066: gv.data = new DataFlags[10];
067: gv.oldData = EMPTY_DATA_FLAGS;
068: return gv;
069: }
070:
071: private void ensureCapacity(final int requestedSize) {
072: final int capacity = this .data.length;
073: if (capacity > requestedSize) {
074: return;
075: }
076: final int newSize = Math.max(capacity * 2, requestedSize + 10);
077:
078: final DataFlags[] newData = new DataFlags[newSize];
079: System.arraycopy(data, 0, newData, 0, length);
080:
081: this .data = newData;
082: }
083:
084: /**
085: * This adds the expression to the data-row and queries the expression for the
086: * first time.
087: *
088: * @param name the name of the field (cannot be null)
089: * @param value the value of that field (may be null)
090: * @throws DataSourceException
091: */
092: public synchronized void putField(final String name,
093: final Object value, final boolean update)
094: throws DataSourceException {
095: if (name == null) {
096: throw new NullPointerException("Name must not be null.");
097: }
098:
099: final LazyNameMap.NameCarrier nc = nameCache.get(name);
100: final DefaultDataFlags flagedValue = new DefaultDataFlags(name,
101: value, computeChange(name, value));
102: if (nc != null) {
103: this .data[nc.getValue()] = flagedValue;
104: if (update == false) {
105: nc.increase();
106: }
107: return;
108: }
109:
110: // oh fine, a new one ...
111: // step 1: Search for a free slot
112: for (int i = 0; i < length; i++) {
113: final DataFlags dataFlags = data[i];
114: if (dataFlags == null) {
115: data[i] = flagedValue;
116: nameCache.setValue(name, i);
117: return;
118: }
119: }
120:
121: // step 2: No Free Slot, so add
122: ensureCapacity(length + 1);
123: data[length] = flagedValue;
124: nameCache.setValue(name, length);
125: this .length += 1;
126: }
127:
128: private boolean computeChange(final String name,
129: final Object newValue) throws DataSourceException {
130: final LazyNameMap.NameCarrier onc = oldCache.get(name);
131: if (onc == null) {
132: // A new data item, not known before ...
133: return true;
134: }
135:
136: final DataFlags dataFlags = oldData[onc.getValue()];
137: if (dataFlags == null) {
138: return true;
139: }
140: return ObjectUtilities.equal(dataFlags.getValue(), newValue) == false;
141: }
142:
143: /**
144: * Returns the value of the expression or column in the tablemodel using the
145: * given column number as index. For functions and expressions, the
146: * <code>getValue()</code> method is called and for columns from the
147: * tablemodel the tablemodel method <code>getValueAt(row, column)</code> gets
148: * called.
149: *
150: * @param col the item index.
151: * @return the value.
152: * @throws IllegalStateException if the datarow detected a deadlock.
153: */
154: public Object get(final int col) throws DataSourceException {
155: final DataFlags flag = getFlags(col);
156: if (flag == null) {
157: return null;
158: }
159: return flag.getValue();
160: }
161:
162: /**
163: * Returns the value of the function, expression or column using its specific
164: * name. The given name is translated into a valid column number and the the
165: * column is queried. For functions and expressions, the
166: * <code>getValue()</code> method is called and for columns from the
167: * tablemodel the tablemodel method <code>getValueAt(row, column)</code> gets
168: * called.
169: *
170: * @param col the item index.
171: * @return the value.
172: * @throws IllegalStateException if the datarow detected a deadlock.
173: */
174: public Object get(final String col) throws DataSourceException {
175: final DataFlags flag = getFlags(col);
176: if (flag == null) {
177: return null;
178: }
179: return flag.getValue();
180: }
181:
182: /**
183: * Returns the name of the column, expression or function. For columns from
184: * the tablemodel, the tablemodels <code>getColumnName</code> method is
185: * called. For functions, expressions and report properties the assigned name
186: * is returned.
187: *
188: * @param col the item index.
189: * @return the name.
190: */
191: public String getColumnName(final int col) {
192: final DataFlags flag = getFlags(col);
193: if (flag == null) {
194: return null;
195: }
196: return flag.getName();
197: }
198:
199: /**
200: * Returns the number of columns, expressions and functions and marked
201: * ReportProperties in the report.
202: *
203: * @return the item count.
204: */
205: public int getColumnCount() {
206: return length;
207: }
208:
209: public DataFlags getFlags(final String col) {
210: final LazyNameMap.NameCarrier idx = nameCache.get(col);
211: if (idx != null) {
212: final int idxVal = idx.getValue();
213: final DataFlags df = data[idxVal];
214: if (df != null) {
215: return df;
216: }
217: }
218:
219: final LazyNameMap.NameCarrier oidx = oldCache.get(col);
220: if (oidx == null) {
221: return null;
222: }
223:
224: final int oidxVal = oidx.getValue();
225: if (oidxVal < oldData.length) {
226: return oldData[oidxVal];
227: }
228: return null;
229: }
230:
231: public DataFlags getFlags(final int col) {
232: final DataFlags df = data[col];
233: if (df != null) {
234: return df;
235: }
236: return oldData[col];
237: }
238:
239: public GlobalView derive() {
240: final GlobalView gv = new GlobalView();
241: gv.oldCache = (LazyNameMap) oldCache.clone();
242: gv.data = (DataFlags[]) data.clone();
243: gv.oldData = (DataFlags[]) oldData.clone();
244: gv.length = length;
245: gv.nameCache = (LazyNameMap) nameCache.clone();
246: return gv;
247: }
248:
249: public GlobalView advance() {
250: final GlobalView gv = new GlobalView();
251: gv.oldCache = (LazyNameMap) nameCache.clone();
252: gv.oldData = (DataFlags[]) data.clone();
253: gv.data = new DataFlags[gv.oldData.length];
254: gv.length = length;
255: gv.nameCache = new LazyNameMap();
256: return gv;
257: }
258:
259: /**
260: * Note: Dont remove the column. It will stay around here as long as the
261: * process lives.
262: *
263: * @param name
264: */
265: public synchronized void removeColumn(final String name) {
266: final LazyNameMap.NameCarrier idx = nameCache.get(name);
267: if (idx == null) {
268: return;
269: }
270: idx.decrease();
271: if (idx.getInstanceCount() < 1) {
272: nameCache.remove(name);
273: data[idx.getValue()] = null;
274:
275: // todo: In a sane world, we would now start to reindex the whole thing.
276: }
277: }
278:
279: }
|