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,v 1.10 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
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 }