View Javadoc

1   /*
2    * @(#) CollectionTableModel.java Apr 15, 2004 Copyright (c) 2002-2005 Delft
3    * University of Technology Jaffalaan 5, 2628 BX Delft, the Netherlands. All
4    * rights reserved. This software is proprietary information of Delft University
5    * of Technology The code is published under the Lesser General Public License
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         // Initialize buttons
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         // Generate a List reflecting changes
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 }