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 }