001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, 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: * FastGlobalView.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.states.datarow;
030:
031: import java.util.Arrays;
032: import java.util.HashMap;
033: import java.util.HashSet;
034:
035: import org.jfree.report.DataRow;
036: import org.jfree.report.JFreeReportBoot;
037: import org.jfree.report.util.IntegerCache;
038: import org.jfree.util.Log;
039: import org.jfree.util.ObjectUtilities;
040:
041: /**
042: * Creation-Date: 10.08.2007, 14:07:32
043: *
044: * @author Thomas Morgner
045: */
046: public final class FastGlobalView implements DataRow {
047: private HashSet invalidColumns;
048: private boolean modifiableNameCache;
049: private HashMap nameCache;
050: private String[] columnNames;
051: private Boolean[] columnChanged;
052: private Object[] columnValue;
053: private Object[] columnOldValue;
054: private int[] columnPrev;
055: private int length;
056: private static final boolean DEBUG = false;
057: private boolean warnInvalidColumns;
058:
059: public FastGlobalView(final FastGlobalView parent) {
060: if (parent.modifiableNameCache) {
061: this .nameCache = (HashMap) parent.nameCache.clone();
062: this .modifiableNameCache = false;
063: this .columnNames = (String[]) parent.columnNames.clone();
064: } else {
065: this .nameCache = parent.nameCache;
066: this .columnNames = parent.columnNames;
067: this .modifiableNameCache = false;
068: }
069: this .columnChanged = (Boolean[]) parent.columnChanged.clone();
070: this .columnValue = (Object[]) parent.columnValue.clone();
071: this .columnOldValue = (Object[]) parent.columnOldValue.clone();
072: this .columnPrev = (int[]) parent.columnPrev.clone();
073: this .length = parent.length;
074: this .warnInvalidColumns = parent.warnInvalidColumns;
075: this .invalidColumns = parent.invalidColumns;
076: }
077:
078: public FastGlobalView() {
079: this .warnInvalidColumns = "true".equals(JFreeReportBoot
080: .getInstance().getGlobalConfig().getConfigProperty(
081: "org.jfree.report.WarnInvalidColumns"));
082: if (warnInvalidColumns) {
083: this .invalidColumns = new HashSet();
084: }
085: this .nameCache = new HashMap();
086: this .modifiableNameCache = true;
087: this .columnNames = new String[20];
088: this .columnChanged = new Boolean[20];
089: this .columnValue = new Object[20];
090: this .columnOldValue = new Object[20];
091: this .columnPrev = new int[20];
092: }
093:
094: public Object get(final int col) {
095: if (col < 0 || col >= length) {
096: throw new IndexOutOfBoundsException("Column-Index " + col
097: + " is invalid.");
098: }
099: return columnValue[col];
100: }
101:
102: public Object get(final String col) throws IllegalStateException {
103: final int idx = findColumn(col);
104: if (idx < 0) {
105: if (warnInvalidColumns) {
106: if (invalidColumns.add(col)) {
107: Log
108: .warn("Warning: Data-Set does not contain a column with name '"
109: + col + "'");
110: }
111: }
112: return null;
113: }
114: return get(idx);
115: }
116:
117: public String getColumnName(final int col) {
118: if (col < 0 || col >= length) {
119: throw new IndexOutOfBoundsException("Column-Index " + col
120: + " is invalid.");
121: }
122: return columnNames[col];
123: }
124:
125: public int findColumn(final String name) {
126: final Integer o = (Integer) nameCache.get(name);
127: if (o == null) {
128: return -1;
129: }
130: return o.intValue();
131: }
132:
133: public int getColumnCount() {
134: return length;
135: }
136:
137: public boolean isChanged(final String name) {
138: final int idx = findColumn(name);
139: if (idx < 0) {
140: if (warnInvalidColumns) {
141: if (invalidColumns.add(name)) {
142: Log
143: .warn("Warning: Data-Set does not contain a column with name '"
144: + name + "'");
145: }
146: }
147: return false;
148: }
149: return isChanged(idx);
150: }
151:
152: public boolean isChanged(final int col) {
153: if (col < 0 || col >= length) {
154: throw new IndexOutOfBoundsException("Column-Index " + col
155: + " is invalid.");
156: }
157: final Boolean val = columnChanged[col];
158: if (val != null) {
159: return val.booleanValue();
160: }
161: final Boolean computedFlag = computeChangedFlag(col);
162: columnChanged[col] = computedFlag;
163: return computedFlag.booleanValue();
164: }
165:
166: private Boolean computeChangedFlag(final int col) {
167: Log
168: .debug("Computing these values should be no longer necessary.");
169: // todo
170: return Boolean.FALSE;
171: }
172:
173: public FastGlobalView derive() {
174: return new FastGlobalView(this );
175: }
176:
177: public FastGlobalView advance() {
178: final FastGlobalView advanced = new FastGlobalView(this );
179: Arrays.fill(advanced.columnChanged, null);
180: System.arraycopy(advanced.columnValue, 0,
181: advanced.columnOldValue, 0, length);
182: return advanced;
183: }
184:
185: public void removeColumn(final String name) {
186: final Integer o = (Integer) nameCache.get(name);
187: if (o == null) {
188: return;
189: }
190: final int idx = o.intValue();
191: if (DEBUG) {
192: Log.debug("Removing column " + name + " (Length: " + length
193: + " NameCache: " + nameCache.size() + ", Idx: "
194: + idx);
195: }
196:
197: if (modifiableNameCache == false) {
198: this .columnNames = (String[]) columnNames.clone();
199: this .nameCache = (HashMap) nameCache.clone();
200: this .modifiableNameCache = true;
201: }
202:
203: if (idx == (length - 1)) {
204: columnChanged[idx] = null;
205: columnNames[idx] = null;
206: columnValue[idx] = null;
207: if (columnPrev[idx] == -1) {
208: nameCache.remove(name);
209: } else {
210: nameCache.put(name, IntegerCache
211: .getInteger(columnPrev[idx]));
212: }
213: // thats the easy case ..
214: length -= 1;
215: return;
216: }
217:
218: if (DEBUG) {
219: Log.warn("Out of order removeal of a column: " + name);
220: }
221:
222: if (columnPrev[idx] == -1) {
223: nameCache.remove(name);
224: } else {
225: nameCache.put(name, IntegerCache
226: .getInteger(columnPrev[idx]));
227: }
228:
229: final int moveStartIdx = idx + 1;
230: final int moveLength = length - moveStartIdx;
231: System.arraycopy(columnNames, moveStartIdx, columnNames, idx,
232: moveLength);
233: System.arraycopy(columnChanged, moveStartIdx, columnChanged,
234: idx, moveLength);
235: System.arraycopy(columnOldValue, moveStartIdx, columnOldValue,
236: idx, moveLength);
237: System.arraycopy(columnValue, moveStartIdx, columnValue, idx,
238: moveLength);
239: System.arraycopy(columnPrev, moveStartIdx, columnPrev, idx,
240: moveLength);
241: columnNames[length - 1] = null;
242: columnChanged[length - 1] = null;
243: columnOldValue[length - 1] = null;
244: columnPrev[length - 1] = 0;
245:
246: // Now it gets expensive: Rebuild the namecache ..
247: final int newLength = moveLength + idx;
248: nameCache.clear();
249: for (int i = 0; i < newLength; i++) {
250: final String columnName = columnNames[i];
251: final Integer oldVal = (Integer) nameCache.get(columnName);
252: nameCache.put(columnName, IntegerCache.getInteger(i));
253: if (oldVal != null) {
254: columnPrev[i] = oldVal.intValue();
255: } else {
256: columnPrev[i] = -1;
257: }
258: }
259: length -= 1;
260: if (DEBUG) {
261: Log.debug("New Namecache: " + nameCache);
262: }
263: }
264:
265: public void putField(final String name, final Object value,
266: final boolean update) {
267: if (DEBUG) {
268: if (update) {
269: Log.debug(" + : " + name);
270: } else {
271: Log.debug("Adding: " + name);
272: }
273:
274: }
275:
276: final Integer o = (Integer) nameCache.get(name);
277:
278: if (update == false) {
279: if (modifiableNameCache == false) {
280: this .columnNames = (String[]) columnNames.clone();
281: this .nameCache = (HashMap) nameCache.clone();
282: this .modifiableNameCache = true;
283: }
284:
285: // A new column ...
286: ensureCapacity(length + 1);
287: columnNames[length] = name;
288: columnValue[length] = value;
289: if (o == null) {
290: columnPrev[length] = -1;
291: } else {
292: columnPrev[length] = o.intValue();
293: }
294:
295: columnOldValue[length] = null;
296: columnChanged[length] = Boolean.TRUE;
297: nameCache.put(name, IntegerCache.getInteger(length));
298: length += 1;
299: } else {
300: // Updating an existing column ...
301: if (o == null) {
302: throw new IllegalStateException(
303: "Update to a non-existing column: " + name);
304: }
305:
306: final int idx = o.intValue();
307: columnNames[idx] = name;
308: columnOldValue[idx] = columnValue[idx];
309: columnValue[idx] = value;
310: if (ObjectUtilities.equal(columnOldValue[idx],
311: columnValue[idx])) {
312: columnChanged[idx] = Boolean.FALSE;
313: } else {
314: columnChanged[idx] = Boolean.TRUE;
315: }
316: }
317: }
318:
319: private void ensureCapacity(final int requestedSize) {
320: final int capacity = this .columnNames.length;
321: if (capacity > requestedSize) {
322: return;
323: }
324: final int newSize = Math.max(capacity << 1, requestedSize + 10);
325:
326: final String[] newColumnNames = new String[newSize];
327: System.arraycopy(columnNames, 0, newColumnNames, 0, length);
328: this .columnNames = newColumnNames;
329:
330: final Boolean[] newColumnChanged = new Boolean[newSize];
331: System.arraycopy(columnChanged, 0, newColumnChanged, 0, length);
332: this .columnChanged = newColumnChanged;
333:
334: final int[] newColumnPrev = new int[newSize];
335: System.arraycopy(columnPrev, 0, newColumnPrev, 0, length);
336: this .columnPrev = newColumnPrev;
337:
338: final Object[] newColumnValue = new Object[newSize];
339: System.arraycopy(columnValue, 0, newColumnValue, 0, length);
340: this .columnValue = newColumnValue;
341:
342: final Object[] newOldColumnValue = new Object[newSize];
343: System.arraycopy(columnOldValue, 0, newOldColumnValue, 0,
344: length);
345: this.columnOldValue = newOldColumnValue;
346: }
347: }
|