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,v 1.17 2007/04/01 18:49:32 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.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, JFreeReportBoot.getInstance().getGlobalConfig().getConfigProperty
101 (COLUMN_NAME_MAPPING_KEY, "Label").equals("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 (prop.equalsIgnoreCase("simple"))
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 JFreeReportBoot.getInstance().getGlobalConfig().getConfigProperty
217 (COLUMN_NAME_MAPPING_KEY, "Label").equals("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 synchronized static ResultSetTableModelFactory getInstance ()
287 {
288 if (defaultInstance == null)
289 {
290 defaultInstance = new ResultSetTableModelFactory();
291 }
292 return defaultInstance;
293 }
294
295 }