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: ResultSetTableModelFactory.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.modules.misc.tablemodel;
032    
033    import java.sql.ResultSet;
034    import java.sql.ResultSetMetaData;
035    import java.sql.SQLException;
036    import java.util.ArrayList;
037    import javax.swing.table.DefaultTableModel;
038    
039    import org.jfree.report.JFreeReportBoot;
040    import org.jfree.util.Log;
041    
042    /**
043     * Creates a <code>TableModel</code> which is backed up by a <code>ResultSet</code>. If
044     * the <code>ResultSet</code> is scrollable, a {@link org.jfree.report.modules.misc.tablemodel.ScrollableResultSetTableModel}
045     * is created, otherwise all data is copied from the <code>ResultSet</code> into a
046     * <code>DefaultTableModel</code>.
047     * <p/>
048     * The creation of a <code>DefaultTableModel</code> can be forced if the system property
049     * <code>"org.jfree.report.modules.misc.tablemodel.TableFactoryMode"</code> is set to
050     * <code>"simple"</code>.
051     *
052     * @author Thomas Morgner
053     */
054    public final class ResultSetTableModelFactory
055    {
056      /**
057       * The configuration key defining how to map column names to column indices.
058       */
059      public static final String COLUMN_NAME_MAPPING_KEY =
060              "org.jfree.report.modules.misc.tablemodel.ColumnNameMapping";
061    
062      /**
063       * The 'ResultSet factory mode'.
064       */
065      public static final String RESULTSET_FACTORY_MODE
066              = "org.jfree.report.modules.misc.tablemodel.TableFactoryMode";
067    
068      /**
069       * Singleton instance of the factory.
070       */
071      private static ResultSetTableModelFactory defaultInstance;
072    
073      /**
074       * Default constructor. This is a Singleton, use getInstance().
075       */
076      private ResultSetTableModelFactory ()
077      {
078      }
079    
080      /**
081       * Creates a table model by using the given <code>ResultSet</code> as the backend. If
082       * the <code>ResultSet</code> is scrollable (the type is not
083       * <code>TYPE_FORWARD_ONLY</code>), an instance of {@link org.jfree.report.modules.misc.tablemodel.ScrollableResultSetTableModel}
084       * is returned. This model uses the extended capabilities of scrollable resultsets to
085       * directly read data from the database without caching or the need of copying the
086       * complete <code>ResultSet</code> into the programs memory.
087       * <p/>
088       * If the <code>ResultSet</code> lacks the scollable features, the data will be copied
089       * into a <code>DefaultTableModel</code> and the <code>ResultSet</code> gets closed.
090       *
091       * @param rs the result set.
092       * @return a closeable table model.
093       *
094       * @throws SQLException if there is a problem with the result set.
095       */
096      public CloseableTableModel createTableModel (final ResultSet rs)
097              throws SQLException
098      {
099        return createTableModel
100                (rs, "Label".equals(JFreeReportBoot.getInstance().getGlobalConfig().getConfigProperty
101                    (COLUMN_NAME_MAPPING_KEY, "Label")));
102      }
103    
104      /**
105       * Creates a table model by using the given <code>ResultSet</code> as the backend. If
106       * the <code>ResultSet</code> is scrollable (the type is not
107       * <code>TYPE_FORWARD_ONLY</code>), an instance of {@link org.jfree.report.modules.misc.tablemodel.ScrollableResultSetTableModel}
108       * is returned. This model uses the extended capabilities of scrollable resultsets to
109       * directly read data from the database without caching or the need of copying the
110       * complete <code>ResultSet</code> into the programs memory.
111       * <p/>
112       * If the <code>ResultSet</code> lacks the scollable features, the data will be copied
113       * into a <code>DefaultTableModel</code> and the <code>ResultSet</code> gets closed.
114       *
115       * @param rs           the result set.
116       * @param labelMapping defines, whether to use column names or column labels to compute
117       *                     the column index.
118       * @return a closeable table model.
119       *
120       * @throws SQLException if there is a problem with the result set.
121       */
122      public CloseableTableModel createTableModel (final ResultSet rs,
123                                                   final boolean labelMapping)
124              throws SQLException
125      {
126        // Allow for override, some jdbc drivers are buggy :(
127        final String prop =
128                JFreeReportBoot.getInstance().getGlobalConfig().getConfigProperty
129                        (RESULTSET_FACTORY_MODE, "");
130    
131        if ("simple".equalsIgnoreCase(prop))
132        {
133          return generateDefaultTableModel(rs, labelMapping);
134        }
135    
136        int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
137        try
138        {
139          resultSetType = rs.getType();
140        }
141        catch (SQLException sqle)
142        {
143          Log.info
144                  ("ResultSet type could not be determined, assuming default table model.");
145        }
146        if (resultSetType == ResultSet.TYPE_FORWARD_ONLY)
147        {
148          return generateDefaultTableModel(rs, labelMapping);
149        }
150        else
151        {
152          return new ScrollableResultSetTableModel(rs, labelMapping);
153        }
154      }
155    
156      /**
157       * A DefaultTableModel that implements the CloseableTableModel interface.
158       */
159      private static final class CloseableDefaultTableModel extends DefaultTableModel
160              implements CloseableTableModel
161      {
162        /**
163         * The results set.
164         */
165        private final ResultSet res;
166    
167        /**
168         * Creates a new closeable table model.
169         *
170         * @param objects  the table data.
171         * @param objects1 the column names.
172         * @param res      the result set.
173         */
174        private CloseableDefaultTableModel (final Object[][] objects,
175                                            final Object[] objects1, final ResultSet res)
176        {
177          super(objects, objects1);
178          this.res = res;
179        }
180    
181        /**
182         * If this model has a resultset assigned, close it, if this is a DefaultTableModel,
183         * remove all data.
184         */
185        public void close ()
186        {
187          setDataVector(new Object[0][0], new Object[0]);
188          try
189          {
190            res.close();
191          }
192          catch (Exception e)
193          {
194            //Log.debug ("Close failed for resultset table model", e);
195          }
196        }
197      }
198    
199      /**
200       * Generates a <code>TableModel</code> that gets its contents filled from a
201       * <code>ResultSet</code>. The column names of the <code>ResultSet</code> will form the
202       * column names of the table model.
203       * <p/>
204       * Hint: To customize the names of the columns, use the SQL column aliasing (done with
205       * <code>SELECT nativecolumnname AS "JavaColumnName" FROM ....</code>
206       *
207       * @param rs the result set.
208       * @return a closeable table model.
209       *
210       * @throws SQLException if there is a problem with the result set.
211       */
212      public CloseableTableModel generateDefaultTableModel (final ResultSet rs)
213              throws SQLException
214      {
215        return generateDefaultTableModel(rs,
216            "Label".equals(JFreeReportBoot.getInstance().getGlobalConfig().getConfigProperty
217                (COLUMN_NAME_MAPPING_KEY, "Label")));
218      }
219    
220      /**
221       * Generates a <code>TableModel</code> that gets its contents filled from a
222       * <code>ResultSet</code>. The column names of the <code>ResultSet</code> will form the
223       * column names of the table model.
224       * <p/>
225       * Hint: To customize the names of the columns, use the SQL column aliasing (done with
226       * <code>SELECT nativecolumnname AS "JavaColumnName" FROM ....</code>
227       *
228       * @param rs           the result set.
229       * @param labelMapping defines, whether to use column names or column labels to compute
230       *                     the column index.
231       * @return a closeable table model.
232       *
233       * @throws SQLException if there is a problem with the result set.
234       */
235      public CloseableTableModel generateDefaultTableModel
236              (final ResultSet rs, final boolean labelMapping)
237              throws SQLException
238      {
239        final ResultSetMetaData rsmd = rs.getMetaData();
240        final int colcount = rsmd.getColumnCount();
241        final ArrayList header = new ArrayList(colcount);
242        for (int i = 0; i < colcount; i++)
243        {
244          if (labelMapping)
245          {
246            final String name = rsmd.getColumnLabel(i + 1);
247            header.add(name);
248          }
249          else
250          {
251            final String name = rsmd.getColumnName(i + 1);
252            header.add(name);
253          }
254        }
255        final ArrayList rows = new ArrayList();
256        while (rs.next())
257        {
258          final Object[] column = new Object[colcount];
259          for (int i = 0; i < colcount; i++)
260          {
261            final Object val = rs.getObject(i + 1);
262            column[i] = val;
263          }
264          rows.add(column);
265        }
266    
267        final Object[] tempRows = rows.toArray();
268        final Object[][] rowMap = new Object[tempRows.length][];
269        for (int i = 0; i < tempRows.length; i++)
270        {
271          rowMap[i] = (Object[]) tempRows[i];
272        }
273        final CloseableDefaultTableModel model =
274                new CloseableDefaultTableModel(rowMap, header.toArray(), rs);
275        for (int i = 0; i < colcount; i++)
276        {
277        }
278        return model;
279      }
280    
281      /**
282       * Returns the singleton instance of the factory.
283       *
284       * @return an instance of this factory.
285       */
286      public static synchronized ResultSetTableModelFactory getInstance ()
287      {
288        if (defaultInstance == null)
289        {
290          defaultInstance = new ResultSetTableModelFactory();
291        }
292        return defaultInstance;
293      }
294    
295    }