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: ReportDataRow.java 3048 2007-07-28 18:02:42Z 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 java.util.Collections;
034    import java.util.HashMap;
035    import java.util.Map;
036    
037    import org.jfree.report.DataFlags;
038    import org.jfree.report.DataRow;
039    import org.jfree.report.DataSet;
040    import org.jfree.report.DataSourceException;
041    import org.jfree.report.ReportData;
042    import org.jfree.report.ReportDataFactory;
043    import org.jfree.report.ReportDataFactoryException;
044    import org.jfree.report.util.IntegerCache;
045    import org.jfree.util.ObjectUtilities;
046    
047    /**
048     * Creation-Date: 20.02.2006, 15:32:32
049     *
050     * @author Thomas Morgner
051     */
052    public final class ReportDataRow implements DataRow
053    {
054      private Map nameCache;
055      private DataFlags[] data;
056      private ReportData reportData;
057      private int cursor;
058    
059      private ReportDataRow(final ReportData reportData) throws DataSourceException
060      {
061        if (reportData == null)
062        {
063          throw new NullPointerException();
064        }
065    
066        synchronized (reportData)
067        {
068          this.reportData = reportData;
069    
070          reportData.setCursorPosition(ReportData.BEFORE_FIRST_ROW);
071          final boolean readable;
072          if (reportData.isAdvanceable())
073          {
074            readable = reportData.next() && reportData.isReadable();
075            cursor = reportData.getCursorPosition();
076          }
077          else
078          {
079            readable = false;
080            cursor = 0;
081          }
082    
083          final HashMap nameCache = new HashMap();
084          final int columnCount = reportData.getColumnCount();
085          this.data = new DataFlags[columnCount];
086    
087          for (int i = 0; i < columnCount; i++)
088          {
089            final String columnName = reportData.getColumnName(i);
090            if (columnName != null)
091            {
092              nameCache.put(columnName, IntegerCache.getInteger(i));
093            }
094    
095            if (readable)
096            {
097              final Object value = reportData.get(i);
098              this.data[i] = new DefaultDataFlags(columnName, value, true);
099            }
100            else
101            {
102              this.data[i] = new DefaultDataFlags(columnName, null, true);
103            }
104          }
105          this.nameCache = Collections.unmodifiableMap(nameCache);
106        }
107      }
108    
109      private ReportDataRow(final ReportData reportData,
110                            final ReportDataRow reportDataRow)
111          throws DataSourceException
112      {
113        if (reportData == null)
114        {
115          throw new NullPointerException();
116        }
117    
118        if (reportDataRow == null)
119        {
120          throw new NullPointerException();
121        }
122    
123        synchronized (reportData)
124        {
125          this.reportData = reportData;
126          this.cursor = reportData.getCursorPosition();
127          final int columnCount = reportData.getColumnCount();
128          this.data = new DataFlags[columnCount];
129    
130          for (int i = 0; i < columnCount; i++)
131          {
132            final String columnName = reportData.getColumnName(i);
133            final Object value = reportData.get(i);
134            final boolean changed = ObjectUtilities.equal(value, reportDataRow.get(i));
135            this.data[i] = new DefaultDataFlags(columnName, value, changed);
136          }
137          this.nameCache = reportDataRow.nameCache;
138        }
139      }
140    
141      public static ReportDataRow createDataRow(final ReportDataFactory dataFactory,
142                                                final String query,
143                                                final DataSet parameters)
144          throws DataSourceException, ReportDataFactoryException
145      {
146        final ReportData reportData = dataFactory.queryData(query, parameters);
147        return new ReportDataRow(reportData);
148      }
149    
150      /**
151       * Returns the value of the expression or column in the tablemodel using the given column number as index. For
152       * functions and expressions, the <code>getValue()</code> method is called and for columns from the tablemodel the
153       * tablemodel method <code>getValueAt(row, column)</code> gets called.
154       *
155       * @param col the item index.
156       * @return the value.
157       * @throws IllegalStateException if the datarow detected a deadlock.
158       */
159      public Object get(final int col) throws DataSourceException
160      {
161        return data[col].getValue();
162      }
163    
164      /**
165       * Returns the value of the function, expression or column using its specific name. The given name is translated into
166       * a valid column number and the the column is queried. For functions and expressions, the <code>getValue()</code>
167       * method is called and for columns from the tablemodel the tablemodel method <code>getValueAt(row, column)</code>
168       * gets 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      {
176        final Integer colIdx = (Integer) nameCache.get(col);
177        if (colIdx == null)
178        {
179          throw new DataSourceException
180              ("Invalid name specified. There is no such column.");
181        }
182    
183        return data[colIdx.intValue()].getValue();
184      }
185    
186      /**
187       * Returns the name of the column, expression or function. For columns from the tablemodel, the tablemodels
188       * <code>getColumnName</code> method is called. For functions, expressions and report properties the assigned name is
189       * returned.
190       *
191       * @param col the item index.
192       * @return the name.
193       */
194      public String getColumnName(final int col)
195      {
196        return data[col].getName();
197      }
198    
199      /**
200       * Returns the number of columns, expressions and functions and marked ReportProperties in the report.
201       *
202       * @return the item count.
203       */
204      public int getColumnCount()
205      {
206        return data.length;
207      }
208    
209      public DataFlags getFlags(final String col) throws DataSourceException
210      {
211        final Integer colIdx = (Integer) nameCache.get(col);
212        if (colIdx == null)
213        {
214          throw new DataSourceException
215              ("Invalid name specified. There is no such column.");
216        }
217    
218        return data[colIdx.intValue()];
219      }
220    
221      public DataFlags getFlags(final int col)
222      {
223        return data[col];
224      }
225    
226      /**
227       * Advances to the next row and attaches the given master row to the objects contained in that client data row.
228       *
229       * @param master
230       * @return
231       */
232      public ReportDataRow advance() throws DataSourceException
233      {
234        synchronized (reportData)
235        {
236          if (reportData.getCursorPosition() != cursor)
237          {
238            // directly go to the position we need.
239            if (reportData.setCursorPosition(cursor + 1) == false)
240            {
241              throw new DataSourceException("Unable to advance cursor position");
242            }
243          }
244          else
245          {
246            if (reportData.next() == false)
247            {
248              throw new DataSourceException("Unable to advance cursor position");
249            }
250          }
251          return new ReportDataRow(reportData, this);
252        }
253      }
254    
255      public boolean isAdvanceable() throws DataSourceException
256      {
257        synchronized (reportData)
258        {
259          if (reportData.getCursorPosition() != cursor)
260          {
261            // directly go to the position we need.
262            if (reportData.setCursorPosition(cursor) == false)
263            {
264              return false;
265            }
266          }
267          return reportData.isAdvanceable();
268        }
269      }
270    
271      public ReportData getReportData()
272      {
273        return reportData;
274      }
275    
276      public int getCursor()
277      {
278        return cursor;
279      }
280    }