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: SubSetTableModel.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 032 package org.jfree.report.modules.misc.tablemodel; 033 034 import java.util.ArrayList; 035 import javax.swing.event.TableModelEvent; 036 import javax.swing.event.TableModelListener; 037 import javax.swing.table.TableModel; 038 039 /** 040 * A TableModel that proxies an other tablemodel and cuts rows from the start and/or the 041 * end of the other tablemodel. 042 * 043 * @author Thomas Morgner 044 */ 045 public class SubSetTableModel implements TableModel 046 { 047 /** 048 * A helper class, that translates tableevents received from the wrapped table model and 049 * forwards them with changed indices to the registered listeners. 050 */ 051 private final class TableEventTranslator implements TableModelListener 052 { 053 /** 054 * the registered listeners. 055 */ 056 private final ArrayList listeners; 057 058 /** 059 * Default Constructor. 060 */ 061 private TableEventTranslator () 062 { 063 listeners = new ArrayList(); 064 } 065 066 /** 067 * This fine grain notification tells listeners the exact range of cells, rows, or 068 * columns that changed. The received rows are translated to fit the external 069 * tablemodel size. 070 * 071 * @param e the event, that should be translated. 072 */ 073 public void tableChanged (final TableModelEvent e) 074 { 075 int firstRow = e.getFirstRow(); 076 if (e.getFirstRow() > 0) 077 { 078 firstRow -= getStart(); 079 } 080 081 int lastRow = e.getLastRow(); 082 if (lastRow > 0) 083 { 084 lastRow -= getStart(); 085 lastRow -= (getEnclosedModel().getRowCount() - getEnd()); 086 } 087 final int type = e.getType(); 088 final int column = e.getColumn(); 089 090 final TableModelEvent event = 091 new TableModelEvent(SubSetTableModel.this, firstRow, lastRow, column, type); 092 093 for (int i = 0; i < listeners.size(); i++) 094 { 095 final TableModelListener l = (TableModelListener) listeners.get(i); 096 l.tableChanged(event); 097 } 098 } 099 100 /** 101 * Adds the TableModelListener to this Translator. 102 * 103 * @param l the tablemodel listener 104 */ 105 protected void addTableModelListener (final TableModelListener l) 106 { 107 listeners.add(l); 108 } 109 110 /** 111 * Removes the TableModelListener from this Translator. 112 * 113 * @param l the tablemodel listener 114 */ 115 protected void removeTableModelListener (final TableModelListener l) 116 { 117 listeners.remove(l); 118 } 119 } 120 121 /** 122 * the row that should be the first row. 123 */ 124 private int start; 125 126 /** 127 * the row that should be the last row. 128 */ 129 private int end; 130 131 /** 132 * the model. 133 */ 134 private TableModel model; 135 136 /** 137 * the event translator. 138 */ 139 private TableEventTranslator eventHandler; 140 141 /** 142 * Creates a new SubSetTableModel, the start and the end parameters define the new 143 * tablemodel row count. The parameter <code>start</code> must be a positive integer and 144 * denotes the number or rows removed from the start of the tablemodel. <code>end</code> 145 * is the number of the last translated row. Any row after <code>end</code> is ignored. 146 * End must be greater or equal the given start row. 147 * 148 * @param start the number of rows that should be removed. 149 * @param end the last row. 150 * @param model the wrapped model 151 * @throws NullPointerException if the given model is null 152 * @throws IllegalArgumentException if start or end are invalid. 153 */ 154 public SubSetTableModel (final int start, final int end, final TableModel model) 155 { 156 if (start < 0) 157 { 158 throw new IllegalArgumentException("Start < 0"); 159 } 160 if (end <= start) 161 { 162 throw new IllegalArgumentException("end < start"); 163 } 164 if (model == null) 165 { 166 throw new NullPointerException(); 167 } 168 if (end >= model.getRowCount()) 169 { 170 throw new IllegalArgumentException("End >= Model.RowCount"); 171 } 172 173 this.start = start; 174 this.end = end; 175 this.model = model; 176 this.eventHandler = new TableEventTranslator(); 177 } 178 179 /** 180 * Translates the given row to fit for the wrapped tablemodel. 181 * 182 * @param rowIndex the original row index. 183 * @return the translated row index. 184 */ 185 private int getClientRowIndex (final int rowIndex) 186 { 187 return rowIndex + start; 188 } 189 190 /** 191 * Returns the number of rows in the model. A <code>JTable</code> uses this method to 192 * determine how many rows it should display. This method should be quick, as it is 193 * called frequently during rendering. 194 * 195 * @return the number of rows in the model 196 * 197 * @see #getColumnCount 198 */ 199 public int getRowCount () 200 { 201 final int rowCount = model.getRowCount(); 202 return rowCount - start - (rowCount - end); 203 } 204 205 /** 206 * Returns the number of columns in the model. A <code>JTable</code> uses this method to 207 * determine how many columns it should create and display by default. 208 * 209 * @return the number of columns in the model 210 * 211 * @see #getRowCount 212 */ 213 public int getColumnCount () 214 { 215 return model.getColumnCount(); 216 } 217 218 /** 219 * Returns the name of the column at <code>columnIndex</code>. This is used to 220 * initialize the table's column header name. Note: this name does not need to be 221 * unique; two columns in a table can have the same name. 222 * 223 * @param columnIndex the index of the column 224 * @return the name of the column 225 */ 226 public String getColumnName (final int columnIndex) 227 { 228 return model.getColumnName(columnIndex); 229 } 230 231 /** 232 * Returns the most specific superclass for all the cell values in the column. This is 233 * used by the <code>JTable</code> to set up a default renderer and editor for the 234 * column. 235 * 236 * @param columnIndex the index of the column 237 * @return the base ancestor class of the object values in the model. 238 */ 239 public Class getColumnClass (final int columnIndex) 240 { 241 return model.getColumnClass(columnIndex); 242 } 243 244 /** 245 * Returns true if the cell at <code>rowIndex</code> and <code>columnIndex</code> is 246 * editable. Otherwise, <code>setValueAt</code> on the cell will not change the value 247 * of that cell. 248 * 249 * @param rowIndex the row whose value to be queried 250 * @param columnIndex the column whose value to be queried 251 * @return true if the cell is editable 252 * 253 * @see #setValueAt 254 */ 255 public boolean isCellEditable (final int rowIndex, final int columnIndex) 256 { 257 return model.isCellEditable(getClientRowIndex(rowIndex), columnIndex); 258 } 259 260 /** 261 * Returns the value for the cell at <code>columnIndex</code> and 262 * <code>rowIndex</code>. 263 * 264 * @param rowIndex the row whose value is to be queried 265 * @param columnIndex the column whose value is to be queried 266 * @return the value Object at the specified cell 267 */ 268 public Object getValueAt (final int rowIndex, final int columnIndex) 269 { 270 return model.getValueAt(getClientRowIndex(rowIndex), columnIndex); 271 } 272 273 /** 274 * Sets the value in the cell at <code>columnIndex</code> and <code>rowIndex</code> to 275 * <code>aValue</code>. 276 * 277 * @param aValue the new value 278 * @param rowIndex the row whose value is to be changed 279 * @param columnIndex the column whose value is to be changed 280 * @see #getValueAt 281 * @see #isCellEditable 282 */ 283 public void setValueAt (final Object aValue, final int rowIndex, final int columnIndex) 284 { 285 model.setValueAt(aValue, getClientRowIndex(rowIndex), columnIndex); 286 } 287 288 /** 289 * Adds a listener to the list that is notified each time a change to the data model 290 * occurs. 291 * 292 * @param l the TableModelListener 293 */ 294 public void addTableModelListener (final TableModelListener l) 295 { 296 eventHandler.addTableModelListener(l); 297 } 298 299 /** 300 * Removes a listener from the list that is notified each time a change to the data 301 * model occurs. 302 * 303 * @param l the TableModelListener 304 */ 305 public void removeTableModelListener (final TableModelListener l) 306 { 307 eventHandler.removeTableModelListener(l); 308 } 309 310 /** 311 * Returns the enclosed tablemodel, which is wrapped by this subset table model. 312 * 313 * @return the enclosed table model, never null. 314 */ 315 protected TableModel getEnclosedModel () 316 { 317 return model; 318 } 319 320 /** 321 * Returns the start row that should be mapped to row 0 of this model. 322 * 323 * @return the first row that should be visible. 324 */ 325 protected int getStart () 326 { 327 return start; 328 } 329 330 /** 331 * Returns the last row that should be visible. 332 * 333 * @return the number of the last row. 334 */ 335 protected int getEnd () 336 { 337 return end; 338 } 339 }