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 2725 2007-04-01 18:49:29Z taqua $ 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 }