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    }