Coverage Report - net.wotonomy.ui.swing.components.PropertyEditorTableModel
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertyEditorTableModel
0% 
0% 
3.389
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Michael Powers
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 17  
 */
 18  
 
 19  
 package net.wotonomy.ui.swing.components;
 20  
 
 21  
 import java.awt.Color;
 22  
 import java.awt.Component;
 23  
 import java.awt.Font;
 24  
 import java.awt.event.ActionEvent;
 25  
 import java.awt.event.ActionListener;
 26  
 import java.lang.reflect.Method;
 27  
 import java.util.Hashtable;
 28  
 import java.util.Vector;
 29  
 
 30  
 import javax.swing.Timer;
 31  
 import javax.swing.table.AbstractTableModel;
 32  
 
 33  
 /**
 34  
 * PropertyEditorTableModel introspects an object to facilitate
 35  
 * editing it in a PropertyTable.
 36  
 *
 37  
 * Because the model always reflects the current state of the
 38  
 * inspected object, it is useful to have a table update at
 39  
 * automated intervals.  By default, this feature is turned off.
 40  
 * If you turn it on, you'll want to remember to turn it off
 41  
 * when you're done with the table model.
 42  
 *
 43  
 * @author michael@mpowers.net
 44  
 * @author $Author: cgruber $
 45  
 * @version $Revision: 904 $
 46  
 */
 47  0
 public class PropertyEditorTableModel extends AbstractTableModel implements ActionListener
 48  
 {
 49  0
         protected Object inspectedObject = null;
 50  
 
 51  0
         final String[] columnNames = { "Property", "Value" };
 52  
     static final String METHOD_TAG = " ";
 53  
 
 54  0
         Vector properties = new Vector();
 55  0
         Hashtable methods = new Hashtable(0);
 56  
 
 57  
         public void setObject( Object o ) {
 58  0
                 inspectedObject = o;
 59  0
                 Class c = o.getClass();
 60  0
                 Method[] m = c.getMethods();
 61  
 
 62  0
                 properties = new Vector();
 63  0
                 methods = new Hashtable(m.length, 1F);
 64  
                 String name, propertyName;
 65  0
                 for ( int i = 0; i < m.length; i++ ) {
 66  
 //                                if ( m[i].getName().startsWith( "get" ) )
 67  
 //                                        System.out.println( m[i].getName() + ": " + m[i].getReturnType().getName() );
 68  
 
 69  
                         // get methods
 70  0
                         if (
 71  0
                                  ( ( m[i].getName().startsWith( "get" ) ) || ( m[i].getName().startsWith( "is" ) ) )
 72  0
                                 && ( m[i].getParameterTypes().length == 0 )
 73  
                         ) {
 74  0
                 name = m[i].getName();
 75  0
                 if ( m[i].getName().startsWith( "is" ) ) {
 76  0
                     propertyName = name.substring( 2, name.length() );
 77  
                     // probably should only add "is" methods if accompanied by "set" method
 78  0
                 } else { // "get"
 79  0
                     propertyName = name.substring( 3, name.length() );
 80  
                 }
 81  0
                                 if (
 82  0
                                         ( m[i].getReturnType().getName().equals( String.class.getName() ) )
 83  0
                                 ||  ( m[i].getReturnType().getName().equals( "boolean" ) )
 84  0
                                 ||  ( m[i].getReturnType().getName().equals( "int" ) )
 85  0
                                 ||  ( m[i].getReturnType().getName().equals( "float" ) )
 86  0
                                 ||  ( m[i].getReturnType().getName().equals( "char" ) )
 87  0
                                 ||  ( m[i].getReturnType().getName().equals( Color.class.getName() ) )
 88  0
                                 ||  ( m[i].getReturnType().getName().equals( Font.class.getName() ) )
 89  
                                 ) {
 90  0
                                         properties.addElement( propertyName ); // put property
 91  0
                                         methods.put( name, m[i] ); // put method
 92  0
                                 }
 93  
                 else
 94  
                 {
 95  
                     // handle unknown types as invokable methods:
 96  0
                     properties.addElement( propertyName + METHOD_TAG );
 97  0
                     methods.put( propertyName + METHOD_TAG, m[i] );
 98  
                 }
 99  0
                         }
 100  
             else
 101  
                         // set methods
 102  0
                         if ( ( m[i].getName().startsWith( "set" ) ) &&
 103  0
                                 ( m[i].getParameterTypes().length == 1 ) ) {
 104  0
                                 name = m[i].getName();
 105  0
                                 if (
 106  0
                                         ( m[i].getParameterTypes()[0].getName().equals( String.class.getName() ) )
 107  0
                                 ||  ( m[i].getParameterTypes()[0].getName().equals( "boolean" ) )
 108  0
                                 ||  ( m[i].getParameterTypes()[0].getName().equals( "int" ) )
 109  0
                                 ||  ( m[i].getParameterTypes()[0].getName().equals( "float" ) )
 110  0
                                 ||  ( m[i].getParameterTypes()[0].getName().equals( Color.class.getName() ) )
 111  
 //                                ||  ( m[i].getParameterTypes()[0].getName().equals( Font.class.getName() ) )
 112  
                                 ) {
 113  
 //                                        System.out.println( "Accepted: " + name + ": " + m[i].getParameterTypes()[0].getName() );
 114  0
                                         methods.put( name, m[i] ); // set method
 115  0
                                 } else {
 116  
 //                                        System.out.println( "Rejected: " + name + ": " + m[i].getParameterTypes()[0].getName() );
 117  
                                 }
 118  
                         }
 119  
             else
 120  
             // zero-parameter methods to be invoked on click
 121  0
                         if ( m[i].getParameterTypes().length == 0 )
 122  
             {
 123  0
                 properties.addElement( m[i].getName() + METHOD_TAG );
 124  0
                 methods.put( m[i].getName() + METHOD_TAG, m[i] );
 125  
             }
 126  
 
 127  
                 }
 128  
 
 129  0
                 sort(properties);
 130  0
                 fireTableDataChanged();
 131  0
         }
 132  
 
 133  
         public int getColumnCount() {
 134  0
           return columnNames.length;
 135  
         }
 136  
 
 137  
         public int getRowCount() {
 138  0
           return properties.size();
 139  
         }
 140  
 
 141  
         public String getColumnName(int col) {
 142  0
           return columnNames[col];
 143  
         }
 144  
 
 145  
         public Object getValueAt(int row, int col) {
 146  0
                 if ( col == 0 )
 147  0
                         return properties.elementAt( row );
 148  
                 else
 149  
                 {
 150  0
                         Method m = null;
 151  0
                                 m = (Method) methods.get( "get" + ( (String) properties.elementAt( row ) ) ) ;
 152  0
                         if ( m == null ) // try "is"
 153  0
                                 m = (Method) methods.get( "is" + ( (String) properties.elementAt( row ) ) ) ;
 154  0
             if ( m == null ) { // try entire method name
 155  0
                                 m = (Method) methods.get( (String) properties.elementAt( row ) ) ;
 156  0
                 if ( m != null ) return m;
 157  
             }
 158  
                         try {
 159  0
                                 return m.invoke( inspectedObject, (Object[])null );
 160  0
                         } catch ( Exception exc ) {
 161  0
                                 System.out.println( "InspectorFrame.tableModel.getValueAt: error occured while reflecting: " );
 162  0
                                 System.out.println( exc );
 163  
                         }
 164  0
                         return null;
 165  
                 }
 166  
         }
 167  
 
 168  
         public Class getColumnClass( int col ) {
 169  
 //                System.out.println( "getColumnClass" );
 170  
 /*                try {
 171  
                         throw new Exception();
 172  
                 } catch ( Exception exc ) {
 173  
                         exc.printStackTrace( System.out );
 174  
                 }
 175  0
 */                return new String().getClass();
 176  
         }
 177  
 
 178  
         public Class getCellClass(int row, int col) {
 179  
 //                System.out.println( "getCellClass" );
 180  
 /*
 181  
 
 182  
                 Class c;
 183  
                 Method m = (Method) methods.get( "set" + ( (String) properties.elementAt( row ) ) ) ;
 184  
                 if ( m == null )
 185  
                         c = new Object().getClass();
 186  
                 else {
 187  
                         c = m.getParameterTypes()[0];
 188  
 
 189  
                         // special case for boolean
 190  
                         if ( c.getName().equals( "boolean" ) )
 191  
                                 c = new Boolean(true).getClass();
 192  
 
 193  
                         // special case for int
 194  
                         if ( c.getName().equals( "int" ) )
 195  
                                 c = new Integer(0).getClass();
 196  
                 }
 197  
                 System.out.println( row + ": " + c.getName() );
 198  
                 return c;
 199  
 */
 200  
 
 201  
 //                return new String().getClass();
 202  
 
 203  0
                 Object o = getValueAt( row, col );
 204  0
                 if ( o == null ) o = "null";
 205  0
                 return o.getClass();
 206  
         }
 207  
 
 208  
         /*
 209  
         * Don't need to implement this method unless your table's
 210  
         * editable.
 211  
         */
 212  
         public boolean isCellEditable(int row, int col) {
 213  
           //Note that the data/cell address is constant,
 214  
           //no matter where the cell appears onscreen.
 215  0
           if (col < 1) {
 216  0
                   return false;
 217  
           } else {
 218  
         // handle method invocation
 219  0
         if ( ((String)properties.elementAt(row)).endsWith(METHOD_TAG) ) return true;
 220  
         // handle read-only properties
 221  0
                 Method m = (Method) methods.get( "set" + ( (String) properties.elementAt( row ) ) ) ;
 222  0
                 if ( m == null )
 223  0
                         return false;
 224  
                 else
 225  0
                         return true;
 226  
           }
 227  
         }
 228  
 
 229  
         /*
 230  
         * Don't need to implement this method unless your table's
 231  
         * data can change.
 232  
         */
 233  
         public void setValueAt(Object value, int row, int col) {
 234  
         // test for inspected object
 235  0
                 if ( inspectedObject == null ) return;
 236  
         // handle method invocation - no need to update values
 237  0
         if ( ((String)properties.elementAt(row)).endsWith( METHOD_TAG ) )
 238  
         {
 239  0
             fireTableDataChanged();
 240  0
             return;
 241  
         };
 242  
 
 243  
         // handle writable properties
 244  0
                 Method m = (Method) methods.get( "set" + ( (String) properties.elementAt( row ) ) ) ;
 245  0
                 String parameterType = m.getParameterTypes()[0].getName();
 246  
 
 247  
                 // ugly cast code
 248  0
                 if (
 249  0
                         ( parameterType.equals( "int" ) )
 250  0
                 ||        ( parameterType.equals( "java.lang.Integer" ) )
 251  
                 )
 252  
                 {
 253  
             try {
 254  0
                 value = new Integer((String)value);
 255  0
             } catch (NumberFormatException e) {
 256  0
                  System.out.println("PropertyEditorTableModel.setValueAt: User attempted to enter non-integer"
 257  0
                                + " value (" + value
 258  0
                                + ") into an integer-only column.");
 259  0
             }
 260  
                 }
 261  0
                 Object[] parameters = { value };
 262  
                 try {
 263  0
                         m.invoke( inspectedObject, parameters );
 264  0
                         if ( inspectedObject instanceof Component ) {
 265  0
                                 Component c = (Component)inspectedObject;
 266  0
                                 if ( c.getParent() != null )
 267  0
                                         c.getParent().repaint();
 268  
                         }
 269  0
                 } catch ( Exception exc ) {
 270  0
                         System.out.println( "PropertyEditorTableModel.setValueAt: error occured while reflecting: " );
 271  0
                         System.out.println( exc );
 272  0
                 }
 273  
 
 274  0
                 fireTableDataChanged();
 275  0
         }
 276  
 
 277  
 
 278  
     protected void sort(Vector v){
 279  0
         quickSort(v, 0, v.size()-1);
 280  0
     }
 281  
 
 282  
 
 283  
     // Liberated from the BasicDirectoryModel which was...
 284  
     // Liberated from the 1.1 SortDemo
 285  
 
 286  
     // This is a generic version of C.A.R Hoare's Quick Sort
 287  
     // algorithm.  This will handle arrays that are already
 288  
     // sorted, and arrays with duplicate keys.<BR>
 289  
     //
 290  
     // If you think of a one dimensional array as going from
 291  
     // the lowest index on the left to the highest index on the right
 292  
     // then the parameters to this function are lowest index or
 293  
     // left and highest index or right.  The first time you call
 294  
     // this function it will be with the parameters 0, a.length - 1.
 295  
     //
 296  
     // @param a       an integer array
 297  
     // @param lo0     left boundary of array partition
 298  
     // @param hi0     right boundary of array partition
 299  
     private void quickSort(Vector v, int lo0, int hi0) {
 300  0
         int lo = lo0;
 301  0
         int hi = hi0;
 302  
         String mid;
 303  
 
 304  0
         if (hi0 > lo0) {
 305  
             // Arbitrarily establishing partition element as the midpoint of
 306  
             // the array.
 307  0
             mid = (String) v.elementAt((lo0 + hi0) / 2);
 308  
 
 309  
             // loop through the array until indices cross
 310  0
             while(lo <= hi) {
 311  
                 // find the first element that is greater than or equal to
 312  
                 // the partition element starting from the left Index.
 313  
                 //
 314  
                 // Nasty to have to cast here. Would it be quicker
 315  
                 // to copy the vectors into arrays and sort the arrays?
 316  0
                 while((lo < hi0) && lt((String)v.elementAt(lo), mid)) {
 317  0
                     ++lo;
 318  0
                 }
 319  
 
 320  
                 // find an element that is smaller than or equal to
 321  
                 // the partition element starting from the right Index.
 322  0
                 while((hi > lo0) && lt(mid, (String)v.elementAt(hi))) {
 323  0
                     --hi;
 324  0
                 }
 325  
 
 326  
                 // if the indexes have not crossed, swap
 327  0
                 if(lo <= hi) {
 328  0
                     swap(v, lo, hi);
 329  0
                     ++lo;
 330  0
                     --hi;
 331  0
                 }
 332  
             }
 333  
 
 334  
 
 335  
             // If the right index has not reached the left side of array
 336  
             // must now sort the left partition.
 337  0
             if(lo0 < hi) {
 338  0
                 quickSort(v, lo0, hi);
 339  
             }
 340  
 
 341  
             // If the left index has not reached the right side of array
 342  
             // must now sort the right partition.
 343  0
             if(lo < hi0) {
 344  0
                 quickSort(v, lo, hi0);
 345  
             }
 346  
 
 347  
         }
 348  0
     }
 349  
 
 350  
     private void swap(Vector a, int i, int j) {
 351  0
         Object T = a.elementAt(i);
 352  0
         a.setElementAt(a.elementAt(j), i);
 353  0
         a.setElementAt(T, j);
 354  0
     }
 355  
 
 356  
     protected boolean lt(String a, String b) {
 357  0
         return a.compareTo(b) < 0;
 358  
     }
 359  
 
 360  
     // automated updates
 361  
 
 362  0
     private boolean autoUpdating = false;
 363  0
     private int updateInterval = 2000; // one-second delay on average
 364  0
     protected Timer timer = null;
 365  
 
 366  
     public boolean isAutoUpdating()
 367  
     {
 368  0
         return autoUpdating;
 369  
     }
 370  
 
 371  
     public void setAutoUpdating( boolean shouldAutoUpdate )
 372  
     {
 373  0
         if ( shouldAutoUpdate )
 374  
         {
 375  0
             if ( timer == null )
 376  
             {
 377  0
                 timer = new Timer( updateInterval, this );
 378  0
                 timer.setRepeats( true );
 379  0
                 timer.start();
 380  0
             }
 381  
         }
 382  
         else
 383  
         {
 384  0
             if ( timer != null )
 385  
             {
 386  0
                 timer.stop();
 387  0
                 timer = null;
 388  
             }
 389  
         }
 390  
 
 391  0
         autoUpdating = shouldAutoUpdate;
 392  0
     }
 393  
 
 394  
     public int getUpdateInterval()
 395  
     {
 396  0
         return updateInterval;
 397  
     }
 398  
 
 399  
     public void setUpdateInterval( int anInterval )
 400  
     {
 401  0
         if ( timer != null )
 402  
         {
 403  0
             timer.setDelay( anInterval );
 404  
         }
 405  
 
 406  0
         updateInterval = anInterval;
 407  0
     }
 408  
 
 409  
     public void actionPerformed( ActionEvent evt )
 410  
     {
 411  0
         if ( evt.getSource() == timer )
 412  
         {
 413  0
             fireTableDataChanged();
 414  
         }
 415  0
     }
 416  
 
 417  
 }
 418