1
2
3
4
5
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 }