View Javadoc

1   /*
2    * @(#) CollectionTableModel.java Apr 15, 2004
3    * 
4    * Copyright (c) 2003 Delft University of Technology Jaffalaan 5, 2628 BX Delft,
5    * 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.gui;
12  
13  import java.lang.reflect.Constructor;
14  import java.util.ArrayList;
15  import java.util.Arrays;
16  import java.util.Collection;
17  import java.util.Collections;
18  import java.util.HashMap;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Map;
22  
23  import javax.swing.table.AbstractTableModel;
24  
25  import nl.tudelft.simulation.introspection.AbstractProperty;
26  import nl.tudelft.simulation.introspection.Introspector;
27  import nl.tudelft.simulation.introspection.Property;
28  import nl.tudelft.simulation.introspection.beans.BeanIntrospector;
29  import nl.tudelft.simulation.introspection.table.DynamicTableModel;
30  import nl.tudelft.simulation.logger.Logger;
31  
32  /***
33   * A tablemodel used to manage and present the instances of a composite
34   * property.
35   * <p>
36   * (c) copyright 2003-2004 <a href="http://www.simulation.tudelft.nl">Delft
37   * University of Technology </a>, the Netherlands. <br>
38   * See for project information <a
39   * href="http://www.simulation.tudelft.nl">www.simulation.tudelft.nl </a> <br>
40   * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
41   * License (GPL) </a>, no warranty <br>
42   * 
43   * @author <a
44   *         href="http://web.eur.nl/fbk/dep/dep1/Introduction/Staff/People/Lang">Niels
45   *         Lang </a><a
46   *         href="http://www.tbm.tudelft.nl/webstaf/peterja/index.htm">Peter
47   *         Jacobs </a>
48   * @version 1.1 Apr 15, 2004
49   * @since 1.4
50   */
51  public class CollectionTableModel extends AbstractTableModel implements
52  		IntrospectingTableModelInterface, DynamicTableModel
53  {
54  	/*** the instances of the collection */
55  	protected Map instances = Collections.synchronizedMap(new HashMap(20));
56  
57  	/*** the keys identifying specific instances */
58  	protected List keys = Collections.synchronizedList(new ArrayList(20));
59  
60  	/*** the componentType */
61  	private Class componentType = null;
62  
63  	/*** the COLUMNS of this tabbleModel */
64  	private static final String[] COLUMNS = {"#", "+", "Instance"};
65  
66  	/*** the expand button */
67  	private List buttons = Collections.synchronizedList(new ArrayList(20));
68  
69  	/*** the parentProperty */
70  	private Property parentProperty;
71  
72  	/*** the introspector */
73  	private Introspector introspector;
74  
75  	/*** The model manager */
76  	private ModelManager manager = new DefaultModelManager();
77  
78  	/*** The highest key currently allocated */
79  	private int maxKey = 0;
80  
81  	/***
82  	 * constructs a new CollectionTableModel
83  	 * 
84  	 * @param parentProperty the parentPropert
85  	 */
86  	public CollectionTableModel(final Property parentProperty)
87  	{
88  		this(parentProperty, new BeanIntrospector());
89  	}
90  
91  	/***
92  	 * constructs a new CollectionTableModel
93  	 * 
94  	 * @param parentProperty the parentProperty
95  	 * @param introspector the introspector to use
96  	 */
97  	public CollectionTableModel(final Property parentProperty,
98  			final Introspector introspector)
99  	{
100 		Object values = parentProperty.getValue();
101 		if (values.getClass().isArray())
102 		{
103 			for (int i = 0; i < ((Object[]) values).length; i++)
104 			{
105 				addValue(((Object[]) values)[i]);
106 			}
107 		}
108 		if (values instanceof Collection)
109 		{
110 			for (Iterator i = ((Collection) values).iterator(); i.hasNext();)
111 			{
112 				addValue(i.next());
113 			}
114 		}
115 		this.parentProperty = parentProperty;
116 		this.introspector = introspector;
117 		// Initialize buttons
118 		for (int i = 0; i < this.instances.size(); i++)
119 		{
120 			this.buttons.add(new ExpandButton(getProperty(i), this));
121 		}
122 	}
123 
124 	/***
125 	 * Adds a new value to the managed composite property.
126 	 * 
127 	 * @param value the value to add
128 	 */
129 	private void addValue(final Object value)
130 	{
131 		Integer nextKey = new Integer(this.maxKey++);
132 		this.keys.add(nextKey);
133 		this.instances.put(nextKey, value);
134 	}
135 
136 	/***
137 	 * @see javax.swing.table.TableModel#getRowCount()
138 	 */
139 	public int getRowCount()
140 	{
141 		return this.instances.size();
142 	}
143 
144 	/***
145 	 * @see javax.swing.table.TableModel#getColumnCount()
146 	 */
147 	public int getColumnCount()
148 	{
149 		return CollectionTableModel.COLUMNS.length;
150 	}
151 
152 	/***
153 	 * @see javax.swing.table.TableModel#getValueAt(int, int)
154 	 */
155 	public Object getValueAt(final int rowIndex, final int columnIndex)
156 	{
157 		if (columnIndex == 0)
158 		{
159 			return new Integer(rowIndex);
160 		}
161 		if (columnIndex == 1)
162 		{
163 			return this.buttons.get(rowIndex);
164 		}
165 		if (columnIndex == 2)
166 		{
167 			return this.instances.get(this.keys.get(rowIndex));
168 		}
169 		return null;
170 	}
171 
172 	/***
173 	 * @see javax.swing.table.TableModel#getColumnName(int)
174 	 */
175 	public String getColumnName(final int columnIndex)
176 	{
177 		return CollectionTableModel.COLUMNS[columnIndex];
178 	}
179 
180 	/***
181 	 * @see javax.swing.table.TableModel#isCellEditable(int, int)
182 	 */
183 	public boolean isCellEditable(final int rowIndex, final int columnIndex)
184 	{
185 		if (columnIndex == 1 || columnIndex == 2)
186 		{
187 			return true;
188 		}
189 		return false;
190 	}
191 
192 	/***
193 	 * @see javax.swing.table.TableModel#setValueAt(Object, int, int)
194 	 */
195 	public void setValueAt(final Object aValue, final int rowIndex,
196 			final int columnIndex)
197 	{
198 		if (columnIndex == 2)
199 		{
200 			Integer key = (Integer) this.keys.get(rowIndex);
201 			this.instances.put(key, aValue);
202 		}
203 		this.update();
204 	}
205 
206 	/***
207 	 * updates the tableModel
208 	 */
209 	private void update()
210 	{
211 		// Generate a List reflecting changes
212 		List newValue = new ArrayList(this.keys.size());
213 		for (int i = 0; i < this.keys.size(); i++)
214 		{
215 			newValue.add(this.instances.get(this.keys.get(i)));
216 		}
217 		this.parentProperty.setValue(newValue);
218 		this.fireTableDataChanged();
219 	}
220 
221 	/***
222 	 * @see javax.swing.table.TableModel#getColumnClass(int)
223 	 */
224 	public Class getColumnClass(final int columnIndex)
225 	{
226 		if (columnIndex == 1)
227 		{
228 			return ExpandButton.class;
229 		}
230 		return Object.class;
231 	}
232 
233 	/***
234 	 * The collection table model labels all properties according to their
235 	 * rowIndex. Only these labels are expected to be requested here.
236 	 * 
237 	 * @see nl.tudelft.simulation.introspection.gui.IntrospectingTableModelInterface
238 	 *      #getProperty(java.lang.String)
239 	 */
240 	public Property getProperty(final String propertyName)
241 	{
242 		int index = Integer.parseInt(propertyName);
243 		return getProperty(index);
244 	}
245 
246 	/***
247 	 * @param index the index of the property
248 	 * @return the Property
249 	 */
250 	protected Property getProperty(final int index)
251 	{
252 		return new CollectionProperty((Integer) this.keys.get(index),
253 				this.parentProperty.getName());
254 	}
255 
256 	/***
257 	 * The CollectionProperty
258 	 */
259 	class CollectionProperty extends AbstractProperty implements Property
260 	{
261 		/*** the key of this property */
262 		private final Integer key;
263 
264 		/*** the name */
265 		private final String name;
266 
267 		/***
268 		 * This implementation is NOT thread-safe. When multiple users will edit
269 		 * the parent at the same time, errors are expected.
270 		 * 
271 		 * @param key the key
272 		 * @param name the name
273 		 */
274 		public CollectionProperty(Integer key, String name)
275 		{
276 			this.key = key;
277 			this.name = name;
278 		}
279 
280 		/***
281 		 * @see nl.tudelft.simulation.introspection.Property#getInstance()
282 		 */
283 		public Object getInstance()
284 		{
285 			return CollectionTableModel.this.instances.values();
286 		}
287 
288 		/***
289 		 * @see nl.tudelft.simulation.introspection.Property#getName()
290 		 */
291 		public String getName()
292 		{
293 			return this.name + "["
294 					+ CollectionTableModel.this.keys.indexOf(this.key) + "]";
295 		}
296 
297 		/***
298 		 * @see nl.tudelft.simulation.introspection.Property#getType()
299 		 */
300 		public Class getType()
301 		{
302 			return CollectionTableModel.this.instances.get(this.key).getClass();
303 		}
304 
305 		/***
306 		 * @see nl.tudelft.simulation.introspection.Property#getValue()
307 		 */
308 		public Object getValue()
309 		{
310 			return CollectionTableModel.this.instances.get(this.key);
311 		}
312 
313 		/***
314 		 * @see nl.tudelft.simulation.introspection.Property#isEditable()
315 		 */
316 		public boolean isEditable()
317 		{
318 			return true;
319 		}
320 
321 		/***
322 		 * @see nl.tudelft.simulation.introspection.AbstractProperty#setRegularValue(java.lang.Object)
323 		 */
324 		protected void setRegularValue(final Object value)
325 		{
326 			throw new IllegalArgumentException(this + " is only supposed to be"
327 					+ " set to composite values."
328 					+ "A program is not supposed to arrive here.");
329 		}
330 
331 		/***
332 		 * @see java.lang.Object#toString()
333 		 */
334 		public String toString()
335 		{
336 			return "Coll.Prop, key:" + this.key;
337 		}
338 	}
339 
340 	/***
341 	 * @see nl.tudelft.simulation.introspection.table.DynamicTableModel#createRow()
342 	 */
343 	public void createRow()
344 	{
345 		if (this.componentType == null)
346 		{
347 			this.componentType = this.parentProperty.getComponentType();
348 			if (this.componentType == null)
349 			{
350 				return;
351 			}
352 		}
353 		try
354 		{
355 			Constructor instanceConstructor = this.componentType
356 					.getConstructor(new Class[0]);
357 			Object instance = instanceConstructor.newInstance(new Object[0]);
358 			addValue(instance);
359 			this.buttons.add(new ExpandButton(
360 					getProperty(this.instances.size() - 1), this));
361 			update();
362 		} catch (Exception e)
363 		{
364 			Logger.warning(this, "createRow",
365 					"Could not instantiate new instance: " + e.getMessage());
366 		}
367 	}
368 
369 	/***
370 	 * @see nl.tudelft.simulation.introspection.table.DynamicTableModel#createRows(int)
371 	 */
372 	public void createRows(final int amount)
373 	{
374 		for (int i = 0; i < amount; i++)
375 		{
376 			this.createRow();
377 		}
378 	}
379 
380 	/***
381 	 * @see nl.tudelft.simulation.introspection.table.DynamicTableModel#deleteRow(int)
382 	 */
383 	public void deleteRow(final int index)
384 	{
385 		Integer deletionKey = (Integer) this.keys.get(index);
386 		this.instances.remove(deletionKey);
387 		this.keys.remove(index);
388 		this.buttons.remove(index);
389 		update();
390 	}
391 
392 	/***
393 	 * @see nl.tudelft.simulation.introspection.table.DynamicTableModel#deleteRows(int[])
394 	 */
395 	public synchronized void deleteRows(final int[] indices)
396 	{
397 		Arrays.sort(indices);
398 		for (int i = indices.length - 1; i >= 0; i--)
399 		{
400 			deleteRow(indices[i]);
401 		}
402 	}
403 
404 	/***
405 	 * @see nl.tudelft.simulation.introspection.gui.IntrospectingTableModelInterface
406 	 *      #getIntrospector()
407 	 */
408 	public Introspector getIntrospector()
409 	{
410 		return this.introspector;
411 	}
412 
413 	/***
414 	 * @see nl.tudelft.simulation.introspection.gui.IntrospectingTableModelInterface
415 	 *      #getTypeAt(int,int)
416 	 */
417 	public Class getTypeAt(final int rowIndex, final int columnIndex)
418 	{
419 		if (columnIndex == 0)
420 		{
421 			return String.class;
422 		}
423 		if (columnIndex == 1)
424 		{
425 			return ExpandButton.class;
426 		}
427 		if (columnIndex == 2)
428 		{
429 			return this.instances.get(this.keys.get(rowIndex)).getClass();
430 		}
431 		return null;
432 	}
433 
434 	/***
435 	 * Sets the modelmanager. By default, a {see DefaultModelManager}is used.
436 	 * 
437 	 * @param manager the manager
438 	 */
439 	public void setModelManager(final ModelManager manager)
440 	{
441 		this.manager = manager;
442 	}
443 
444 	/***
445 	 * By default, a {see DefaultModelManager}returned.
446 	 * 
447 	 * @see nl.tudelft.simulation.introspection.gui.IntrospectingTableModelInterface
448 	 *      #getModelManager()
449 	 * @return the Manager
450 	 */
451 	public ModelManager getModelManager()
452 	{
453 		return this.manager;
454 	}
455 }