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: ScrollableResultSetTableModel.java,v 1.11 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 javax.swing.table.AbstractTableModel;
037
038 import org.jfree.util.Log;
039
040 /**
041 * A tableModel which is backed up by a java.sql.ResultSet. Use this to directly feed your
042 * database data into JFreeReport. If you have trouble using this TableModel and you have
043 * either enough memory or your query result is not huge, you may want to use
044 * <code>ResultSetTableModelFactory.generateDefaultTableModel (ResultSet rs)</code>. That
045 * implementation will read all data from the given ResultSet and keep that data in
046 * memory.
047 * <p/>
048 * Use the close() function to close the ResultSet contained in this model.
049 *
050 * @author Thomas Morgner
051 */
052 public class ScrollableResultSetTableModel extends AbstractTableModel
053 implements CloseableTableModel
054 {
055 /**
056 * The scrollable ResultSet source.
057 */
058 private ResultSet resultset;
059 /**
060 * The ResultSetMetaData object for this result set.
061 */
062 private ResultSetMetaData dbmd;
063 /**
064 * The number of rows in the result set.
065 */
066 private int rowCount;
067 /**
068 * Defines the column naming mode.
069 */
070 private final boolean labelMapMode;
071 /**
072 * The column types as read from the result set.
073 */
074 private Class[] types;
075
076 /**
077 * Constructs the model.
078 *
079 * @param resultset the result set.
080 * @param labelMapMode defines, whether to use column names or column labels to compute
081 * the column index.
082 * @throws SQLException if there is a problem with the result set.
083 */
084 public ScrollableResultSetTableModel (final ResultSet resultset,
085 final boolean labelMapMode)
086 throws SQLException
087 {
088 this.labelMapMode = labelMapMode;
089 if (resultset != null)
090 {
091 updateResultSet(resultset);
092 }
093 else
094 {
095 close();
096 }
097 }
098
099 /**
100 * Creates a new scrollable result set with no resultset assigned and the specified
101 * label map mode.
102 *
103 * @param labelMapMode defines, whether to use column names or column labels to compute
104 * the column index.
105 */
106 protected ScrollableResultSetTableModel (final boolean labelMapMode)
107 {
108 this.labelMapMode = labelMapMode;
109 }
110
111 /**
112 * Returns the column name mode used to map column names into column indices. If true,
113 * then the Label is used, else the Name is used.
114 *
115 * @return true, if the column label is used for the mapping, false otherwise.
116 *
117 * @see ResultSetMetaData#getColumnLabel
118 * @see ResultSetMetaData#getColumnName
119 */
120 public boolean isLabelMapMode ()
121 {
122 return labelMapMode;
123 }
124
125 /**
126 * Updates the result set in this model with the given ResultSet object.
127 *
128 * @param resultset the new result set.
129 * @throws SQLException if there is a problem with the result set.
130 */
131 public void updateResultSet (final ResultSet resultset)
132 throws SQLException
133 {
134 if (this.resultset != null)
135 {
136 close();
137 }
138
139 this.resultset = resultset;
140 this.dbmd = resultset.getMetaData();
141
142 if (resultset.last())
143 {
144 rowCount = resultset.getRow();
145 }
146 else
147 {
148 rowCount = 0;
149 }
150
151 fireTableStructureChanged();
152 }
153
154 /**
155 * Clears the model of the current result set. The resultset is closed.
156 */
157 public void close ()
158 {
159 // Close the old result set if needed.
160 if (resultset != null)
161 {
162 try
163 {
164 resultset.close();
165 }
166 catch (SQLException e)
167 {
168 // Just in case the JDBC driver can't close a result set twice.
169 // e.printStackTrace();
170 }
171 }
172 resultset = null;
173 dbmd = null;
174 rowCount = 0;
175 fireTableStructureChanged();
176 }
177
178 /**
179 * Get a rowCount. This can be a very expensive operation on large datasets. Returns -1
180 * if the total amount of rows is not known to the result set.
181 *
182 * @return the row count.
183 */
184 public int getRowCount ()
185 {
186 if (resultset == null)
187 {
188 return 0;
189 }
190
191 try
192 {
193 if (resultset.last())
194 {
195 rowCount = resultset.getRow();
196 if (rowCount == -1)
197 {
198 rowCount = 0;
199 }
200 }
201 else
202 {
203 rowCount = 0;
204 }
205 }
206 catch (SQLException sqle)
207 {
208 //Log.debug ("GetRowCount failed, returning 0 rows", sqle);
209 return 0;
210 }
211 return rowCount;
212 }
213
214 /**
215 * Returns the number of columns in the ResultSet. Returns 0 if no result set is set or
216 * the column count could not be retrieved.
217 *
218 * @return the column count.
219 *
220 * @see java.sql.ResultSetMetaData#getColumnCount()
221 */
222 public int getColumnCount ()
223 {
224 if (resultset == null)
225 {
226 return 0;
227 }
228
229 if (dbmd != null)
230 {
231 try
232 {
233 return dbmd.getColumnCount();
234 }
235 catch (SQLException e)
236 {
237 //Log.debug ("GetColumnCount failed", e);
238 }
239 }
240 return 0;
241 }
242
243 /**
244 * Returns the columnLabel or column name for the given column. Whether the label or the
245 * name is returned depends on the label map mode.
246 *
247 * @param column the column index.
248 * @return the column name.
249 *
250 * @see java.sql.ResultSetMetaData#getColumnLabel(int)
251 */
252 public String getColumnName (final int column)
253 {
254 if (dbmd != null)
255 {
256 try
257 {
258 if (isLabelMapMode())
259 {
260 return dbmd.getColumnLabel(column + 1);
261 }
262 else
263 {
264 return dbmd.getColumnName(column + 1);
265 }
266 }
267 catch (SQLException e)
268 {
269 Log.info("ScrollableResultSetTableModel.getColumnName: SQLException.");
270 }
271 }
272 return null;
273 }
274
275 /**
276 * Returns the value of the specified row and the specified column from within the
277 * resultset.
278 *
279 * @param row the row index.
280 * @param column the column index.
281 * @return the value.
282 */
283 public Object getValueAt (final int row, final int column)
284 {
285 if (resultset != null)
286 {
287 try
288 {
289 resultset.absolute(row + 1);
290 return resultset.getObject(column + 1);
291 }
292 catch (SQLException e)
293 {
294 //Log.debug ("Query failed for [" + row + "," + column + "]", e);
295 }
296 }
297 return null;
298 }
299
300 /**
301 * Returns the class of the resultset column. Returns Object.class if an error
302 * occurred.
303 *
304 * @param column the column index.
305 * @return the column class.
306 */
307 public Class getColumnClass (final int column)
308 {
309 if (types != null)
310 {
311 return types[column];
312 }
313 if (dbmd != null)
314 {
315 try
316 {
317 types = TypeMapper.mapTypes(dbmd);
318 return types[column];
319 }
320 catch (Exception e)
321 {
322 //Log.debug ("GetColumnClass failed for " + column, e);
323 }
324 }
325 return Object.class;
326 }
327
328
329 /**
330 * Returns the classname of the resultset column. Returns Object.class if an error
331 * occurred.
332 *
333 * @param column the column index.
334 * @return the column class name.
335 */
336 public String getColumnClassName (final int column)
337 {
338 if (dbmd != null)
339 {
340 return mckoiDBFixClassName(getColumnClass(column).getName());
341 }
342 return Object.class.getName();
343 }
344
345 /**
346 * Just removes the word class from the start of the classname string McKoiDB version
347 * 0.92 was not able to properly return classnames of resultset elements.
348 *
349 * @param classname the class name.
350 * @return the modified class name.
351 */
352 private String mckoiDBFixClassName (final String classname)
353 {
354 if (classname.startsWith("class "))
355 {
356 return classname.substring(6).trim();
357 }
358 return classname;
359 }
360 }