Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/pjsofts/eurobudget/beans/BeanTableModel.java


1   package com.pjsofts.eurobudget.beans;
2   
3   import com.pjsofts.eurobudget.EBConstants;
4   import java.beans.*;
5   import java.lang.reflect.InvocationTargetException;
6   import java.util.*;
7   import javax.swing.table.AbstractTableModel;
8   
9   /**
10   *
11   * Simple Table model to display any bean in read only or write mode.
12   * Need to use it with a nice bean (got bean info ..)
13   * quite powerful already (editable, filterable (one column value), ...)
14   * Future: 
15   * make it autosizable, find, goto,sortable, choose columns, save columns order...
16   * 
17   */
18  public class BeanTableModel extends AbstractTableModel {
19      
20      /** List of same Bean class, original list of beans */
21      protected List list;
22  
23      /** displayed list */
24      protected List filteredList;
25      
26      /** */
27      private BeanInfo beanInfo = null;
28  
29      /** not hidden descriptors */
30      private PropertyDescriptor[] descriptors = null;
31      
32      /** contains the displayed columns order */
33      private String[] columns=null;
34      
35      /** is the whole table editable ?? */
36      private boolean tableEditable = true;
37      
38      private static final ResourceBundle i8n = EBConstants.i18n;
39      
40      /** 
41       * @param not empty list and modifiable also (if editable), should not contain twice same bean, better to be a RandomAccess 
42       *  Should also contains only instances of same bean (same class !!!)
43       */
44      public BeanTableModel(List list, boolean editable, Class beanClass) {
45          if ( list == null ) list = new ArrayList();
46          this.list = list;
47          this.filteredList = list;
48          assert !list.isEmpty();
49          //assert list contains only instances of same class 
50          //Object bean = list.get(0);
51          initStructureToBean(beanClass);//bean.getClass());
52          this.tableEditable = editable;
53      }
54      
55      private void initStructureToBean(Class beanClass) {
56          int size = 0;
57          BeanDescriptor descriptor;
58          PropertyDescriptor[] props = null;
59          try {
60              this.beanInfo= Introspector.getBeanInfo(beanClass,Introspector.USE_ALL_BEANINFO);
61              descriptor = beanInfo.getBeanDescriptor();
62              props = beanInfo.getPropertyDescriptors();
63              size = props.length;
64          } catch ( IntrospectionException ie) {
65              ie.printStackTrace();
66          }
67          // remove hidden descriptor
68          if ( size > 0 && props != null) {
69              Vector v = new Vector();
70              for (int i=0; i<props.length ;i++) {
71                  if ( ! props[i].isHidden() ) {
72                      v.add(props[i]);
73                  }
74              }
75              this.descriptors = (PropertyDescriptor[])v.toArray(new PropertyDescriptor[v.size()]);
76              columns = new String[v.size()];
77              for (int i=0; i<v.size() ;i++) {
78                  columns[i] = this.descriptors[i].getName();
79              }
80          }
81       }
82  
83      /** convenient method 
84       * @param attribute name of used bean (ignore case)
85       * @return column index in model or -1 if not found
86       */
87      public int getColumnIndex(String attName) {
88          for (int i=0; i<descriptors.length ;i++) {
89              if ( descriptors[i].getName().equalsIgnoreCase(attName) )
90                  return i;
91    }
92          return -1;
93      }
94      
95      public int getRowCount() {return filteredList != null ? filteredList.size() : 0; }
96      
97      public int getColumnCount(){
98          return columns != null ? columns.length : 0;
99      }
100     
101     public Object getValueAt(int row, int col){
102         Object bean = filteredList.get(row);
103         Object result = null;
104         try {
105             result = descriptors[col].getReadMethod().invoke(bean,null);
106         } catch (InvocationTargetException ite  ) {
107             ite.printStackTrace();
108         } catch (IllegalAccessException iae ) {
109             iae.printStackTrace();
110         }
111         return result;
112     }
113     
114     /** */
115     public String getColumnName(int col) {
116         return descriptors[col].getDisplayName();
117     }
118     
119         /*
120          * JTable uses this method to determine the default renderer/
121          * editor for each cell.  If we didn't implement this method,
122          * then the last column would contain text ("true"/"false"),
123          * rather than a check box.
124          */
125     public Class getColumnClass(int c) {
126         return descriptors[c].getPropertyType();
127     }
128     
129     public boolean isCellEditable(int row, int col) {
130         return this.tableEditable;
131     }
132     
133     /**
134      * Sets the value in the cell at <code>columnIndex</code> and
135      * <code>rowIndex</code> to <code>aValue</code>.
136      *
137      * @param  aValue     the new value
138      * @param  rowIndex   the row whose value is to be changed
139      * @param  columnIndex    the column whose value is to be changed
140      * @see #getValueAt
141      * @see #isCellEditable
142      */
143     public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
144         Object bean = filteredList.get(rowIndex);
145         try {
146             Object []args = new Object[1];
147             args[0] = aValue;
148             descriptors[columnIndex].getWriteMethod().invoke(bean,args);
149         } catch (InvocationTargetException ite  ) {
150             ite.printStackTrace();
151         } catch (IllegalAccessException iae ) {
152             iae.printStackTrace();
153         }
154 //        super.setValueAt(aValue, rowIndex, columnIndex);
155         fireTableCellUpdated(rowIndex,columnIndex);
156     }
157 
158     /** create and @return line of a new bean (or -1 if error)*/
159     public int newRow() {
160         Class beanClass = beanInfo.getBeanDescriptor().getBeanClass();
161         Object newBean = null;
162         try { newBean =beanClass.newInstance(); 
163         } catch(InstantiationException ie) {
164             ie.printStackTrace();
165         } catch(IllegalAccessException iae) {
166             iae.printStackTrace();
167         }
168         if ( newBean != null ) {
169             int row = addRow(newBean);
170             return row;
171         }
172         return -1;
173     }
174     
175     /** add a bean to the list 
176      * @return line number 
177      */
178     public int addRow(Object bean) {
179         int ind = filteredList.size();
180         this.filteredList.add(ind, bean);
181         if ( this.list != this.filteredList )
182             this.list.add(bean);
183         fireTableRowsInserted(ind,ind);
184         return ind;
185     }
186     
187     public void deleteRow(int row){
188         Object o = filteredList.remove(row);
189         if ( this.list != this.filteredList )
190             this.list.remove(o);
191         fireTableRowsDeleted(row, row);
192     }
193 
194     /**
195      * change the model structure to show only beans that got 'value' as 'column'
196      * @param column column index, -1 means no more filter
197      * @param value on which to filter (use equals not == ), don't like null
198      */
199     public void applyFilter(int col, Object value) {
200         if ( col == -1 ) {
201             this.filteredList = this.list;
202         } else {
203             List newList = null;
204             synchronized ( list ) {
205                 newList = new ArrayList(this.list.size());
206                 for (Iterator it = this.list.iterator(); it.hasNext() ;) {
207                     Object object = it.next();
208                     Object beanValue = null;
209                     try { beanValue = descriptors[col].getReadMethod().invoke(object,null); 
210                     } catch (IllegalAccessException iae) {
211                         iae.printStackTrace();
212                     } catch (InvocationTargetException ite) {                
213                         ite.printStackTrace();
214                     }
215                     if ( value.equals(beanValue) ) 
216                         newList.add(object);
217                 }
218             }
219             this.filteredList = null;//gc flag
220             this.filteredList = newList;            
221         }
222         fireTableDataChanged();
223     }
224 
225     /**
226      * change the model structure to sort items on this column.
227      * Stable sort.
228      * Bean properties classes must support a Comparable interface
229      * @param column column index, -1 means no more sort.
230      */
231     public void sortColumn(int col, boolean ascending ) {
232         if ( col == -1 ) {
233             this.filteredList = this.list;            
234             fireTableStructureChanged(); //reinit headers
235         } else {
236             List newList;
237             synchronized ( this.filteredList ) {
238                 newList = new ArrayList(this.filteredList);//copy old list
239                 Collections.sort(newList, new BeanComparator(col,ascending));
240             }
241             this.filteredList = null;//gc flag
242             this.filteredList = newList;
243             fireTableDataChanged(); //update only data, caller should update header if they want
244         }
245     }
246 
247     
248     private class BeanComparator implements Comparator {
249         private int index;
250         private boolean ascending;
251         public BeanComparator(int index, boolean ascending) {
252             this.index = index;
253             this.ascending = ascending;
254         }
255         /**
256          * Compares its two arguments for order.  Returns a negative integer,
257          * zero, or a positive integer as the first argument is less than, equal
258          * to, or greater than the second.<p>
259          *
260          * The implementor must ensure that <tt>sgn(compare(x, y)) ==
261          * -sgn(compare(y, x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
262          * implies that <tt>compare(x, y)</tt> must throw an exception if and only
263          * if <tt>compare(y, x)</tt> throws an exception.)<p>
264          *
265          * The implementor must also ensure that the relation is transitive:
266          * <tt>((compare(x, y)&gt;0) &amp;&amp; (compare(y, z)&gt;0))</tt> implies
267          * <tt>compare(x, z)&gt;0</tt>.<p>
268          *
269          * Finally, the implementer must ensure that <tt>compare(x, y)==0</tt>
270          * implies that <tt>sgn(compare(x, z))==sgn(compare(y, z))</tt> for all
271          * <tt>z</tt>.<p>
272          *
273          * It is generally the case, but <i>not</i> strictly required that
274          * <tt>(compare(x, y)==0) == (x.equals(y))</tt>.  Generally speaking,
275          * any comparator that violates this condition should clearly indicate
276          * this fact.  The recommended language is "Note: this comparator
277          * imposes orderings that are inconsistent with equals."
278          *
279          * @param o1 the first object to be compared.
280          * @param o2 the second object to be compared.
281          * @return a negative integer, zero, or a positive integer as the
282          *          first argument is less than, equal to, or greater than the
283          *          second.
284          * @throws ClassCastException if the arguments' types prevent them from
285          *          being compared by this Comparator.
286          */
287         public int compare(Object o1, Object o2) {
288             Object v1 = null,v2 = null;
289             try {
290                 v1 = descriptors[index].getReadMethod().invoke(o1,null);
291                 v2 = descriptors[index].getReadMethod().invoke(o2,null);
292             } catch (IllegalAccessException iae) {
293                 iae.printStackTrace();
294             } catch (InvocationTargetException ite) {
295                 ite.printStackTrace();
296             }
297             if ( v1 == null ) return ascending ? 1 : -1;
298             if ( v1 instanceof Comparable) {
299                 Comparable c1 = (Comparable)v1;
300                 Comparable c2 = (Comparable)v2;
301                 //System.out.println(c1 + " <> "+c2);
302                 return ascending ? c1.compareTo(c2) : c2.compareTo(c1);
303             } else {
304                 throw new IllegalArgumentException(i8n.getString("error_Use_sort_only_on_comparable_fields!"));
305             }
306         }        
307      }
308 }
309 
310