View Javadoc

1   /*
2    * @(#) SortingTableModel.java April 15, 2004 Copyright (c) 2002-2005-2004 Delft
3    * University of Technology Jaffalaan 5, 2628 BX Delft, the Netherlands. All
4    * rights reserved. This software is proprietary information of Delft University
5    * of Technology The code is published under the Lesser General Public License
6    */
7   
8   package nl.tudelft.simulation.introspection.sortable;
9   
10  import java.util.ArrayList;
11  import java.util.Arrays;
12  import java.util.Collections;
13  import java.util.HashMap;
14  import java.util.List;
15  import java.util.Map;
16  
17  import javax.swing.event.TableModelEvent;
18  import javax.swing.event.TableModelListener;
19  import javax.swing.table.DefaultTableModel;
20  import javax.swing.table.TableModel;
21  
22  import nl.tudelft.simulation.logger.Logger;
23  
24  /***
25   * The SortingTableModel.
26   * <p>
27   * (c) copyright 2002-2005-2004 <a href="http://www.simulation.tudelft.nl">Delft
28   * University of Technology </a>, the Netherlands. <br>
29   * See for project information <a
30   * href="http://www.simulation.tudelft.nl">www.simulation.tudelft.nl </a> <br>
31   * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser
32   * General Public License (LGPL) </a>, no warranty.
33   * 
34   * @author <a
35   *         href="http://web.eur.nl/fbk/dep/dep1/Introduction/Staff/People/Lang">Niels
36   *         Lang </a><a href="http://www.peter-jacobs.com/index.htm">Peter
37   *         Jacobs </a>
38   * @version 1.1 Apr 15, 2004
39   * @since 1.5
40   */
41  public class SortingTableModel implements TableModel, Sortable
42  {
43      /*** the listeners */
44      protected Map<ProxyListener, TableModelListener> proxyListeners = new HashMap<ProxyListener, TableModelListener>(
45              5);
46  
47      /*** the source */
48      protected TableModel source;
49  
50      /*** the index */
51      private List<Object> index = Collections
52              .synchronizedList(new ArrayList<Object>());
53  
54      /*** expandedIndex */
55      protected Integer[] expandedIndex;
56  
57      /*** the definitions */
58      private List<Definition> definitions = Collections
59              .synchronizedList(new ArrayList<Definition>());
60  
61      /***
62       * constructs a new SortingTableModel
63       * 
64       * @param source the sorce
65       */
66      public SortingTableModel(final TableModel source)
67      {
68          this.source = source;
69      }
70  
71      /***
72       * The ProxyListener
73       */
74      private class ProxyListener implements TableModelListener
75      {
76          /***
77           * @see javax.swing.event.TableModelListener#tableChanged(javax.swing.event.TableModelEvent)
78           */
79          public void tableChanged(final TableModelEvent e)
80          {
81              sort();
82              (SortingTableModel.this.proxyListeners.get(this)).tableChanged(e);
83          }
84      }
85  
86      /***
87       * builds the index
88       */
89      private synchronized void buildIndex()
90      {
91          initIndex();
92          for (Definition definition : this.definitions)
93          {
94              this.index = sortList(this.index, definition.isAcendingSort(),
95                      definition.getFieldID());
96          }
97          this.expandIndex();
98      }
99  
100     /***
101      * expands the index
102      */
103     private void expandIndex()
104     {
105         List<Object> expandedList = new ArrayList<Object>();
106         for (Object current : this.index)
107         {
108             if (current instanceof List)
109             {
110                 expandedList.addAll((List< ? >) current);
111             } else
112             {
113                 expandedList.add(current);
114             }
115         }
116         this.expandedIndex = expandedList.toArray(new Integer[0]);
117     }
118 
119     /***
120      * initializes the index
121      */
122     private void initIndex()
123     {
124         this.index.clear();
125         List<Integer> entry = new ArrayList<Integer>();
126         for (int i = 0; i < this.getRowCount(); i++)
127         {
128             entry.add(i);
129         }
130         this.index.add(entry);
131     }
132 
133     /***
134      * @param unsorted the list
135      * @param ascending is it ascending
136      * @param column the column
137      * @return the sortedList
138      */
139     private List<Object> sortList(final List<Object> unsorted,
140             final boolean ascending, final int column)
141     {
142         List<Object> result = new ArrayList<Object>(unsorted.size());
143         synchronized (unsorted)
144         {
145             for (Object current : unsorted)
146             {
147                 if (current instanceof Integer)
148                 {
149                     result.add(current);
150                 } else
151                 {
152                     List< ? > currentList = (List< ? >) current;
153                     result.addAll(sortSubList(currentList, ascending, column));
154                 }
155             }
156         }
157         return result;
158     }
159 
160     /***
161      * @param unsorted the list
162      * @param ascending is it ascending
163      * @param column the column
164      * @return the sortedList
165      */
166     @SuppressWarnings("unchecked")
167     private List<Object> sortSubList(final List< ? > unsorted,
168             final boolean ascending, final int column)
169     {
170         List<Object> result = new ArrayList<Object>(unsorted.size());
171         synchronized (unsorted)
172         {
173             for (int i = 0; i < unsorted.size(); i++)
174             {
175                 Integer unsortedEntry = (Integer) unsorted.get(i);
176                 Object current = this.source.getValueAt(unsortedEntry
177                         .intValue(), column);
178                 boolean allocated = false;
179                 for (int y = 0; (y < result.size() && !allocated); y++)
180                 {
181                     boolean inList = false;
182                     Object resultValue = result.get(y);
183                     if (resultValue instanceof List)
184                     {
185                         inList = true;
186                         resultValue = this.source.getValueAt(
187                                 ((Integer) ((List< ? >) resultValue).get(0))
188                                         .intValue(), column);
189                     } else
190                     {
191                         resultValue = this.source.getValueAt(
192                                 ((Integer) resultValue).intValue(), column);
193                     }
194                     if (current instanceof Comparable
195                             && resultValue instanceof Comparable)
196                     {
197                         try
198                         {
199                             int comparisson = ((Comparable) current)
200                                     .compareTo(resultValue);
201                             if (comparisson == 0)
202                             {
203                                 if (inList)
204                                 {
205                                     ((List<Object>) result.get(y))
206                                             .add(unsortedEntry);
207                                 } else
208                                 {
209                                     List valueList = new ArrayList(2);
210                                     valueList.add(result.get(y));
211                                     valueList.add(unsortedEntry);
212                                     result.remove(y);
213                                     result.add(y, valueList);
214                                 }
215                                 allocated = true;
216                             }
217                             if (ascending && comparisson < 0 || !ascending
218                                     && comparisson > 0)
219                             {
220                                 result.add(y, unsortedEntry);
221                                 allocated = true;
222                             }
223                         } catch (ClassCastException exception)
224                         {
225                             Logger.info(this, "sortSubList",
226                                     " Could not compare " + current + " and "
227                                             + resultValue + ": " + exception);
228                         }
229                     }
230                 }
231                 if (!allocated)
232                 {
233                     result.add(unsortedEntry);
234                 }
235             }
236         }
237         return result;
238     }
239 
240     /***
241      * @see nl.tudelft.simulation.introspection.sortable.Sortable#getDefinitions()
242      */
243     public Definition[] getDefinitions()
244     {
245         return this.definitions.toArray(new Definition[0]);
246     }
247 
248     /***
249      * @see nl.tudelft.simulation.introspection.sortable.Sortable
250      *      #setDefinitions(Definition[])
251      */
252     public void setDefinitions(final Definition[] definitions)
253     {
254         this.definitions.clear();
255         this.definitions.addAll(Arrays.asList(definitions));
256     }
257 
258     /***
259      * @see nl.tudelft.simulation.introspection.sortable.Sortable#sort()
260      */
261     public void sort()
262     {
263         buildIndex();
264     }
265 
266     /***
267      * @see javax.swing.table.TableModel#addTableModelListener(TableModelListener)
268      */
269     public void addTableModelListener(final TableModelListener l)
270     {
271         ProxyListener proxy = new ProxyListener();
272         this.proxyListeners.put(proxy, l);
273         this.source.addTableModelListener(proxy);
274     }
275 
276     /***
277      * @see javax.swing.table.TableModel#getColumnClass(int)
278      */
279     public Class< ? > getColumnClass(final int columnIndex)
280     {
281         return this.source.getColumnClass(columnIndex);
282     }
283 
284     /***
285      * @see javax.swing.table.TableModel#getColumnCount()
286      */
287     public int getColumnCount()
288     {
289         return this.source.getColumnCount();
290     }
291 
292     /***
293      * @see javax.swing.table.TableModel#getColumnName(int)
294      */
295     public String getColumnName(final int columnIndex)
296     {
297         return this.source.getColumnName(columnIndex);
298     }
299 
300     /***
301      * @see javax.swing.table.TableModel#getRowCount()
302      */
303     public int getRowCount()
304     {
305         return this.source.getRowCount();
306     }
307 
308     /***
309      * @see javax.swing.table.TableModel#getValueAt(int, int)
310      */
311     public Object getValueAt(final int rowIndex, final int columnIndex)
312     {
313         if (this.expandedIndex == null)
314         {
315             buildIndex();
316         }
317         if (rowIndex > this.expandedIndex.length)
318         {
319             Logger.warning(this, "getValueAt", " could not retrieve row "
320                     + rowIndex
321                     + " from sorted list. Returning default instead.");
322             return this.source.getValueAt(rowIndex, columnIndex);
323         }
324         return this.source.getValueAt(this.expandedIndex[rowIndex].intValue(),
325                 columnIndex);
326     }
327 
328     /***
329      * @see javax.swing.table.TableModel#isCellEditable(int, int)
330      */
331     public boolean isCellEditable(final int rowIndex, final int columnIndex)
332     {
333         return this.source.isCellEditable(this.expandedIndex[rowIndex]
334                 .intValue(), columnIndex);
335     }
336 
337     /***
338      * @see javax.swing.table.TableModel#removeTableModelListener(TableModelListener)
339      */
340     public void removeTableModelListener(final TableModelListener l)
341     {
342         ProxyListener proxy = (ProxyListener) this.proxyListeners.get(l);
343         this.source.removeTableModelListener(proxy);
344         this.proxyListeners.remove(proxy);
345     }
346 
347     /***
348      * @see javax.swing.table.TableModel#setValueAt(Object, int, int)
349      */
350     public void setValueAt(final Object aValue, final int rowIndex,
351             final int columnIndex)
352     {
353         if (rowIndex > this.expandedIndex.length)
354         {
355             Logger.warning(this, "setValueAt", " could not retrieve row "
356                     + rowIndex
357                     + " from sorted list. Ignoring 'setValue' command.");
358             return;
359         }
360         this.source.setValueAt(aValue, this.expandedIndex[rowIndex].intValue(),
361                 columnIndex);
362         this.buildIndex();
363         if (this.source instanceof DefaultTableModel)
364         {
365             ((DefaultTableModel) this.source).fireTableDataChanged();
366         }
367     }
368 }