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    }