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