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