Coverage Report - net.wotonomy.ui.GenericAssociation
 
Classes in this File Line Coverage Branch Coverage Complexity
GenericAssociation
0% 
0% 
1.824
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2001 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;
 20  
 
 21  
 import java.util.HashSet;
 22  
 import java.util.Iterator;
 23  
 import java.util.Set;
 24  
 
 25  
 import net.wotonomy.control.EOKeyValueCoding;
 26  
 import net.wotonomy.control.EOKeyValueCodingSupport;
 27  
 import net.wotonomy.control.EOObserverCenter;
 28  
 import net.wotonomy.foundation.NSArray;
 29  
 
 30  
 /**
 31  
 * GenericAssociation binds one or more properties on an 
 32  
 * observable object to a display group.  The controlled
 33  
 * object is expected to use the ObserverCenter and will
 34  
 * be observed by this association. <br><br>
 35  
 *
 36  
 * Bindings for this association are <i>generic</i>: the 
 37  
 * name of the aspect will be treated as a property key 
 38  
 * on the displayed object(s) and synchronized with the 
 39  
 * bound property on the controlled object. <br><br>
 40  
 *
 41  
 * NOTE: because we cannot assume that the controlled
 42  
 * object will retain a reference to this association,
 43  
 * you must explicitly retain a reference to prevent
 44  
 * the association from getting garbage collected.
 45  
 *
 46  
 * @author michael@mpowers.net
 47  
 * @author $Author: cgruber $
 48  
 * @version $Revision: 904 $
 49  
 */
 50  0
 public class GenericAssociation extends EOAssociation
 51  
 {
 52  
     protected boolean objectModified;
 53  
     protected Set aspectsModified;
 54  
     
 55  0
     static final NSArray aspects = 
 56  0
         new NSArray( new Object[] {
 57  
         } );
 58  0
     static final NSArray aspectSignatures = 
 59  0
         new NSArray( new Object[] {
 60  
         } );
 61  0
     static final NSArray objectKeysTaken = 
 62  0
         new NSArray( new Object[] {
 63  
         } );
 64  
                 
 65  
     /**
 66  
     * Constructor specifying the object to be controlled by this
 67  
     * association.  Does not establish connection.
 68  
     */
 69  
     public GenericAssociation ( Object anObject )
 70  
     {
 71  0
         super( anObject );
 72  0
         objectModified = false;
 73  0
         aspectsModified = new HashSet();
 74  0
     }
 75  
     
 76  
     /**
 77  
     * Returns a List of aspect signatures whose contents
 78  
     * correspond with the aspects list.  Each element is 
 79  
     * a string whose characters represent a capability of
 80  
     * the corresponding aspect. <ul>
 81  
     * <li>"A" attribute: the aspect can be bound to
 82  
     * an attribute.</li>
 83  
     * <li>"1" to-one: the aspect can be bound to a
 84  
     * property that returns a single object.</li>
 85  
     * <li>"M" to-one: the aspect can be bound to a
 86  
     * property that returns multiple objects.</li>
 87  
     * </ul> 
 88  
     * An empty signature "" means that the aspect can
 89  
     * bind without needing a key.
 90  
     * This implementation returns "A1M" for each
 91  
     * element in the aspects array.
 92  
     */
 93  
     public static NSArray aspectSignatures ()
 94  
     {
 95  0
         return aspectSignatures;
 96  
     }
 97  
     
 98  
     /**
 99  
     * Returns a List that describes the aspects supported
 100  
     * by this class.  Each element in the list is the string
 101  
     * name of the aspect.  This implementation returns an
 102  
     * empty list.
 103  
     */
 104  
     public static NSArray aspects ()
 105  
     {
 106  0
         return aspects;
 107  
     }
 108  
     
 109  
     /**
 110  
     * Returns a List of EOAssociation subclasses that,
 111  
     * for the objects that are usable for this association,
 112  
     * are less suitable than this association.
 113  
     */
 114  
     public static NSArray associationClassesSuperseded ()
 115  
     {
 116  0
         return new NSArray();
 117  
     }
 118  
     
 119  
     /**
 120  
     * Returns whether this class can control the specified 
 121  
     * object. 
 122  
     */
 123  
     public static boolean isUsableWithObject ( Object anObject )
 124  
     {
 125  0
         return true;
 126  
     }
 127  
     
 128  
     /**
 129  
     * Returns a List of properties of the controlled object
 130  
     * that are controlled by this class.  For example,
 131  
     * "stringValue", or "selected".
 132  
     */
 133  
     public static NSArray objectKeysTaken ()
 134  
     {
 135  0
         return objectKeysTaken;
 136  
     }
 137  
     
 138  
     /**
 139  
     * Returns the aspect that is considered primary
 140  
     * or default.  This is typically "value" or somesuch.
 141  
     */
 142  
     public static String primaryAspect ()
 143  
     {
 144  0
         return "";
 145  
     }
 146  
         
 147  
     /**
 148  
     * Returns whether this association can bind to the
 149  
     * specified display group on the specified key for
 150  
     * the specified aspect.  
 151  
     */
 152  
     public boolean canBindAspect ( 
 153  
         String anAspect, EODisplayGroup aDisplayGroup, String aKey)
 154  
     {
 155  0
         return true;
 156  
     }
 157  
     
 158  
     /**
 159  
     * Establishes a connection between this association
 160  
     * and the controlled object.  This implementation 
 161  
         * registers with ObserverCenter for change notifications
 162  
     * from the controlled object.
 163  
     */
 164  
     public void establishConnection ()
 165  
     {
 166  0
                 EOObserverCenter.addObserver( this, object() );
 167  0
         super.establishConnection();
 168  
                 
 169  
                 // forces update from bindings
 170  0
                 subjectChanged();
 171  0
     }
 172  
         
 173  
     /**
 174  
     * Breaks the connection between this association and 
 175  
     * its object.  Override to stop listening for events
 176  
     * from the object.
 177  
     */
 178  
     public void breakConnection ()
 179  
     {
 180  0
                 EOObserverCenter.removeObserver( this, object() );
 181  0
         super.breakConnection();
 182  0
     }
 183  
     
 184  
     /**
 185  
     * Overridden to track which observed object is changing.
 186  
     */
 187  
     public void objectWillChange( Object anObject )
 188  
     {
 189  0
         if ( object() == anObject ) 
 190  
         { 
 191  0
             objectModified = true;
 192  0
         }
 193  
         else
 194  
         {
 195  0
             aspectsModified.add( aspectToGroup.allKeysForObject( anObject ) );
 196  
         }
 197  0
     }
 198  
 
 199  
     /**
 200  
     * Called when either the selection or the contents 
 201  
     * of an associated display group have changed.
 202  
     */
 203  
     public void subjectChanged ()
 204  
     {
 205  
         String aspect;
 206  
         String key;
 207  
         Object value;
 208  
         EODisplayGroup displayGroup;
 209  
         Iterator iterator;
 210  
         
 211  0
         iterator = aspectsModified.iterator();
 212  0
         while ( iterator.hasNext() )
 213  
         {
 214  0
             aspect = (String) iterator.next();
 215  0
             key = displayGroupKeyForAspect( aspect );
 216  0
             displayGroup = displayGroupForAspect( aspect );
 217  
 
 218  0
             value = readValueFromDisplayGroupForKey( displayGroup, key );    
 219  0
             writeValueForKey( object(), value, key );
 220  0
         }
 221  0
         aspectsModified.clear();
 222  
         
 223  0
         if ( objectModified )
 224  
         {
 225  0
             iterator = aspectToGroup.keySet().iterator();
 226  0
             while ( iterator.hasNext() )
 227  
             {
 228  0
                 aspect = (String) iterator.next();
 229  0
                 key = displayGroupKeyForAspect( aspect );
 230  0
                 value = readValueForKey( object(), key );
 231  0
                 displayGroup = displayGroupForAspect( aspect );
 232  0
                 displayGroup.setSelectedObjectValue( value, key );
 233  0
             }
 234  
         }
 235  0
     }
 236  
     
 237  
     protected Object readValueForKey( Object object, String key )
 238  
     {
 239  0
         if ( object instanceof EOKeyValueCoding )
 240  
         {
 241  0
             return ((EOKeyValueCoding)object).valueForKey( key );
 242  
         }
 243  0
         return EOKeyValueCodingSupport.valueForKey( object, key );
 244  
     }
 245  
     
 246  
     protected void writeValueForKey( Object object, Object value, String key )
 247  
     {
 248  0
         if ( object instanceof EOKeyValueCoding )
 249  
         {
 250  0
             ((EOKeyValueCoding)object).takeValueForKey( value, key );
 251  0
         }
 252  
         else
 253  
         {
 254  0
             EOKeyValueCodingSupport.takeValueForKey( object, value, key );
 255  
         }
 256  0
     }
 257  
     
 258  
     protected Object readValueFromDisplayGroupForKey( 
 259  
         EODisplayGroup displayGroup, String key )
 260  
     {
 261  
         Object value;
 262  
         
 263  0
         if ( displayGroup.selectedObjects().size() > 1 )
 264  
         {
 265  
             // if there're more than one object selected, set
 266  
             // the value to blank for all of them.
 267  
             Object previousValue;
 268  
 
 269  0
             Iterator indexIterator = displayGroup.selectionIndexes().
 270  0
                                       iterator();
 271  
 
 272  
             // get value for the first selected object.
 273  0
             int initialIndex = ( (Integer)indexIterator.next() ).intValue();
 274  0
             previousValue = displayGroup.valueForObjectAtIndex(
 275  0
                                       initialIndex, key );
 276  0
             value = null;
 277  
 
 278  
             // go through the rest of the selected objects, compare each
 279  
             // value with the previous one. continue comparing if two
 280  
             // values are equal, break the while loop if they're different.
 281  
             // the final value will be the common value of all selected objects
 282  
             // if there is one, or be blank if there is not.
 283  0
             while ( indexIterator.hasNext() )
 284  
             {
 285  0
                 int index = ( (Integer)indexIterator.next() ).intValue();
 286  0
                 Object currentValue = displayGroup.valueForObjectAtIndex(
 287  0
                                       index, key );
 288  0
                 if ( currentValue != null && !currentValue.equals( previousValue ) )
 289  
                 {
 290  0
                     value = null;
 291  0
                     break;
 292  
                 }
 293  
                 else
 294  
                 {
 295  
                     // currentValue is the same as the previous one
 296  0
                     value = currentValue;
 297  
                 }
 298  
 
 299  0
             } // end while
 300  
 
 301  0
         } else {
 302  
 
 303  0
             value = displayGroup.selectedObjectValueForKey( key );
 304  
         } // end checking size of displayGroup
 305  
         
 306  0
         return value;
 307  
     }
 308  
         
 309  
         /**
 310  
         * Writes the value currently in the component
 311  
         * to the selected object in the display group
 312  
         * bound to the value aspect.
 313  
     * @return false if there were problems validating,
 314  
     * or true to continue.
 315  
         */
 316  
         protected boolean writeValueForAspect( Object value, String aspect )
 317  
         {
 318  0
                 EODisplayGroup displayGroup = 
 319  0
                         displayGroupForAspect( aspect );
 320  0
                 if ( displayGroup != null )
 321  
                 {
 322  0
                         String key = displayGroupKeyForAspect( aspect );
 323  
 
 324  0
             boolean returnValue = true;
 325  0
             Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
 326  0
             while ( selectedIterator.hasNext() )
 327  
             {
 328  0
                 int index = ( (Integer)selectedIterator.next() ).intValue();
 329  
 
 330  0
                 if ( !displayGroup.setValueForObjectAtIndex( value, index, key ) )
 331  
                 {
 332  0
                     returnValue = false;
 333  
                 }
 334  0
             }
 335  0
             return returnValue;
 336  
                 }
 337  0
                 return false;
 338  
         }
 339  
 
 340  
     /**
 341  
     * Forces this association to cause the object to 
 342  
     * stop editing and validate the user's input.
 343  
     * @return false if there were problems validating,
 344  
     * or true to continue.
 345  
     */
 346  
     public boolean endEditing ()
 347  
     {        
 348  0
         return false;
 349  
 //!            return writeValueToDisplayGroup();
 350  
     }
 351  
         
 352  
 }
 353  
 
 354  
 /*
 355  
  * $Log$
 356  
  * Revision 1.2  2006/02/18 23:14:35  cgruber
 357  
  * Update imports and maven dependencies.
 358  
  *
 359  
  * Revision 1.1  2006/02/16 13:22:22  cgruber
 360  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 361  
  *
 362  
  * Revision 1.3  2003/08/06 23:07:52  chochos
 363  
  * general code cleanup (mostly, removing unused imports)
 364  
  *
 365  
  * Revision 1.2  2001/11/08 19:51:24  mpowers
 366  
  * Draft implementation.
 367  
  *
 368  
  * Revision 1.1  2001/11/02 23:15:04  mpowers
 369  
  * Contributing "generic association".
 370  
  *
 371  
  *
 372  
  */
 373