View Javadoc

1   /*
2    * @(#) SortingTableModel.java April 15, 2004
3    * 
4    * Copyright (c) 2003-2004 Delft University of Technology Jaffalaan 5, 2628 BX
5    * Delft, the Netherlands. All rights reserved.
6    * 
7    * This software is proprietary information of Delft University of Technology
8    * The code is published under the General Public License
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 }