Coverage Report - net.wotonomy.ui.swing.ButtonAssociation
 
Classes in this File Line Coverage Branch Coverage Complexity
ButtonAssociation
0% 
0% 
2.769
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Intersect Software Corporation
 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;
 20  
 
 21  
 import java.awt.Component;
 22  
 import java.util.Iterator;
 23  
 
 24  
 import javax.swing.ButtonModel;
 25  
 import javax.swing.event.ChangeEvent;
 26  
 import javax.swing.event.ChangeListener;
 27  
 
 28  
 import net.wotonomy.foundation.NSArray;
 29  
 import net.wotonomy.foundation.NSSelector;
 30  
 import net.wotonomy.foundation.internal.ValueConverter;
 31  
 import net.wotonomy.foundation.internal.WotonomyException;
 32  
 import net.wotonomy.ui.EOAssociation;
 33  
 import net.wotonomy.ui.EODisplayGroup;
 34  
 
 35  
 /**
 36  
 * ButtonAssociation binds any component that uses a ButtonModel
 37  
 * (all Swing button classes) to a display group.  This association
 38  
 * should be used to handle individual JRadioButtons and JCheckBoxes.
 39  
 * Bindings are:
 40  
 * <ul>
 41  
 * <li>value: a boolean property that determines the
 42  
 * selected state of the button model.  This will set
 43  
 * the value for radio buttons and check boxes.</li>
 44  
 * <li>enabled: a boolean property that determines the
 45  
 * enabled state of the button model.</li>
 46  
 * <li>visible: a boolean property that determines the
 47  
 * visible state of the button model.</li>
 48  
 * </ul>
 49  
 *
 50  
 * @author michael@mpowers.net
 51  
 * @author $Author: cgruber $
 52  
 * @version $Revision: 904 $
 53  
 */
 54  0
 public class ButtonAssociation extends EOAssociation
 55  
     implements ChangeListener
 56  
 {
 57  0
     static final NSArray aspects =
 58  0
         new NSArray( new Object[] {
 59  0
             ValueAspect, EnabledAspect, VisibleAspect
 60  
         } );
 61  0
     static final NSArray aspectSignatures =
 62  0
         new NSArray( new Object[] {
 63  0
             AttributeToOneAspectSignature,
 64  0
             AttributeToOneAspectSignature,
 65  0
             AttributeToOneAspectSignature
 66  
         } );
 67  0
     static final NSArray objectKeysTaken =
 68  0
         new NSArray( new Object[] {
 69  0
             "model.selected"
 70  
         } );
 71  
 
 72  0
     static NSSelector getModel =
 73  0
         new NSSelector( "getModel", new Class[] {} );
 74  
 
 75  
     protected ButtonModel buttonModel;
 76  
     protected boolean lastKnownValue;
 77  
 
 78  
     /**
 79  
     * Constructor specifying the object to be controlled by this
 80  
     * association.  Does not establish connection.
 81  
     * This implementation expects a ButtonModel or a class
 82  
     * that has a "getModel" method that returns a ButtonModel.
 83  
     */
 84  
     public ButtonAssociation ( Object anObject )
 85  
     {
 86  0
         super( anObject );
 87  
 
 88  0
         if ( anObject instanceof ButtonModel )
 89  
         {
 90  0
             buttonModel = (ButtonModel) anObject;
 91  0
         }
 92  
         else
 93  
         {
 94  
             try
 95  
             {
 96  0
                 buttonModel = (ButtonModel) getModel.invoke( anObject );
 97  
             }
 98  0
             catch ( Exception exc )
 99  
             {
 100  0
                 throw new WotonomyException( "EOButtonAssociation: " +
 101  0
                     "could not retrieve a button model from object:" + anObject );
 102  0
             }
 103  
         }
 104  0
     }
 105  
 
 106  
     /**
 107  
     * Returns a List of aspect signatures whose contents
 108  
     * correspond with the aspects list.  Each element is
 109  
     * a string whose characters represent a capability of
 110  
     * the corresponding aspect. <ul>
 111  
     * <li>"A" attribute: the aspect can be bound to
 112  
     * an attribute.</li>
 113  
     * <li>"1" to-one: the aspect can be bound to a
 114  
     * property that returns a single object.</li>
 115  
     * <li>"M" to-one: the aspect can be bound to a
 116  
     * property that returns multiple objects.</li>
 117  
     * </ul>
 118  
     * An empty signature "" means that the aspect can
 119  
     * bind without needing a key.
 120  
     * This implementation returns "A1M" for each
 121  
     * element in the aspects array.
 122  
     */
 123  
     public static NSArray aspectSignatures ()
 124  
     {
 125  0
         return aspectSignatures;
 126  
     }
 127  
 
 128  
     /**
 129  
     * Returns a List that describes the aspects supported
 130  
     * by this class.  Each element in the list is the string
 131  
     * name of the aspect.  This implementation returns an
 132  
     * empty list.
 133  
     */
 134  
     public static NSArray aspects ()
 135  
     {
 136  0
         return aspects;
 137  
     }
 138  
 
 139  
     /**
 140  
     * Returns a List of EOAssociation subclasses that,
 141  
     * for the objects that are usable for this association,
 142  
     * are less suitable than this association.
 143  
     */
 144  
     public static NSArray associationClassesSuperseded ()
 145  
     {
 146  0
         return new NSArray();
 147  
     }
 148  
 
 149  
     /**
 150  
     * Returns whether this class can control the specified
 151  
     * object.
 152  
     * This implementation expects a ButtonModel or a class
 153  
     * that has a "getModel" method that returns a ButtonModel.
 154  
     */
 155  
     public static boolean isUsableWithObject ( Object anObject )
 156  
     {
 157  0
         return
 158  0
            ( anObject instanceof ButtonModel )
 159  0
         || ( getModel.implementedByObject( anObject ) );
 160  
     }
 161  
 
 162  
     /**
 163  
     * Returns a List of properties of the controlled object
 164  
     * that are controlled by this class.  For example,
 165  
     * "stringValue", or "selected".
 166  
     */
 167  
     public static NSArray objectKeysTaken ()
 168  
     {
 169  0
         return objectKeysTaken;
 170  
     }
 171  
 
 172  
     /**
 173  
     * Returns the aspect that is considered primary
 174  
     * or default.
 175  
     */
 176  
     public static String primaryAspect ()
 177  
     {
 178  0
         return ValueAspect;
 179  
     }
 180  
 
 181  
     /**
 182  
     * Returns whether this association can bind to the
 183  
     * specified display group on the specified key for
 184  
     * the specified aspect.
 185  
     */
 186  
     public boolean canBindAspect (
 187  
         String anAspect, EODisplayGroup aDisplayGroup, String aKey)
 188  
     {
 189  0
         return ( aspects.containsObject( anAspect ) );
 190  
     }
 191  
 
 192  
     /**
 193  
     * Establishes a connection between this association
 194  
     * and the controlled object.  Subclasses should begin
 195  
     * listening for events from their controlled object here.
 196  
     */
 197  
     public void establishConnection ()
 198  
     {
 199  0
         buttonModel.addChangeListener( this );
 200  0
         super.establishConnection();
 201  0
         subjectChanged();
 202  0
     }
 203  
 
 204  
     /**
 205  
     * Breaks the connection between this association and
 206  
     * its object.  Override to stop listening for events
 207  
     * from the object.
 208  
     */
 209  
     public void breakConnection ()
 210  
     {
 211  0
         buttonModel.removeChangeListener( this );
 212  0
         super.breakConnection();
 213  0
     }
 214  
 
 215  
     /**
 216  
     * Called when either the selection or the contents
 217  
     * of an associated display group have changed.
 218  
     * This implementation does nothing.
 219  
     */
 220  
     public void subjectChanged ()
 221  
     {
 222  0
         Object component = object();
 223  
         EODisplayGroup displayGroup;
 224  
         String key;
 225  
 
 226  
         // value aspect
 227  0
         displayGroup = displayGroupForAspect( ValueAspect );
 228  
 
 229  0
         if ( displayGroup != null )
 230  
         {
 231  0
             key = displayGroupKeyForAspect( ValueAspect );
 232  0
             if ( component instanceof Component )
 233  
             {
 234  0
                 ((Component)component).setEnabled( 
 235  0
                         displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
 236  
             }
 237  
             
 238  
             Object value;
 239  0
             if ( displayGroup.selectedObjects().size() > 1 )
 240  
             {
 241  
                 // if there're more than one object selected, set
 242  
                 // the value to blank for all of them.
 243  
                 Object previousValue;
 244  
 
 245  0
                 Iterator indexIterator = displayGroup.selectionIndexes().
 246  0
                                           iterator();
 247  
 
 248  
                 // get value for the first selected object.
 249  0
                 int initialIndex = ( (Integer)indexIterator.next() ).intValue();
 250  0
                 previousValue = displayGroup.valueForObjectAtIndex(
 251  0
                                           initialIndex, key );
 252  0
                 value = null;
 253  
 
 254  
                 // go through the rest of the selected objects, compare each
 255  
                 // value with the previous one. continue comparing if two
 256  
                 // values are equal, break the while loop if they're different.
 257  
                 // the final value will be the common value of all selected objects
 258  
                 // if there is one, or be blank if there is not.
 259  0
                 while ( indexIterator.hasNext() )
 260  
                 {
 261  0
                     int index = ( (Integer)indexIterator.next() ).intValue();
 262  0
                     Object currentValue = displayGroup.valueForObjectAtIndex(
 263  0
                                           index, key );
 264  0
                     if ( currentValue != null && !currentValue.equals( previousValue ) )
 265  
                     {
 266  0
                         value = null;
 267  0
                         break;
 268  
                     }
 269  
                     else
 270  
                     {
 271  
                         // currentValue is the same as the previous one
 272  0
                         value = currentValue;
 273  
                     }
 274  
 
 275  0
                 } // end while
 276  
 
 277  0
             } 
 278  
             else // displayGroup has only one object 
 279  
             {
 280  0
                 value =
 281  0
                     displayGroup.selectedObjectValueForKey( key );
 282  
             } // end checking size of displayGroup
 283  
 
 284  0
             buttonModel.setArmed( false );
 285  0
             buttonModel.setPressed( false );
 286  
             
 287  0
             if ( value != null )
 288  
             {
 289  0
                 Boolean converted = (Boolean)
 290  0
                     ValueConverter.convertObjectToClass(
 291  0
                         value, Boolean.class );
 292  0
                 if ( converted != null )
 293  
                 {
 294  0
                     lastKnownValue = converted.booleanValue();
 295  0
                     if ( converted.booleanValue() !=
 296  0
                         buttonModel.isSelected() )
 297  
                     {
 298  0
                         buttonModel.removeChangeListener( this );
 299  0
                         buttonModel.setSelected(
 300  0
                             converted.booleanValue() );
 301  0
                         buttonModel.addChangeListener( this );
 302  
                     }
 303  
                 } // end checking converted  == null
 304  0
             }
 305  
             else
 306  
             {
 307  0
                 buttonModel.setArmed( true );
 308  0
                 buttonModel.setPressed( true );
 309  
             }
 310  
         }
 311  
 
 312  
         // enabled aspect
 313  0
         displayGroup = displayGroupForAspect( EnabledAspect );
 314  0
         if ( displayGroup != null )
 315  
         {
 316  0
             key = displayGroupKeyForAspect( EnabledAspect );
 317  0
             Object value =
 318  0
                 displayGroup.selectedObjectValueForKey( key );
 319  0
             Boolean converted = null;
 320  0
             if ( value != null ) 
 321  
             {
 322  0
                 converted = (Boolean)
 323  0
                     ValueConverter.convertObjectToClass(
 324  0
                         value, Boolean.class );
 325  
             }
 326  0
             if ( converted == null ) converted = Boolean.FALSE;
 327  0
             if ( converted.booleanValue() !=
 328  0
                 buttonModel.isEnabled() )
 329  
             {
 330  0
                 buttonModel.removeChangeListener( this );
 331  0
                 buttonModel.setEnabled(
 332  0
                     converted.booleanValue() );
 333  0
                 buttonModel.addChangeListener( this );
 334  
             }
 335  
         }
 336  
 
 337  
         // visible aspect
 338  0
         displayGroup = displayGroupForAspect( VisibleAspect );
 339  0
         if ( displayGroup != null )
 340  
         {
 341  0
             key = displayGroupKeyForAspect( VisibleAspect );
 342  0
             Object value =
 343  0
                 displayGroup.selectedObjectValueForKey( key );
 344  0
             Boolean converted = (Boolean)
 345  0
                 ValueConverter.convertObjectToClass(
 346  0
                     value, Boolean.class );
 347  0
             if ( converted != null )
 348  
             {
 349  0
                 if ( converted.booleanValue() !=
 350  0
                     ((Component)component).isVisible() )
 351  
                 {
 352  0
                     ((Component)component).setVisible(
 353  0
                         converted.booleanValue() );
 354  
                 }
 355  
             }
 356  
         }
 357  0
     }
 358  
 
 359  
     /**
 360  
     * Writes the value currently in the component
 361  
     * to the selected object in the display group
 362  
     * bound to the value aspect.
 363  
     * @return false if there were problems validating,
 364  
     * or true to continue.
 365  
     */
 366  
     protected boolean writeValueToDisplayGroup()
 367  
     {
 368  0
         EODisplayGroup displayGroup =
 369  0
             displayGroupForAspect( ValueAspect );
 370  0
         if ( displayGroup != null )
 371  
         {
 372  0
             boolean returnValue = true;
 373  0
             String key = displayGroupKeyForAspect( ValueAspect );
 374  0
             Object value = new Boolean( buttonModel.isSelected() );
 375  
 
 376  0
             Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
 377  0
             while ( selectedIterator.hasNext() )
 378  
             {
 379  0
                 int index = ( (Integer)selectedIterator.next() ).intValue();
 380  
 
 381  0
                 if ( !displayGroup.setValueForObjectAtIndex( value, index, key ) )
 382  
                 {
 383  0
                     returnValue = false;
 384  
                 }
 385  0
             }
 386  0
             return returnValue;
 387  
 
 388  
         }
 389  0
         return false;
 390  
     }
 391  
     // interface ChangeListener
 392  
 
 393  
     public void stateChanged(ChangeEvent e)
 394  
     {
 395  0
         if ( buttonModel.isSelected() != lastKnownValue )
 396  
         {
 397  0
             lastKnownValue = buttonModel.isSelected();
 398  0
             writeValueToDisplayGroup();
 399  
         }
 400  0
     }
 401  
 
 402  
 }
 403  
 
 404  
 /*
 405  
  * $Log$
 406  
  * Revision 1.2  2006/02/18 23:19:05  cgruber
 407  
  * Update imports and maven dependencies.
 408  
  *
 409  
  * Revision 1.1  2006/02/16 13:22:22  cgruber
 410  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 411  
  *
 412  
  * Revision 1.8  2004/01/28 18:34:57  mpowers
 413  
  * Better handling for enabling.
 414  
  * Now respecting enabledToSetSelectedObjectValueForKey from display group.
 415  
  *
 416  
  * Revision 1.7  2003/08/06 23:07:52  chochos
 417  
  * general code cleanup (mostly, removing unused imports)
 418  
  *
 419  
  * Revision 1.6  2001/07/30 16:32:55  mpowers
 420  
  * Implemented support for bulk-editing.  Detail associations will now
 421  
  * apply changes to all selected objects.
 422  
  *
 423  
  * Revision 1.5  2001/06/29 22:28:19  mpowers
 424  
  * Tabs to spaces.
 425  
  *
 426  
  * Revision 1.4  2001/06/29 22:17:31  mpowers
 427  
  * Now updating the component on establishConnection.
 428  
  *
 429  
  * Revision 1.3  2001/02/27 02:10:38  mpowers
 430  
  * No longer updating values to the display group if the value
 431  
  * has not changed.
 432  
  *
 433  
  * Revision 1.2  2001/02/21 20:33:01  mpowers
 434  
  * Fixed bug with change listener.
 435  
  *
 436  
  * Revision 1.1.1.1  2000/12/21 15:48:38  mpowers
 437  
  * Contributing wotonomy.
 438  
  *
 439  
  * Revision 1.3  2000/12/20 16:25:40  michael
 440  
  * Added log to all files.
 441  
  *
 442  
  *
 443  
  */
 444