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    }