Coverage Report - net.wotonomy.ui.EODisplayGroup
 
Classes in this File Line Coverage Branch Coverage Complexity
EODisplayGroup
0% 
0% 
2.22
 
 1  0
 /*
 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;
 20  
 
 21  
 import java.lang.ref.Reference;
 22  
 import java.lang.ref.WeakReference;
 23  
 import java.util.Collection;
 24  
 import java.util.Enumeration;
 25  
 import java.util.Iterator;
 26  
 import java.util.LinkedList;
 27  
 import java.util.List;
 28  
 import java.util.Map;
 29  
 import java.util.Observable;
 30  
 
 31  
 import javax.swing.JOptionPane;
 32  
 
 33  
 import net.wotonomy.control.EODataSource;
 34  
 import net.wotonomy.control.EODelayedObserver;
 35  
 import net.wotonomy.control.EOEditingContext;
 36  
 import net.wotonomy.control.EOKeyValueCoding;
 37  
 import net.wotonomy.control.EOKeyValueCodingSupport;
 38  
 import net.wotonomy.control.EOObjectStore;
 39  
 import net.wotonomy.control.EOObserverCenter;
 40  
 import net.wotonomy.control.EOObserving;
 41  
 import net.wotonomy.control.EOQualifier;
 42  
 import net.wotonomy.control.EOSortOrdering;
 43  
 import net.wotonomy.control.OrderedDataSource;
 44  
 import net.wotonomy.foundation.NSArray;
 45  
 import net.wotonomy.foundation.NSDictionary;
 46  
 import net.wotonomy.foundation.NSMutableArray;
 47  
 import net.wotonomy.foundation.NSNotification;
 48  
 import net.wotonomy.foundation.NSNotificationCenter;
 49  
 import net.wotonomy.foundation.NSSelector;
 50  
 import net.wotonomy.foundation.internal.Duplicator;
 51  
 import net.wotonomy.foundation.internal.WotonomyException;
 52  
 
 53  
 /**
 54  
 * EODisplayGroup provides an abstraction of a user interface,
 55  
 * comprising of an ordered collection of data objects, some
 56  
 * of which are displayed, and of those some are selected.
 57  
 *
 58  
 * @author michael@mpowers.net
 59  
 * @author $Author: cgruber $
 60  
 * @version $Revision: 904 $
 61  
 */
 62  0
 public class EODisplayGroup extends Observable
 63  
                          implements EOObserving, EOEditingContext.Editor
 64  
 {
 65  
     /**
 66  
     * Notification sent when the display group is about to fetch.
 67  
     */ 
 68  
     public static final String DisplayGroupWillFetchNotification
 69  
         = "DisplayGroupWillFetchNotification";
 70  
     
 71  
     private static boolean
 72  0
         globalDefaultForValidatesChangesImmediately = true;
 73  
     private static String
 74  0
         globalDefaultStringMatchFormat = "caseInsensitiveLike";
 75  
     private static String
 76  0
         globalDefaultStringMatchOperator = "%@*";
 77  
 
 78  
     protected NSMutableArray allObjects;
 79  
     protected NSArray allObjectsProxy;
 80  
     protected NSMutableArray displayedObjects;
 81  
     protected NSArray displayedObjectsProxy;
 82  
     protected NSMutableArray selectedObjects;
 83  
     protected NSArray selectedObjectsProxy;
 84  
     protected NSMutableArray selectedIndexes;
 85  
 
 86  
     private String defaultStringMatchOperator;
 87  
     private String defaultStringMatchFormat;
 88  
 
 89  
     private boolean validatesChangesImmediately;
 90  
     private Object delegate;
 91  
     private EODataSource dataSource;
 92  
     private EOAssociation editingAssociation;
 93  
     private EOQualifier qualifier;
 94  
     private NSMutableArray sortOrderings;
 95  
     private NSArray sortOrderingsProxy;
 96  
 
 97  
     private NSArray localKeys;
 98  
     private NSDictionary insertedObjectDefaultValues;
 99  
     private boolean fetchesOnLoad;
 100  
     private boolean selectsFirstObjectAfterFetch;
 101  
     private boolean usesOptimisticRefresh;
 102  
     private boolean inQueryMode;
 103  
 
 104  
     // change detection: package access for helper classes
 105  
     boolean contentsChanged;
 106  
     boolean selectionChanged;
 107  
     int updatedObjectIndex;
 108  
 
 109  
     // this property is not in the spec
 110  0
     private boolean compareByReference = false;
 111  
 
 112  
     private EOObserving lastGroupObserver;
 113  
     
 114  
     /**
 115  
     * Creates a new display group.
 116  
     */
 117  0
     public EODisplayGroup ()
 118  0
     {
 119  0
         validatesChangesImmediately =
 120  0
             globalDefaultForValidatesChangesImmediately();
 121  0
         defaultStringMatchOperator =
 122  0
             globalDefaultStringMatchFormat();
 123  0
         defaultStringMatchFormat =
 124  0
             globalDefaultStringMatchOperator();
 125  
 
 126  0
         allObjects = new ObservableArray( this );
 127  0
         allObjectsProxy = NSArray.arrayBackedByList( allObjects );
 128  0
         displayedObjects = new NSMutableArray();
 129  0
         displayedObjectsProxy = NSArray.arrayBackedByList( displayedObjects );
 130  0
         selectedObjects = new NSMutableArray();
 131  0
         selectedObjectsProxy = NSArray.arrayBackedByList( selectedObjects );
 132  0
         sortOrderings = new NSMutableArray();
 133  0
         sortOrderingsProxy = NSArray.arrayBackedByList( sortOrderings );
 134  0
         selectedIndexes = new NSMutableArray();
 135  
 
 136  0
         delegate = null;
 137  0
         dataSource = null;
 138  0
         editingAssociation = null;
 139  0
         qualifier = null;
 140  
 
 141  0
         localKeys = new NSArray(); // not implemented
 142  0
         insertedObjectDefaultValues = new NSDictionary();
 143  0
         fetchesOnLoad = false; // not implemented
 144  0
         selectsFirstObjectAfterFetch = false;
 145  0
         usesOptimisticRefresh = false;
 146  0
         inQueryMode = false; // not implemented
 147  
 
 148  0
         contentsChanged = false;
 149  0
         selectionChanged = false;
 150  0
         updatedObjectIndex = -1;
 151  
 
 152  
         // create our private delayed observer
 153  0
         lastGroupObserver = new LastGroupObserver( this );
 154  0
         EOObserverCenter.addObserver( lastGroupObserver, this );
 155  0
     }
 156  
 
 157  
 
 158  
 
 159  
     // specify optional data source
 160  
 
 161  
     /**
 162  
     * Sets the data source that will be used by
 163  
     * this display group.
 164  
     */
 165  
     public void setDataSource ( EODataSource aDataSource )
 166  
     {
 167  0
         if ( ( dataSource != null )
 168  0
         &&   ( dataSource.editingContext() != null ) )
 169  
         {
 170  
             // un-register for notifications from existing parent store
 171  0
             NSNotificationCenter.defaultCenter().removeObserver(
 172  0
                 this, null, dataSource.editingContext() );
 173  0
             dataSource.editingContext().removeEditor( this );
 174  0
             if ( dataSource.editingContext().messageHandler() == this )
 175  
             {
 176  0
                 dataSource.editingContext().setMessageHandler( null );
 177  
             }
 178  
             
 179  
         }
 180  
 
 181  0
         dataSource = aDataSource;
 182  
 
 183  0
         if ( ( dataSource != null )
 184  0
         &&   ( dataSource.editingContext() != null ) )
 185  
         {
 186  
             // register for notifications from parent store
 187  0
             NSNotificationCenter.defaultCenter().addObserver(
 188  0
                 this, new NSSelector( "objectsInvalidatedInEditingContext",
 189  0
                     new Class[] { NSNotification.class } ),
 190  0
                 null, dataSource.editingContext() );
 191  
             
 192  
             // add ourselves as editor
 193  0
             dataSource.editingContext().addEditor( this );
 194  
             
 195  
             // add ourselves as message handler if no such handler exists
 196  0
             if ( dataSource.editingContext().messageHandler() == null )
 197  
             {
 198  0
                 dataSource.editingContext().setMessageHandler( this );
 199  
             }
 200  
         }
 201  0
     }
 202  
     
 203  
     /**
 204  
     * Returns the current data source backing this display group,
 205  
     * or null if no dataSource is currently used.
 206  
     */
 207  
     public EODataSource dataSource ()
 208  
     {
 209  0
         return dataSource;
 210  
     }
 211  
 
 212  
 
 213  
 
 214  
     // specify optional delegate
 215  
 
 216  
     /**
 217  
     * Sets the display group delegate that
 218  
     * will be used by this display group.
 219  
     */
 220  
     public void setDelegate ( Object aDelegate )
 221  
     {
 222  0
         delegate = aDelegate;
 223  0
     }
 224  
 
 225  
    /**
 226  
     * Returns the current delegate for this display group,
 227  
     * or null if no delegate is currently set.
 228  
     */
 229  
     public Object delegate ()
 230  
     {
 231  0
         return delegate;
 232  
     }
 233  
 
 234  
 
 235  
 
 236  
     // display group configuration
 237  
 
 238  
     /**
 239  
     * Returns the current string matching format.
 240  
     * If not set, defaults to "%@*".
 241  
     */
 242  
     public String defaultStringMatchFormat ()
 243  
     {
 244  0
         return defaultStringMatchFormat;
 245  
     }
 246  
 
 247  
     /**
 248  
     * Returns the current string matching operator.
 249  
     * If not set, defaults to "caseInsensitiveLike".
 250  
     */
 251  
     public String defaultStringMatchOperator ()
 252  
     {
 253  0
         return defaultStringMatchOperator;
 254  
     }
 255  
 
 256  
     /**
 257  
     * Sets the display group and associations to edit a
 258  
     * "query by example" query object.  This method is
 259  
     * used for target/action connections.
 260  
     */
 261  
     public void enterQueryMode ( Object aSender )
 262  
     {
 263  0
         throw new RuntimeException( "Not implemented yet." );
 264  
     }
 265  
 
 266  
     /**
 267  
     * Returns whether this display group should immediate
 268  
     * fetch when loaded.
 269  
     */
 270  
     public boolean fetchesOnLoad ()
 271  
     {
 272  0
         return fetchesOnLoad;
 273  
     }
 274  
 
 275  
     /**
 276  
     * Returns whether this display group is in "query by
 277  
     * example" mode.
 278  
     */
 279  
     public boolean inQueryMode ()
 280  
     {
 281  0
         return inQueryMode;
 282  
     }
 283  
 
 284  
     /**
 285  
     * Returns a Map of default values that are applied
 286  
     * to new objects that are inserted into the list.
 287  
     */
 288  
     public NSDictionary insertedObjectDefaultValues ()
 289  
     {
 290  0
         return insertedObjectDefaultValues;
 291  
     }
 292  
 
 293  
     /**
 294  
     * Returns the keys that were declared when read from
 295  
     * an external resource file.
 296  
     */
 297  
     public NSArray localKeys ()
 298  
     {
 299  0
         return localKeys;
 300  
     }
 301  
 
 302  
     /**
 303  
     * Sets whether this display group will select the
 304  
     * first object in the list after a fetch.
 305  
     */
 306  
     public boolean selectsFirstObjectAfterFetch ()
 307  
     {
 308  0
         return selectsFirstObjectAfterFetch;
 309  
     }
 310  
 
 311  
     /**
 312  
     * Sets the default string matching format that
 313  
     * will be used by this display group.
 314  
     */
 315  
     public void setDefaultStringMatchFormat ( String aFormat )
 316  
     {
 317  0
         throw new RuntimeException( "Not implemented yet." );
 318  
     }
 319  
 
 320  
     /**
 321  
     * Sets the default string matching operator that
 322  
     * will be used by this display group.
 323  
     */
 324  
     public void setDefaultStringMatchOperator ( String anOperator )
 325  
     {
 326  0
         throw new RuntimeException( "Not implemented yet." );
 327  
     }
 328  
 
 329  
     /**
 330  
     * Sets whether this display group will fetch objects
 331  
     * from its data source on load.
 332  
     */
 333  
     public void setFetchesOnLoad ( boolean willFetch )
 334  
     {
 335  0
         fetchesOnLoad = willFetch;
 336  0
     }
 337  
 
 338  
     /**
 339  
     * Sets whether this display group is in "query by example"
 340  
     * mode.  If true, all associations will bind to a special
 341  
     * "example" object.
 342  
     */
 343  
     public void setInQueryMode ( boolean isInQueryMode )
 344  
     {
 345  0
         inQueryMode = isInQueryMode;
 346  0
     }
 347  
 
 348  
     /**
 349  
     * Sets the mapping that contains the values that will
 350  
     * be applied to new objects inserted into the display group.
 351  
     */
 352  
     public void setInsertedObjectDefaultValues ( Map aMap )
 353  
     {
 354  0
         insertedObjectDefaultValues = new NSDictionary( aMap );
 355  0
     }
 356  
 
 357  
     /**
 358  
     * Sets the keys that are declared when instantiated from
 359  
     * an external resource file.
 360  
     */
 361  
     public void setLocalKeys ( List aKeyList )
 362  
     {
 363  0
         localKeys = new NSArray( (Collection) aKeyList );
 364  0
     }
 365  
 
 366  
     /**
 367  
     * Sets whether the first object in the list will be
 368  
     * selected after a fetch.
 369  
     */
 370  
     public void setSelectsFirstObjectAfterFetch (
 371  
         boolean selectsFirst )
 372  
     {
 373  0
         selectsFirstObjectAfterFetch = selectsFirst;
 374  0
     }
 375  
 
 376  
     /**
 377  
     * Sets the order of the keys by which this display group
 378  
     * will be ordered after a fetch or after a call to
 379  
     * updateDisplayedObjects().  The elements in the display
 380  
     * group will be sorted first by the first key, within
 381  
     * the first key, by the second key, and so on.
 382  
     */
 383  
     public void setSortOrderings ( List aList )
 384  
     {
 385  0
         sortOrderings.removeAllObjects();
 386  
 
 387  
         Object o;
 388  0
         Iterator it = aList.iterator();
 389  0
         while ( it.hasNext() )
 390  
         {
 391  0
             o = it.next();
 392  
             // handle the convenience of specifying just a key
 393  0
             if ( ! ( o instanceof EOSortOrdering ) )
 394  
             {
 395  0
                 o = new EOSortOrdering(
 396  0
                     o.toString(), EOSortOrdering.CompareAscending );
 397  
             }
 398  0
             sortOrderings.add( o );
 399  0
         }
 400  0
     }
 401  
 
 402  
     /**
 403  
     * Sets whether only changed objects are refreshed (optimistic),
 404  
     * or whether all objects are refreshed (pessimistic, default).
 405  
     * By default, when the display group receives notification that
 406  
     * one of its objects has changed, updateDisplayedObjects is called.
 407  
     */
 408  
     public void setUsesOptimisticRefresh ( boolean isOptimistic )
 409  
     {
 410  0
         usesOptimisticRefresh = isOptimistic;
 411  0
     }
 412  
 
 413  
     /**
 414  
     * Sets whether changes made by associations are validated
 415  
     * immediately, or when changes are saved.
 416  
     */
 417  
     public void setValidatesChangesImmediately (
 418  
         boolean validatesImmediately )
 419  
     {
 420  0
         validatesChangesImmediately = validatesImmediately;
 421  0
     }
 422  
 
 423  
     /**
 424  
     * Returns a read-only List of sort orderings for this display group.
 425  
     */
 426  
     public NSArray sortOrderings ()
 427  
     {
 428  0
         return sortOrderingsProxy;
 429  
     }
 430  
 
 431  
     /**
 432  
     * Returns whether this display group refreshes only
 433  
     * the changed objects or all objects on refresh.
 434  
     */
 435  
     public boolean usesOptimisticRefresh ()
 436  
     {
 437  0
         return usesOptimisticRefresh;
 438  
     }
 439  
 
 440  
     /**
 441  
     * Returns whether this display group validates changes
 442  
     * immediately.  Otherwise, validation should occur when
 443  
     * changes are saved.  Default is the global default,
 444  
     * which is initially true.
 445  
     */
 446  
     public boolean validatesChangesImmediately ()
 447  
     {
 448  0
         return validatesChangesImmediately;
 449  
     }
 450  
 
 451  
 
 452  
     // qualification
 453  
 
 454  
     /**
 455  
     * Returns a qualifier that will be applied all the objects
 456  
     * in this display group to determine which objects will
 457  
     * be displayed.
 458  
     */
 459  
     public EOQualifier qualifier ()
 460  
     {
 461  0
         return qualifier;
 462  
     }
 463  
 
 464  
     /**
 465  
     * Returns a new qualifier built from the three query
 466  
     * value maps: greater than, equal to, and less than.
 467  
     */
 468  
     public EOQualifier qualifierFromQueryValues ()
 469  
     {
 470  
         //TODO: assemble qualifier from query values
 471  
 
 472  0
         return new EOQualifier()
 473  
         {
 474  
             // use inner class until we actually implement one
 475  
             public EOQualifier qualifierWithBindings(
 476  
                 Map aMap,
 477  
                 boolean requireAll )
 478  
             {
 479  0
                 return null;
 480  
             }
 481  
             public Throwable
 482  
             validateKeysWithRootClassDescription( Class aClass )
 483  
             {
 484  0
                 return null;
 485  
             }
 486  0
                         public boolean evaluateWithObject(Object o)
 487  
                         {
 488  0
                           return false;
 489  
                         }
 490  
         };
 491  
     }
 492  
 
 493  
     /**
 494  
     * Calls qualifierFromQueryValues(), applies the result
 495  
     * to the data source, and calls fetch().
 496  
     */
 497  
     public void qualifyDataSource ()
 498  
     {
 499  0
         throw new RuntimeException( "Not implemented yet." );
 500  
     }
 501  
 
 502  
     /**
 503  
     * Calls qualifierFromQueryValues(), sets the qualifier
 504  
     * with setQualifier(), and calls updateDisplayedObjects().
 505  
     */
 506  
     public void qualifyDisplayGroup ()
 507  
     {
 508  0
         setQualifier( qualifierFromQueryValues() );
 509  0
         updateDisplayedObjects();
 510  0
     }
 511  
 
 512  
     /**
 513  
     * Returns a Map containing the mappings of keys
 514  
     * to binding query values.
 515  
     */
 516  
     public NSDictionary queryBindingValues ()
 517  
     {
 518  0
         throw new RuntimeException( "Not implemented yet." );
 519  
     }
 520  
 
 521  
     /**
 522  
     * Returns a Map containing the mappings of keys
 523  
     * to operator values.
 524  
     */
 525  
     public NSDictionary queryOperatorValues ()
 526  
     {
 527  0
         throw new RuntimeException( "Not implemented yet." );
 528  
     }
 529  
 
 530  
     /**
 531  
     * Sets the qualifier that will be used by
 532  
     * updateDisplayedObjects() to filter displayed objects.
 533  
     */
 534  
     public void setQualifier ( EOQualifier aQualifier )
 535  
     {
 536  0
         qualifier = aQualifier;
 537  0
     }
 538  
 
 539  
     /**
 540  
     * Sets the mapping that contains the mappings of keys
 541  
     * to binding values.
 542  
     */
 543  
     public void setQueryBindingValues ( Map aMap )
 544  
     {
 545  0
         throw new RuntimeException( "Not implemented yet." );
 546  
     }
 547  
 
 548  
     /**
 549  
     * Sets the mapping that contains the mappings of keys
 550  
     * to operator values.
 551  
     */
 552  
     public void setQueryOperatorValues ( Map aMap )
 553  
     {
 554  0
         throw new RuntimeException( "Not implemented yet." );
 555  
     }
 556  
 
 557  
 
 558  
 
 559  
     // qualifier query values
 560  
 
 561  
     /**
 562  
     * Returns a Map containing the mappings of keys
 563  
     * to query values that will be used to test for equality.
 564  
     */
 565  
     public NSDictionary equalToQueryValues ()
 566  
     {
 567  0
         throw new RuntimeException( "Not implemented yet." );
 568  
     }
 569  
 
 570  
     /**
 571  
     * Returns a Map containing the mappings of keys
 572  
     * to query values that will be used to test for greater value.
 573  
     */
 574  
     public NSDictionary greaterThanQueryValues ()
 575  
     {
 576  0
         throw new RuntimeException( "Not implemented yet." );
 577  
     }
 578  
 
 579  
     /**
 580  
     * Returns a Map containing the mappings of keys
 581  
     * to query values that will be used to test for lesser value.
 582  
     */
 583  
     public NSDictionary lessThanQueryValues ()
 584  
     {
 585  0
         throw new RuntimeException( "Not implemented yet." );
 586  
     }
 587  
 
 588  
     /**
 589  
     * Sets the Map that contains the mappings of keys
 590  
     * to query values that will be used to test for equality.
 591  
     */
 592  
     public void setEqualToQueryValues ( Map aMap )
 593  
     {
 594  0
         throw new RuntimeException( "Not implemented yet." );
 595  
     }
 596  
 
 597  
     /**
 598  
     * Sets the mapping that contains the mappings of keys
 599  
     * to query values that will be used to test for greater value.
 600  
     */
 601  
     public void setGreaterThanQueryValues ( Map aMap )
 602  
     {
 603  0
         throw new RuntimeException( "Not implemented yet." );
 604  
     }
 605  
 
 606  
     /**
 607  
     * Sets the mapping that contains the mappings of keys
 608  
     * to query values that will be used to test for lesser value.
 609  
     */
 610  
     public void setLessThanQueryValues ( Map aMap )
 611  
     {
 612  0
         throw new RuntimeException( "Not implemented yet." );
 613  
     }
 614  
 
 615  
 
 616  
     // interface to associations
 617  
 
 618  
     /**
 619  
     * Called by an association when it begins editing.
 620  
     */
 621  
     public void associationDidBeginEditing ( EOAssociation anAssociation )
 622  
     { // System.out.println( "EODisplayGroup.associationDidBeginEditing: " + anAssociation );        
 623  0
         if ( dataSource != null )
 624  
         {
 625  0
             if ( dataSource.editingContext() != null )
 626  
             {
 627  0
                 dataSource.editingContext().setMessageHandler( this );
 628  
             }
 629  
         }
 630  0
         editingAssociation = anAssociation;
 631  0
     }
 632  
 
 633  
     /**
 634  
     * Called by an association when it is finished editing.
 635  
     */
 636  
     public void associationDidEndEditing ( EOAssociation anAssociation )
 637  
     { // System.out.println( "EODisplayGroup.associationDidEndEditing: " + anAssociation );        
 638  0
         editingAssociation = null;
 639  0
     }
 640  
 
 641  
     /**
 642  
     * Called by associations to determine whether the contents
 643  
     * of any objects have been changed.  Returns true if the
 644  
     * contents have changed and not all observers have been
 645  
     * notified.
 646  
     */
 647  
     public boolean contentsChanged ()
 648  
     {
 649  0
         return contentsChanged;
 650  
     }
 651  
 
 652  
     /**
 653  
     * Called by associations to determine whether the
 654  
     * selection has been changed.  Returns true if the
 655  
     * selection has changed and not all observers have
 656  
     * been notified.
 657  
     */
 658  
     public boolean selectionChanged ()
 659  
     {
 660  0
         return selectionChanged;
 661  
     }
 662  
 
 663  
     /**
 664  
     * Called by an association when a user-specified value fails the association's
 665  
     * validation rules.  This implementation returns true, unless the delegate
 666  
     * prevents this.
 667  
     * @return True to allow the association to handle user notification,
 668  
     * otherwise return false to let the association know that the
 669  
     * display group notified the user.
 670  
     */
 671  
     public boolean associationFailedToValidateValue (
 672  
         EOAssociation anAssociation,
 673  
         String aValue,
 674  
         String aKey,
 675  
         Object anObject,
 676  
         String anErrorDescription )
 677  
     {
 678  0
         Object result = notifyDelegate(
 679  0
             "displayGroupShouldDisplayAlert",
 680  0
             new Class[] { EODisplayGroup.class, String.class, String.class },
 681  0
             new Object[] { this, "Validation Failed", anErrorDescription } );
 682  0
         if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
 683  
         {
 684  0
             return true;
 685  
         }
 686  0
         return false;
 687  
     }
 688  
 
 689  
     /**
 690  
     * Called by an association to determine whether it should enable
 691  
     * a component that displayes a value for the specified key.
 692  
     * @return true if an object is selected, or if
 693  
     * the specified key is a query key.  Otherwise false.
 694  
     */
 695  
     public boolean enabledToSetSelectedObjectValueForKey ( String aKey )
 696  
     {
 697  0
         if ( ( aKey != null ) && ( aKey.startsWith( "@" ) ) ) return true;
 698  0
         return ( selectedObject() != null );
 699  
     }
 700  
 
 701  
     
 702  
     // association management
 703  
 
 704  
     /**
 705  
     * Returns the association that is currently being edited,
 706  
     * or null if no editing is taking place.
 707  
     */
 708  
     public EOAssociation editingAssociation ()
 709  
     {
 710  0
         return editingAssociation;
 711  
     }
 712  
 
 713  
     /**
 714  
     * Asks the association currently editing to stop editing.
 715  
     * @returns true if editing was stopped, false if the
 716  
     * association refused to stop editing (if a modal dialog
 717  
     * is displayed or a value failed to validate).
 718  
     */
 719  
     public boolean endEditing ()
 720  
     {
 721  0
         if ( editingAssociation == null ) return true;
 722  0
         return editingAssociation.endEditing();
 723  
     }
 724  
 
 725  
     /**
 726  
     * Returns a read-only List of associations that are observing
 727  
     * this display group.
 728  
     */
 729  
     public NSArray observingAssociations ()
 730  
     {
 731  0
         NSArray observers =
 732  0
             EOObserverCenter.observersForObject( this );
 733  0
         NSMutableArray result = new NSMutableArray();
 734  
 
 735  
         Object o;
 736  0
         Enumeration e = observers.objectEnumerator();
 737  0
         while ( e.hasMoreElements() )
 738  
         {
 739  0
             o = e.nextElement();
 740  0
             if ( o instanceof EOAssociation )
 741  
             {
 742  0
                 result.addObject( o );
 743  0
             }
 744  
         }
 745  0
         return result;
 746  
     }
 747  
 
 748  
 
 749  
 
 750  
     // object management
 751  
 
 752  
     /**
 753  
     * Returns a read-only List containing all objects managed by the display group.
 754  
     * This includes those objects not visible due to disqualification.
 755  
     */
 756  
     public NSArray allObjects ()
 757  
     { //System.out.println( "avoided allocation: allObjects" );
 758  0
         return allObjectsProxy;
 759  
     }
 760  
 
 761  
     /**
 762  
     * Clears the current selection.
 763  
     * @return True is the selection was cleared,
 764  
     * False if the selection could not be cleared
 765  
     * @see #setSelectionIndexes
 766  
     */
 767  
     public boolean clearSelection ()
 768  
     {
 769  0
         Object result = notifyDelegate(
 770  0
             "displayGroupShouldChangeSelection",
 771  0
             new Class[] { EODisplayGroup.class, List.class },
 772  0
             new Object[] { this, new NSArray( selectedObjects ) } );
 773  0
         if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
 774  
         {
 775  0
             return false;
 776  
         }
 777  
 
 778  0
         selectionChanged = true;
 779  0
         willChange();
 780  
 
 781  0
         selectedObjects.removeAllObjects();
 782  0
         selectedIndexes.removeAllObjects();
 783  
 
 784  0
         notifyDelegate(
 785  0
             "displayGroupDidChangeSelection",
 786  0
             new Class[] { EODisplayGroup.class },
 787  0
             new Object[] { this } );
 788  0
         notifyDelegate(
 789  0
             "displayGroupDidChangeSelectedObjects",
 790  0
             new Class[] { EODisplayGroup.class },
 791  0
             new Object[] { this } );
 792  
 
 793  0
         return true;
 794  
     }
 795  
 
 796  
     /**
 797  
     * Deletes the object at the specified index,
 798  
     * notifying the delegate before and after the operation,
 799  
     * and then updating the selection if needed.
 800  
     * @return True if delete was successful, false if the
 801  
     * object was not deleted.
 802  
     */
 803  
     public boolean deleteObjectAtIndex ( int anIndex )
 804  
     {
 805  0
         Object target = displayedObjects.objectAtIndex( anIndex );
 806  
 
 807  0
         Object result = notifyDelegate(
 808  0
             "displayGroupShouldDeleteObject",
 809  0
             new Class[] { EODisplayGroup.class, Object.class },
 810  0
             new Object[] { this, target } );
 811  0
         if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
 812  
         {
 813  0
             return false;
 814  
         }
 815  
 
 816  0
         contentsChanged = true;
 817  
 
 818  0
         deleteObjectAtIndexNoNotify( anIndex );
 819  
 
 820  0
         if ( dataSource != null )
 821  
         {
 822  0
             dataSource.deleteObject( target );
 823  
         }
 824  
 
 825  0
         notifyDelegate(
 826  0
             "displayGroupDidDeleteObject",
 827  0
             new Class[] { EODisplayGroup.class, Object.class },
 828  0
             new Object[] { this, target } );
 829  
 
 830  0
         return true;
 831  
     }
 832  
 
 833  
     private void deleteObjectAtIndexNoNotify ( int anIndex )
 834  
     {
 835  0
         Object target = displayedObjects.objectAtIndex( anIndex );
 836  
 
 837  
         int i;
 838  
 
 839  
         // remove from selected objects if necessary
 840  0
         i = indexOf( selectedObjects, target );
 841  0
         if ( i != NSArray.NotFound )
 842  
         {
 843  0
             selectionChanged = true;
 844  0
             willChange(); // notify before removing
 845  0
             selectedObjects.removeObjectAtIndex( i );
 846  0
             selectedIndexes.remove( new Integer( i ) ); // comps by value
 847  0
         }
 848  
         else // notify - no selection change needed
 849  
         {
 850  0
             willChange();
 851  
         }
 852  
 
 853  
         // remove from all objects
 854  0
         i = indexOf( allObjects, target );
 855  0
         if ( i != NSArray.NotFound )
 856  
         {
 857  0
             allObjects.removeObjectAtIndex( i );
 858  
         }
 859  
         else // otherwise should never happen
 860  
         {
 861  
 //          throw new WotonomyException(
 862  
 //              "Displayed object not found in allObjects" );
 863  
         }
 864  
 
 865  
         // remove from displayed objects
 866  0
         displayedObjects.removeObjectAtIndex( anIndex );
 867  0
     }
 868  
 
 869  
     /**
 870  
     * Deletes the currently selected objects.
 871  
     * This implementation calls deleteObjectAtIndex() for
 872  
     * each index in the selection list, immediately returning
 873  
     * false if any delete operation fails.
 874  
     * @return True if all selected objects were deleted,
 875  
     * false if any deletion failed.
 876  
     */
 877  
     public boolean deleteSelection ()
 878  
     {
 879  
         int i;
 880  0
         boolean result = true;
 881  
 
 882  0
         Enumeration e = new NSArray( selectedObjects ).objectEnumerator();
 883  0
         while ( e.hasMoreElements() )
 884  
         {
 885  0
             i = indexOf( displayedObjects, e.nextElement() );
 886  0
             if ( i == NSArray.NotFound )
 887  
             {
 888  
                 // should never happen
 889  0
                 throw new WotonomyException(
 890  0
                     "Selected object not found in displayedObjects" );
 891  
             }
 892  0
             result = result && deleteObjectAtIndex( i );
 893  0
         }
 894  
 
 895  0
         return result;
 896  
     }
 897  
 
 898  
     /**
 899  
     * Returns a read-only List of all objects in the display group
 900  
     * that are currently displayed by the associations.
 901  
     */
 902  
     public NSArray displayedObjects ()
 903  
     { // System.out.println( "avoided allocation: displayedObjects" );
 904  0
         return displayedObjectsProxy;
 905  
     }
 906  
 
 907  
     /**
 908  
     * Requests a list of objects from the DataSource
 909  
     * and calls setObjectArray to populate the list.
 910  
     * More specifically, calls endEditing(), asks the
 911  
     * delegate, fetches the objects, notifies the delegate,
 912  
     * and populates the list.
 913  
     */
 914  
     public boolean fetch ()
 915  
     {
 916  0
         endEditing();
 917  
 
 918  0
         if ( dataSource == null )
 919  
         {
 920  0
             return false;
 921  
         }
 922  
 
 923  0
         Object result = notifyDelegate(
 924  0
             "displayGroupShouldFetch",
 925  0
             new Class[] { EODisplayGroup.class },
 926  0
             new Object[] { this } );
 927  0
         if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
 928  
         {
 929  0
             return false;
 930  
         }
 931  
 
 932  0
         NSNotificationCenter.defaultCenter().postNotification(
 933  0
             DisplayGroupWillFetchNotification, this, new NSDictionary() );
 934  
         
 935  0
         NSArray objectList = dataSource.fetchObjects();
 936  
 
 937  0
         notifyDelegate(
 938  0
             "displayGroupDidFetchObjects",
 939  0
             new Class[] { EODisplayGroup.class, List.class },
 940  0
             new Object[] { this, objectList } );
 941  
             
 942  0
         if ( selectsFirstObjectAfterFetch ) 
 943  
         {
 944  
             //note: there's a good chance this logic ought to be in master-detail assoc:
 945  
             // we're doing this because changes in the master object trigger a refetch
 946  
             // on the child display group which annoyingly changes the selection.
 947  0
             NSArray original = new NSArray( allObjects );
 948  0
             setObjectArray( objectList );
 949  0
             if ( displayedObjects.size() > 0
 950  0
                 && !original.equals( allObjects ) ) // don't change if no change
 951  
             {
 952  0
                 setSelectionIndexes( new NSArray( new Integer( 0 ) ) );
 953  
             }
 954  0
         }
 955  
         else
 956  
         {
 957  0
             setObjectArray( objectList );
 958  
         }
 959  
 
 960  0
         return true;
 961  
    }
 962  
 
 963  
     /**
 964  
     * Creates a new object at the specified index.
 965  
     * Calls insertObjectAtIndex() with the result
 966  
     * from sending createObject() to the data source.
 967  
     * Presents a JOptionPane if the create fails, unless
 968  
     * the delegate implements displayGroupCreateObjectFailed.
 969  
     * @return the newly created object.
 970  
     */
 971  
     public Object insertNewObjectAtIndex ( int anIndex )
 972  
     {
 973  0
         Object result = null;
 974  0
         if ( dataSource != null )
 975  
         {
 976  0
             result = dataSource.createObject();
 977  
         }
 978  0
         if ( result != null )
 979  
         {
 980  0
             if ( insertedObjectDefaultValues != null )
 981  
             {
 982  0
                 Duplicator.writePropertiesForObject(
 983  0
                     insertedObjectDefaultValues, result );
 984  
             }
 985  0
             insertObjectAtIndex( result, anIndex );
 986  0
         }
 987  
         else // create failed
 988  
         {
 989  0
             if ( delegate() != null )
 990  
             {
 991  0
                 NSSelector selector = new NSSelector(
 992  0
                     "displayGroupCreateObjectFailed",
 993  0
                     new Class[] { EODisplayGroup.class, EODataSource.class } );
 994  0
                 if ( selector.implementedByObject( delegate() ) )
 995  
                 {
 996  
                     try
 997  
                     {
 998  0
                         selector.invoke( delegate(), new Object[] { this, dataSource } );
 999  0
                         return result;
 1000  
                     }
 1001  0
                     catch ( Exception exc )
 1002  
                     {
 1003  0
                         System.err.println( "Error notifying delegate: displayGroupCreateObjectFailed" );
 1004  0
                         exc.printStackTrace();
 1005  
                     }
 1006  
                 }                    
 1007  
             }
 1008  
             
 1009  
             // no delegate or delegate does not implement displayGroupCreateObjectFailed
 1010  
             
 1011  0
             String message = "Data source could not create new object";
 1012  0
             Object delegateResult = notifyDelegate(
 1013  0
                 "displayGroupShouldDisplayAlert",
 1014  0
                 new Class[] { EODisplayGroup.class, String.class, String.class },
 1015  0
                 new Object[] { this, "Error", message } );
 1016  0
             if ( ( delegateResult == null ) || ( Boolean.TRUE.equals( delegateResult ) ) )
 1017  
             {
 1018  0
                 JOptionPane.showMessageDialog( null, message );
 1019  
             }
 1020  
         }
 1021  0
         return result;
 1022  
     }
 1023  
 
 1024  
     /**
 1025  
     * Inserts the specified object into the list at
 1026  
     * the specified index.
 1027  
     */
 1028  
     public void insertObjectAtIndex ( Object anObject, int anIndex )
 1029  
     {
 1030  0
         Object result = notifyDelegate(
 1031  0
             "displayGroupShouldInsertObject",
 1032  0
             new Class[] { EODisplayGroup.class, Object.class, int.class },
 1033  0
             new Object[] { this, anObject, new Integer(anIndex) } );
 1034  0
         if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
 1035  
         {
 1036  0
             return;
 1037  
         }
 1038  
 
 1039  0
         contentsChanged = true;
 1040  0
         updatedObjectIndex = anIndex;
 1041  0
         willChange();
 1042  
 
 1043  
 
 1044  
         // add to all objects
 1045  0
         if ( anIndex == displayedObjects.size() )
 1046  
         {
 1047  0
             allObjects.addObject( anObject );
 1048  0
         }
 1049  
         else // insert before same object
 1050  
         {
 1051  0
             Object target = displayedObjects.objectAtIndex( anIndex );
 1052  0
             int targetIndex = indexOf( allObjects, target );
 1053  0
             if ( targetIndex != NSArray.NotFound )
 1054  
             {
 1055  0
                 allObjects.insertObjectAtIndex( anObject, targetIndex );
 1056  0
             }
 1057  
             else // should never happen
 1058  
             {
 1059  0
                 throw new WotonomyException(
 1060  0
                     "Could not find displayed object in all objects list: "
 1061  0
                     + target );
 1062  
             }
 1063  
         }
 1064  
 
 1065  
         // add to displayed objects
 1066  0
         displayedObjects.insertObjectAtIndex( anObject, anIndex );
 1067  
 
 1068  0
         if ( dataSource != null )
 1069  
         {
 1070  0
             if ( dataSource instanceof OrderedDataSource )
 1071  
             {
 1072  0
                 ((OrderedDataSource)dataSource).insertObjectAtIndex(
 1073  0
                     anObject, anIndex );
 1074  0
             }
 1075  
             else
 1076  
             {
 1077  0
                 dataSource.insertObject( anObject );
 1078  
             }
 1079  
         }
 1080  
 
 1081  0
         notifyDelegate(
 1082  0
             "displayGroupDidInsertObject",
 1083  0
             new Class[] { EODisplayGroup.class, Object.class },
 1084  0
             new Object[] { this, anObject } );
 1085  0
     }
 1086  
 
 1087  
     /**
 1088  
     * Sets contentsChanged to true and notifies all observers.
 1089  
     */
 1090  
     public void redisplay ()
 1091  
     {
 1092  0
         contentsChanged = true;
 1093  0
         willChange();
 1094  0
     }
 1095  
 
 1096  
     /**
 1097  
     * Sets the selection to the next displayed object after the current
 1098  
     * selection.  If the last object is selected, or if no object
 1099  
     * is selected, then the first object becomes selected.
 1100  
     * If multiple items are selected, the first selected item is
 1101  
     * considered the selected item for the purposes of this method.
 1102  
     * Does not call redisplay().
 1103  
     * @return true if an object was selected.
 1104  
     */
 1105  
     public boolean selectNext ()
 1106  
     {
 1107  0
         int count = displayedObjects.count();
 1108  0
         if ( count == 0 ) return false;
 1109  0
         if ( count == 1 )
 1110  
         {
 1111  0
             selectObject( displayedObjects.objectAtIndex( 0 ) );
 1112  0
             return true;
 1113  
         }
 1114  
 
 1115  0
         int i = -1;
 1116  0
         Object selectedObject = selectedObject();
 1117  0
         if ( selectedObject != null )
 1118  
         {
 1119  0
             i = indexOf( displayedObjects, selectedObject );
 1120  
         }
 1121  0
         if ( i == NSArray.NotFound ) i = -1;
 1122  
 
 1123  
         // select next object
 1124  0
         i++;
 1125  0
         if ( i != displayedObjects.count() )
 1126  
         {
 1127  
             // set to next object
 1128  0
             selectedObject = displayedObjects.objectAtIndex( i );
 1129  0
         }
 1130  
         else // out of range
 1131  
         {
 1132  
             // set to null
 1133  0
             selectedObject = displayedObjects.objectAtIndex( 0 );
 1134  
         }
 1135  
 
 1136  0
         return selectObject( selectedObject );
 1137  
     }
 1138  
 
 1139  
     /**
 1140  
     * Sets the selection to the specified object.
 1141  
     * If the specified object is null or does not exist
 1142  
     * in the list of displayed objects, the selection
 1143  
     * will be cleared.
 1144  
     * @return true if the object was selected.
 1145  
     */
 1146  
     public boolean selectObject ( Object anObject )
 1147  
     {
 1148  0
         if ( ( anObject == null ) ||
 1149  0
              ( indexOf( displayedObjects, anObject )
 1150  0
                == NSArray.NotFound ) )
 1151  
         {
 1152  0
             clearSelection();
 1153  0
             return false;
 1154  
         }
 1155  
 
 1156  0
         selectObjectsIdenticalTo( new NSArray( new Object[] { anObject } ) );
 1157  0
         return true;
 1158  
     }
 1159  
 
 1160  
     /**
 1161  
     * Sets the selection to the specified objects.
 1162  
     * If the specified list is null or if none of the objects
 1163  
     * in the list exist in the list of displayed objects, the
 1164  
     * selection will be cleared.
 1165  
     * @return true if all specified objects were selected.
 1166  
     */
 1167  
     public boolean selectObjectsIdenticalTo ( List anObjectList )
 1168  
     {
 1169  
         // optimization: check for resetting of selection
 1170  0
         if ( ( anObjectList != null ) && ( selectedObjects.size() == anObjectList.size() ) )
 1171  
         {
 1172  0
             boolean identical = true;
 1173  0
             int size = selectedObjects.size();
 1174  0
             for ( int i = 0; ( i < size ) && identical; i++ )
 1175  
             {
 1176  
                 // compare by reference
 1177  0
                 if ( anObjectList.get( i ) != selectedObjects.get( i ) )
 1178  
                 {
 1179  0
                     identical = false;
 1180  0
                 }
 1181  0
                 else if ( displayedObjects.indexOfIdenticalObject(
 1182  0
                       anObjectList.get( i ) ) == NSArray.NotFound )
 1183  
                 {
 1184  0
                     identical = false;
 1185  
                 }
 1186  
             }
 1187  0
             if ( identical )
 1188  
             {
 1189  0
                 return true;
 1190  
             }
 1191  
         }
 1192  
 
 1193  0
         Object result = notifyDelegate(
 1194  0
             "displayGroupShouldChangeSelection",
 1195  0
             new Class[] { EODisplayGroup.class, List.class },
 1196  0
             new Object[] { this, anObjectList } );
 1197  0
         if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
 1198  
         {
 1199  
             // need to notify the calling component
 1200  
             //   to revert back to the previous selection
 1201  0
             selectionChanged = true;
 1202  0
             willChange();
 1203  0
             return false;
 1204  
         }
 1205  
 
 1206  
         int i;
 1207  0
         selectionChanged = true;
 1208  0
         willChange();
 1209  
         Object o;
 1210  0
         selectedObjects.removeAllObjects();
 1211  0
         selectedIndexes.removeAllObjects();
 1212  0
         Iterator it = anObjectList.iterator();
 1213  0
         while ( it.hasNext() )
 1214  
         {
 1215  0
             o = it.next();
 1216  0
             if ( ( i = displayedObjects.indexOfIdenticalObject( o ) )
 1217  0
                 != NSArray.NotFound )
 1218  
             {
 1219  0
                 selectedObjects.addObject( o );
 1220  0
                 selectedIndexes.addObject( new Integer( i ) );
 1221  0
             }
 1222  
         }
 1223  
         
 1224  0
         notifyDelegate(
 1225  0
             "displayGroupDidChangeSelection",
 1226  0
             new Class[] { EODisplayGroup.class },
 1227  0
             new Object[] { this } );
 1228  0
         notifyDelegate(
 1229  0
             "displayGroupDidChangeSelectedObjects",
 1230  0
             new Class[] { EODisplayGroup.class },
 1231  0
             new Object[] { this } );
 1232  
 
 1233  0
         return true;
 1234  
     }
 1235  
 
 1236  
     /**
 1237  
     * Sets the selection to the previous displayed object before the current
 1238  
     * selection.  If the first object is selected, or if no object
 1239  
     * is selected, then the last object becomes selected.
 1240  
     * If multiple items are selected, the first selected item is
 1241  
     * considered the selected item for the purposes of this method.
 1242  
     * Does not call redisplay().
 1243  
     * @return true if an object was selected.
 1244  
     */
 1245  
     public boolean selectPrevious ()
 1246  
     {
 1247  0
         int i = displayedObjects.count();
 1248  0
         if ( i == 0 ) return false;
 1249  0
         if ( i == 1 )
 1250  
         {
 1251  0
             selectObject( displayedObjects.objectAtIndex( 0 ) );
 1252  0
             return true;
 1253  
         }
 1254  
 
 1255  0
         Object selectedObject = selectedObject();
 1256  0
         if ( selectedObject != null )
 1257  
         {
 1258  0
             i = indexOf( displayedObjects, selectedObject );
 1259  
         }
 1260  0
         if ( i == NSArray.NotFound ) i = displayedObjects.count();
 1261  
 
 1262  
         // select next object
 1263  0
         i--;
 1264  0
         if ( i < 0 )
 1265  
         {
 1266  
             // out of range - select last object
 1267  0
             i = displayedObjects.count() - 1;
 1268  
         }
 1269  
 
 1270  0
         return selectObject( displayedObjects.objectAtIndex( i ) );
 1271  
     }
 1272  
 
 1273  
     /**
 1274  
     * Returns the currently selected object, or null if
 1275  
     * there is no selection.
 1276  
     */
 1277  
     public Object selectedObject ()
 1278  
     {
 1279  0
         if ( selectedObjects.count() == 0 )
 1280  
         {
 1281  0
             return null;
 1282  
         }
 1283  0
         return selectedObjects.objectAtIndex( 0 );
 1284  
     }
 1285  
 
 1286  
     /**
 1287  
     * Returns a read-only List containing all selected objects, if any.
 1288  
     * Returns an empty list if no objects are selected.
 1289  
     */
 1290  
     public NSArray selectedObjects ()
 1291  
     { // System.out.println( "avoided allocation: selectedObjects" );
 1292  0
         return selectedObjectsProxy;
 1293  
     }
 1294  
 
 1295  
     /**
 1296  
     * Returns a read-only List containing the indexes of all selected
 1297  
     * objects, if any.  The list contains instances of
 1298  
     * java.lang.Number; call intValue() to retrieve the index.
 1299  
     */
 1300  
     public NSArray selectionIndexes ()
 1301  
     {
 1302  
 //        return selectedIndexes;
 1303  
         int i;
 1304  0
         NSMutableArray result = new NSMutableArray();
 1305  0
         Enumeration e = selectedObjects.objectEnumerator();
 1306  0
         while ( e.hasMoreElements() )
 1307  
         {
 1308  0
             i = indexOf( displayedObjects, e.nextElement() );
 1309  0
             if ( i != NSArray.NotFound )
 1310  
             {
 1311  0
                 result.addObject( new Integer( i ) );
 1312  0
             }
 1313  
             else
 1314  
             {
 1315  0
               System.err.println( 
 1316  0
                   "Should never happen: selected objects not in displayed objects" );
 1317  0
               new RuntimeException().printStackTrace( System.err );
 1318  
             }
 1319  0
         }
 1320  0
         return result;
 1321  
     }
 1322  
 
 1323  
     /**
 1324  
     * Sets the objects managed by this display group.
 1325  
     * updateDisplayedObjects() is called to filter the
 1326  
     * display objects.  The previous selection will be
 1327  
     * maintained if possible.  The data source is not
 1328  
     * notified.
 1329  
     */
 1330  
     public void setObjectArray ( List anObjectList )
 1331  
     {
 1332  0
         if ( anObjectList == null ) anObjectList = new NSArray();
 1333  
 
 1334  0
         Object result = notifyDelegate(
 1335  0
             "displayGroupDisplayArrayForObjects",
 1336  0
             new Class[] { EODisplayGroup.class, List.class },
 1337  0
             new Object[] { this, anObjectList } );
 1338  0
         if ( result != null )
 1339  
         {
 1340  0
             anObjectList = (List) result;
 1341  
         }
 1342  
 
 1343  0
         contentsChanged = true;
 1344  0
         willChange();
 1345  
 
 1346  0
         NSArray oldSelectedObjects = new NSArray( selectedObjects ); // copy
 1347  
 
 1348  
         // reset allObjects to new list
 1349  0
         allObjects.removeAllObjects();
 1350  0
         allObjects.addObjectsFromArray( anObjectList );
 1351  
 
 1352  
         // update the displayed object list
 1353  0
         updateDisplayedObjects();
 1354  
         
 1355  
         // restore the selection if possible
 1356  0
         selectObjectsIdenticalTo( oldSelectedObjects );
 1357  0
     }
 1358  
 
 1359  
     /**
 1360  
     * Sets the currently selected object, or clears the
 1361  
     * selection if the object is not found or is null.
 1362  
     * Note: it's not clear how this differs from
 1363  
     * selectObject in the spec.  It is recommended that
 1364  
     * you call selectObject for now.
 1365  
     */
 1366  
     public void setSelectedObject ( Object anObject )
 1367  
     {
 1368  0
         selectObject( anObject );
 1369  0
     }
 1370  
 
 1371  
     /**
 1372  
     * Sets the current selection to the specified objects.
 1373  
     * The previous selection is cleared, and any objects
 1374  
     * in the display group that are in the specified list
 1375  
     * are then selected.  If no items in the specified list
 1376  
     * are found in the display group, then the selection is
 1377  
     * effectively cleared.
 1378  
     * Note: it's not clear how this differs from
 1379  
     * selectObjectsIdenticalTo in the spec.
 1380  
     * It is recommended that you call that method for now.
 1381  
     */
 1382  
     public void setSelectedObjects ( List aList )
 1383  
     {
 1384  0
         selectObjectsIdenticalTo( aList );
 1385  0
     }
 1386  
 
 1387  
     /**
 1388  
     * Sets the current selection to the objects at the
 1389  
     * specified indexes.  Items in the list are assumed
 1390  
     * to be instances of java.lang.Number.
 1391  
     * The previous selection is cleared, and any objects
 1392  
     * in the display group that are in the specified list
 1393  
     * are then selected.  If no items in the specified list
 1394  
     * are found in the display group, then the selection is
 1395  
     * effectively cleared.
 1396  
     */
 1397  
     public boolean setSelectionIndexes ( List aList )
 1398  
     {
 1399  
         Object o;
 1400  
         int index;
 1401  0
         NSMutableArray objects = new NSMutableArray();
 1402  0
         Iterator it = aList.iterator();
 1403  0
         while ( it.hasNext() )
 1404  
         {
 1405  0
             index = ((Number)it.next()).intValue();
 1406  0
             if ( index < displayedObjects.count() )
 1407  
             {
 1408  0
                 o = displayedObjects.objectAtIndex( index );
 1409  0
                 if ( o != null )
 1410  
                 {
 1411  0
                     objects.add( o );
 1412  0
                 }
 1413  
             }
 1414  
         }
 1415  0
         return selectObjectsIdenticalTo( objects );
 1416  
     }
 1417  
 
 1418  
     /**
 1419  
     * Applies the qualifier to all objects and sorts
 1420  
     * the results to update the list of displayed objects.
 1421  
     * Observing associations are notified to reflect the changes.
 1422  
     */
 1423  
     public void updateDisplayedObjects ()
 1424  
     {
 1425  0
         contentsChanged = true;
 1426  0
         updatedObjectIndex = -1;
 1427  0
         willChange();
 1428  
 
 1429  0
         displayedObjects.removeAllObjects();
 1430  
 
 1431  0
         displayedObjects.addObjectsFromArray( allObjects );
 1432  
 
 1433  
         // apply qualifier, if any
 1434  0
         if ( qualifier() != null )
 1435  
         {
 1436  0
             EOQualifier.filterArrayWithQualifier(
 1437  0
                 displayedObjects, qualifier() );
 1438  
         }
 1439  
 
 1440  
         // apply sort orderings, if any
 1441  0
         NSArray orderings = sortOrderings();
 1442  0
         if ( orderings != null )
 1443  
         {
 1444  0
             if ( orderings.count() > 0 )
 1445  
             {
 1446  0
                 selectionChanged = true;
 1447  0
                 willChange();
 1448  0
                 EOSortOrdering.sortArrayUsingKeyOrderArray(
 1449  0
                     displayedObjects, orderings );
 1450  
             }
 1451  
         }
 1452  
 
 1453  
           // make sure the selectedObjects is a subset of displayedObjects
 1454  
           int i;
 1455  
           Object o;
 1456  0
           Iterator it = new LinkedList( selectedObjects ).iterator();
 1457  0
           boolean removeflag = false;
 1458  0
           selectedIndexes.removeAllObjects();
 1459  0
           while ( it.hasNext() )
 1460  
           {
 1461  0
               o = it.next();
 1462  0
               if ( ( i = displayedObjects.indexOfIdenticalObject( o ) )
 1463  0
                       == NSArray.NotFound )
 1464  
               {
 1465  0
                   selectedObjects.removeIdenticalObject( o );
 1466  0
                   removeflag = true;
 1467  0
               }
 1468  
               else
 1469  
               {
 1470  0
                   selectedIndexes.addObject( new Integer( i ) );
 1471  
               }
 1472  0
           }
 1473  
 
 1474  
           //Note: it is important to put the
 1475  
           //selectionChanged = true line below remove.
 1476  0
           if (removeflag)
 1477  
           {
 1478  0
             selectionChanged = true;
 1479  0
             willChange();
 1480  
 
 1481  0
             notifyDelegate(
 1482  0
                 "displayGroupDidChangeSelection",
 1483  0
                 new Class[] { EODisplayGroup.class },
 1484  0
                 new Object[] { this } );
 1485  0
             notifyDelegate(
 1486  0
                 "displayGroupDidChangeSelectedObjects",
 1487  0
                 new Class[] { EODisplayGroup.class },
 1488  0
                 new Object[] { this } );
 1489  
           }
 1490  0
     }
 1491  
 
 1492  
     /**
 1493  
     * Returns the index of the changed object.  If more than
 1494  
     * one object has changed, -1 is returned.
 1495  
     */
 1496  
     public int updatedObjectIndex ()
 1497  
     {
 1498  0
         return updatedObjectIndex;
 1499  
     }
 1500  
 
 1501  
     // getting and setting values in objects
 1502  
 
 1503  
     /**
 1504  
     * Returns a value on the selected object for the specified key.
 1505  
     */
 1506  
     public Object selectedObjectValueForKey ( String aKey )
 1507  
     {
 1508  0
         Object selectedObject = selectedObject();
 1509  0
         if ( selectedObject == null ) return null;
 1510  0
         return valueForObject( selectedObject, aKey );
 1511  
     }
 1512  
 
 1513  
     /**
 1514  
     * Sets the specified value for the specified key on
 1515  
     * all selected objects.
 1516  
     */
 1517  
     public boolean setSelectedObjectValue (
 1518  
         Object aValue, String aKey )
 1519  
     {
 1520  0
         Object selectedObject = selectedObject();
 1521  0
         if ( selectedObject == null ) return false;
 1522  0
         return setValueForObject( aValue, selectedObject, aKey );
 1523  
     }
 1524  
 
 1525  
     /**
 1526  
     * Sets the specified value for the specified key on
 1527  
     * the specified object.  Validations may be triggered,
 1528  
     * and error dialogs may appear to the user.
 1529  
     * @return True if the value was set successfully,
 1530  
     * false if the value could not be set and the update
 1531  
     * operation should not continue.
 1532  
     */
 1533  
     public boolean setValueForObject (
 1534  
         Object aValue, Object anObject, String aKey )
 1535  
     {
 1536  
         // notify object's observers:
 1537  
         //   this includes us, and will notify our observers
 1538  0
         EOObserverCenter.notifyObserversObjectWillChange( anObject );
 1539  
 
 1540  
         //TODO: if key is null, need to remove old object
 1541  
         // and add new object instead of simply replacing it.
 1542  
 
 1543  
         try
 1544  
         {
 1545  0
             if ( anObject instanceof EOKeyValueCoding )
 1546  
             {
 1547  0
                 ((EOKeyValueCoding)anObject).takeValueForKey( aValue, aKey );
 1548  0
             }
 1549  
             else
 1550  
             {
 1551  0
                 EOKeyValueCodingSupport.takeValueForKey( anObject, aValue, aKey );
 1552  
             }
 1553  
         }
 1554  0
         catch ( RuntimeException exc )
 1555  
         {
 1556  0
             Object result = notifyDelegate(
 1557  0
                 "displayGroupShouldDisplayAlert",
 1558  0
                 new Class[] { EODisplayGroup.class, String.class, String.class },
 1559  0
                 new Object[] { this, "Error", exc.getMessage() } );
 1560  0
             if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
 1561  
             {
 1562  0
                 throw exc;
 1563  
             }
 1564  0
             return false;
 1565  0
         }
 1566  
 
 1567  0
         notifyDelegate(
 1568  0
             "displayGroupDidSetValueForObject",
 1569  0
             new Class[] { EODisplayGroup.class, Object.class, Object.class, String.class },
 1570  0
             new Object[] { this, aValue, anObject, aKey } );
 1571  
 
 1572  0
         return true;
 1573  
     }
 1574  
 
 1575  
     /**
 1576  
     * Calls setValueForObject() for the object at
 1577  
     * the specified index.
 1578  
     */
 1579  
     public boolean setValueForObjectAtIndex (
 1580  
         Object aValue, int anIndex, String aKey )
 1581  
     {
 1582  0
         return setValueForObject(
 1583  0
             aValue, displayedObjects.objectAtIndex( anIndex ), aKey );
 1584  
     }
 1585  
 
 1586  
     /**
 1587  
     * Returns the value for the specified key on the specified object.
 1588  
     */
 1589  
     public Object valueForObject ( Object anObject, String aKey )
 1590  
     {
 1591  
         // empty string is considered the identity property
 1592  0
         if ( aKey == null ) return anObject;
 1593  0
         if ( aKey.equals( "" ) ) return anObject;
 1594  
 
 1595  
         try
 1596  
         {
 1597  0
             if ( anObject instanceof EOKeyValueCoding )
 1598  
             {
 1599  0
                 return ((EOKeyValueCoding)anObject).valueForKey( aKey );
 1600  
             }
 1601  
             else
 1602  
             {
 1603  0
                 return EOKeyValueCodingSupport.valueForKey( anObject, aKey );
 1604  
             }
 1605  
         }
 1606  0
         catch ( RuntimeException exc )
 1607  
         {
 1608  0
             Object result = notifyDelegate(
 1609  0
                 "displayGroupShouldDisplayAlert",
 1610  0
                 new Class[] { EODisplayGroup.class, String.class, String.class },
 1611  0
                 new Object[] { this, "Error", exc.getMessage() } );
 1612  0
             if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
 1613  
             {
 1614  0
                 throw exc;
 1615  
             }
 1616  0
             return null;
 1617  
         }
 1618  
     }
 1619  
 
 1620  
     /**
 1621  
     * Calls valueForObject() for the object at the specified index.
 1622  
     */
 1623  
     public Object valueForObjectAtIndex ( int anIndex, String aKey )
 1624  
     {
 1625  0
         Object o = displayedObjects.objectAtIndex( anIndex );
 1626  0
         return valueForObject( o, aKey );
 1627  
     }
 1628  
 
 1629  
     /**
 1630  
     * Prints out the list of displayed objects.
 1631  
     */
 1632  
     public String toString()
 1633  
     {
 1634  0
         return displayedObjects.toString();
 1635  
     }
 1636  
 
 1637  
 
 1638  
     /**
 1639  
     * Handles notifications from the data source's editing context,
 1640  
     * looking for InvalidatedAllObjectsInStoreNotification and
 1641  
     * ObjectsChangedInEditingContextNotification, refetching in
 1642  
     * the former case and updating displayed objects in the latter.
 1643  
     * Note: This method is not in the public specification.
 1644  
     */
 1645  
     public void objectsInvalidatedInEditingContext( NSNotification aNotification )
 1646  
     {
 1647  0
         if ( EOObjectStore.InvalidatedAllObjectsInStoreNotification
 1648  0
             .equals( aNotification.name() ) )
 1649  
         {
 1650  0
             Object result = notifyDelegate(
 1651  0
                 "displayGroupShouldRefetch",
 1652  0
                 new Class[] { EODisplayGroup.class, NSNotification.class },
 1653  0
                 new Object[] { this, aNotification } );
 1654  0
             if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
 1655  
             {
 1656  0
                 fetch();
 1657  
             }
 1658  0
         }
 1659  
         else
 1660  0
         if ( EOEditingContext.ObjectsChangedInEditingContextNotification
 1661  0
             .equals( aNotification.name() ) )
 1662  
         {
 1663  0
             Object result = notifyDelegate(
 1664  0
                 "displayGroupShouldRedisplay",
 1665  0
                 new Class[] { EODisplayGroup.class, NSNotification.class },
 1666  0
                 new Object[] { this, aNotification } );
 1667  0
             if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
 1668  
             {
 1669  
                 int index;
 1670  
                 Enumeration e;
 1671  0
                 boolean didChange = false;
 1672  0
                 NSDictionary userInfo = aNotification.userInfo();
 1673  
     
 1674  
                 // inserts are ignored
 1675  
     
 1676  
                 // mark updated objects as updated
 1677  0
                 NSArray updates = (NSArray) userInfo.objectForKey(
 1678  0
                     EOObjectStore.UpdatedKey );
 1679  0
                 e = updates.objectEnumerator();
 1680  0
                 while ( e.hasMoreElements() )
 1681  
                 {
 1682  0
                     index = indexOf( displayedObjects, e.nextElement() );
 1683  0
                     if ( index != NSArray.NotFound )
 1684  
                     {
 1685  
                         //System.out.println( "EODisplayGroup: updated: " + index );
 1686  0
                         if ( ! didChange )
 1687  
                         {
 1688  0
                             didChange = true;
 1689  0
                             contentsChanged = true;
 1690  0
                             willChange();
 1691  0
                             updatedObjectIndex = index;
 1692  0
                         }
 1693  
                         else
 1694  
                         {
 1695  0
                             updatedObjectIndex = -1;
 1696  
                         }
 1697  0
                     }
 1698  
                 }
 1699  
     
 1700  
                 // treat invalidated objects as updated
 1701  0
                 NSArray invalidates = (NSArray) userInfo.objectForKey(
 1702  0
                     EOObjectStore.InvalidatedKey );
 1703  0
                 e = invalidates.objectEnumerator();
 1704  0
                 while ( e.hasMoreElements() )
 1705  
                 {
 1706  0
                     index = indexOf( displayedObjects, e.nextElement() );
 1707  0
                     if ( index != NSArray.NotFound )
 1708  
                     {
 1709  
                         //System.out.println( "EODisplayGroup: invalidated: " + index );
 1710  0
                         if ( ! didChange )
 1711  
                         {
 1712  0
                             didChange = true;
 1713  0
                             contentsChanged = true;
 1714  0
                             willChange();
 1715  0
                             updatedObjectIndex = index;
 1716  0
                         }
 1717  
                         else
 1718  
                         {
 1719  0
                             updatedObjectIndex = -1;
 1720  
                         }
 1721  0
                     }
 1722  
                 }
 1723  
     
 1724  
                 // remove deletes from display group if they exist
 1725  0
                 NSArray deletes = (NSArray) userInfo.objectForKey(
 1726  0
                     EOObjectStore.DeletedKey );
 1727  0
                 e = deletes.objectEnumerator();
 1728  
                 Object o;
 1729  0
                 while ( e.hasMoreElements() )
 1730  
                 {
 1731  0
                     o = e.nextElement();
 1732  0
                     index = indexOf( displayedObjects, o );
 1733  0
                     if ( index != NSArray.NotFound )
 1734  
                     {
 1735  
                         //System.out.println( "EODisplayGroup: deleted: " + o );
 1736  0
                         deleteObjectAtIndexNoNotify( index );
 1737  0
                     }
 1738  
                 }
 1739  
                 
 1740  0
                 if ( !usesOptimisticRefresh() )
 1741  
                 {
 1742  0
                     updateDisplayedObjects();
 1743  
                 }
 1744  
             }
 1745  
         }
 1746  
 
 1747  0
     }
 1748  
 
 1749  
     // static methods
 1750  
 
 1751  
     /**
 1752  
     * Specifies the default behavior for whether changes
 1753  
     * should be validated immediately for all display groups.
 1754  
     */
 1755  
     public static boolean
 1756  
         globalDefaultForValidatesChangesImmediately ()
 1757  
     {
 1758  0
         return globalDefaultForValidatesChangesImmediately;
 1759  
     }
 1760  
 
 1761  
     /**
 1762  
     * Specifies the default string matching format for all
 1763  
     * display groups.
 1764  
     */
 1765  
     public static String globalDefaultStringMatchFormat ()
 1766  
     {
 1767  0
         return globalDefaultStringMatchFormat;
 1768  
     }
 1769  
 
 1770  
     /**
 1771  
     * Specifies the default string matching operator for all
 1772  
     * display groups.
 1773  
     */
 1774  
     public static String globalDefaultStringMatchOperator ()
 1775  
     {
 1776  0
         return globalDefaultStringMatchOperator;
 1777  
     }
 1778  
 
 1779  
     /**
 1780  
     * Sets the default behavior for validating changes
 1781  
     * for all display groups.
 1782  
     */
 1783  
     public static void
 1784  
     setGlobalDefaultForValidatesChangesImmediately (
 1785  
         boolean validatesImmediately )
 1786  
     {
 1787  0
         globalDefaultForValidatesChangesImmediately =
 1788  0
             validatesImmediately;
 1789  0
     }
 1790  
 
 1791  
     /**
 1792  
     * Sets the default string matching format that
 1793  
     * will be used by all display groups.
 1794  
     */
 1795  
     public static void
 1796  
     setGlobalDefaultStringMatchFormat ( String aFormat )
 1797  
     {
 1798  0
         globalDefaultStringMatchFormat = aFormat;
 1799  0
     }
 1800  
 
 1801  
     /**
 1802  
     * Sets the default string matching operator that
 1803  
     * will be used by all display groups.
 1804  
     */
 1805  
     public static void
 1806  
     setGlobalDefaultStringMatchOperator ( String anOperator )
 1807  
     {
 1808  0
         globalDefaultStringMatchOperator = anOperator;
 1809  0
     }
 1810  
 
 1811  
     /**
 1812  
     * Needed because we don't inherit from NSObject.
 1813  
     * Calls EOObserverCenter.notifyObserversObjectWillChange.
 1814  
     */
 1815  
     protected void willChange()
 1816  
     {
 1817  0
         EOObserverCenter.notifyObserversObjectWillChange( this );
 1818  0
     }
 1819  
 
 1820  
     /**
 1821  
     * Called by LastGroupObserver to clear flags.
 1822  
     */
 1823  
     protected void processRecentChanges()
 1824  
     {
 1825  0
         contentsChanged = false;
 1826  0
         selectionChanged = false;
 1827  0
     }
 1828  
 
 1829  
     /**
 1830  
     * Returns the index of the specified object in the
 1831  
     * specified NSArray, comparing by value or by reference
 1832  
     * as determined by the private instance variable
 1833  
     * compareByReference.  If not found, returns NSArray.NotFound.
 1834  
     */
 1835  
     private int indexOf( NSArray anArray, Object anObject )
 1836  
     {
 1837  0
         if ( compareByReference )
 1838  
         {
 1839  0
             return anArray.indexOfIdenticalObject( anObject );
 1840  
         }
 1841  
         else
 1842  
         {
 1843  0
             return anArray.indexOf( anObject );
 1844  
         }
 1845  
     }
 1846  
 
 1847  
     // interface EOObserving
 1848  
 
 1849  
     /**
 1850  
     * Receives notifications of changes from objects that
 1851  
     * are managed by this display group.  This implementation
 1852  
     * sets updatedObjectIndex and contentsChanged as appropriate.
 1853  
     */
 1854  
     public void objectWillChange(Object anObject)
 1855  
     {
 1856  0
         int index = indexOf( displayedObjects, anObject );
 1857  0
         if ( index != NSArray.NotFound )
 1858  
         {
 1859  0
             updatedObjectIndex = index;
 1860  0
             contentsChanged = true;
 1861  0
             willChange();
 1862  
         }
 1863  0
     }
 1864  
 
 1865  
     // interface EOEditingContext.Editor
 1866  
     
 1867  
     /**
 1868  
     * Called before the editing context begins to save changes.
 1869  
     * This implementation calls endEditing().
 1870  
     */
 1871  
     public void editingContextWillSaveChanges( 
 1872  
         EOEditingContext anEditingContext )
 1873  
     {
 1874  0
         endEditing();
 1875  0
     }
 1876  
 
 1877  
     /**
 1878  
     * Called to determine whether this editor has changes
 1879  
     * that have not been committed to the object in the context.
 1880  
     */
 1881  
     public boolean editorHasChangesForEditingContext(
 1882  
         EOEditingContext anEditingContext )
 1883  
     {
 1884  0
         return ( editingAssociation() != null );
 1885  
     }
 1886  
 
 1887  
     // interface EOEditingContext.MessageHandler
 1888  
     
 1889  
     /**
 1890  
     * Called to display a message for an error that occurred
 1891  
     * in the specified editing context.  If the delegate allows,
 1892  
     * this implementation presents an informational JOptionPane.
 1893  
     * Override to customize.
 1894  
     */
 1895  
     public void editingContextPresentErrorMessage( 
 1896  
         EOEditingContext anEditingContext,
 1897  
         String aMessage )
 1898  
     {
 1899  0
         Object result = notifyDelegate(
 1900  0
             "displayGroupShouldDisplayAlert",
 1901  0
             new Class[] { EODisplayGroup.class, String.class, String.class },
 1902  0
             new Object[] { this, "Error", aMessage } );
 1903  0
         if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
 1904  
         {
 1905  0
             JOptionPane.showMessageDialog( null, aMessage );
 1906  
         }
 1907  0
     }
 1908  
 
 1909  
     /**
 1910  
     * Called by the specified object store to determine whether
 1911  
     * fetching should continue, where count is the current count
 1912  
     * and limit is the limit as specified by the fetch specification.
 1913  
     * This implementation presents an JOptionPane allowing the user
 1914  
     * to specify whether to continue.  Override to customize.
 1915  
     */
 1916  
     public boolean editingContextShouldContinueFetching(
 1917  
         EOEditingContext anEditingContext,
 1918  
         int count,
 1919  
         int limit,
 1920  
         EOObjectStore anObjectStore )
 1921  
     {
 1922  0
         return ( JOptionPane.showConfirmDialog( null, 
 1923  0
             "Fetch limit reached: do you wish to continue?", 
 1924  0
             "Continue?", 
 1925  0
             JOptionPane.YES_NO_OPTION ) == JOptionPane.YES_OPTION );
 1926  
     }
 1927  
 
 1928  
     /**
 1929  
      * Sends the specified message to the delegate.
 1930  
      * Returns the return value of the method,
 1931  
      * or null if no return value or no delegate
 1932  
      * or no implementation.
 1933  
      */
 1934  
     private Object notifyDelegate( 
 1935  
         String aMethodName, Class[] types, Object[] params )
 1936  
     {
 1937  
         try
 1938  
         {
 1939  0
             Object delegate = delegate();
 1940  0
             if ( delegate == null ) return null;
 1941  0
             return NSSelector.invoke( 
 1942  0
                 aMethodName, types, delegate, params );
 1943  
         }
 1944  0
         catch ( NoSuchMethodException e )
 1945  
         {
 1946  
             // ignore: not implemented
 1947  
         }
 1948  0
         catch ( Exception exc )
 1949  
         {
 1950  
             // log to standard error
 1951  0
             System.err.println( 
 1952  0
                 "Error while messaging delegate: " + 
 1953  0
                     delegate + " : " + aMethodName );
 1954  0
             exc.printStackTrace();
 1955  0
         }
 1956  
         
 1957  0
         return null;
 1958  
     }
 1959  
     
 1960  
     /**
 1961  
     * DisplayGroups can delegate important decisions to a Delegate.  
 1962  
     * Note that DisplayGroup doesn't require its delegates to implement
 1963  
     * this interface: rather, this interface defines the methods that
 1964  
     * DisplayGroup will attempt to invoke dynamically on its delegate.
 1965  
     * The delegate may choose to implement only a subset of the methods
 1966  
     * on the interface.
 1967  
     */
 1968  
     public interface Delegate
 1969  
     {
 1970  
         /**
 1971  
         * Called when the specified data source fails
 1972  
         * to create an object for the specified display group.
 1973  
         */
 1974  
         void displayGroupCreateObjectFailed (
 1975  
             EODisplayGroup aDisplayGroup,
 1976  
             EODataSource aDataSource );
 1977  
 
 1978  
         /**
 1979  
         * Called after the specified display group's
 1980  
         * data source is changed.
 1981  
         */
 1982  
         void displayGroupDidChangeDataSource (
 1983  
             EODisplayGroup aDisplayGroup );
 1984  
 
 1985  
         /**
 1986  
         * Called after a change occurs in the specified 
 1987  
         * display group's selected objects.
 1988  
         */
 1989  
         void displayGroupDidChangeSelectedObjects (
 1990  
             EODisplayGroup aDisplayGroup );
 1991  
 
 1992  
         /**
 1993  
         * Called after the specified display group's
 1994  
         * selection has changed.
 1995  
         */
 1996  
         void displayGroupDidChangeSelection (
 1997  
             EODisplayGroup aDisplayGroup );
 1998  
 
 1999  
         /**
 2000  
         * Called after the specified display group has
 2001  
         * deleted the specified object.
 2002  
         */
 2003  
         void displayGroupDidDeleteObject (
 2004  
             EODisplayGroup aDisplayGroup,
 2005  
             Object anObject );
 2006  
 
 2007  
         /**
 2008  
         * Called after the specified display group
 2009  
         * has fetched the specified object list.
 2010  
         */
 2011  
         void displayGroupDidFetchObjects (
 2012  
             EODisplayGroup aDisplayGroup,
 2013  
             List anObjectList );
 2014  
 
 2015  
         /**
 2016  
         * Called after the specified display group
 2017  
         * has inserted the specified object into
 2018  
         * its internal object list.
 2019  
         */
 2020  
         void displayGroupDidInsertObject (
 2021  
             EODisplayGroup aDisplayGroup,
 2022  
             Object anObject );
 2023  
 
 2024  
         /**
 2025  
         * Called after the specified display group
 2026  
         * has set the specified value for the specified
 2027  
         * object and key.
 2028  
         */
 2029  
         void displayGroupDidSetValueForObject (
 2030  
             EODisplayGroup aDisplayGroup,
 2031  
             Object aValue,
 2032  
             Object anObject,
 2033  
             String aKey );
 2034  
 
 2035  
         /**
 2036  
         * Called by the specified display group to
 2037  
         * determine what objects should be displayed
 2038  
         * for the objects in the specified list.
 2039  
         * @return An NSArray containing the objects
 2040  
         * to be displayed for the objects in the
 2041  
         * specified list.
 2042  
         */
 2043  
         NSArray displayGroupDisplayArrayForObjects (
 2044  
             EODisplayGroup aDisplayGroup,
 2045  
             List aList );
 2046  
 
 2047  
         /**
 2048  
         * Called by the specified display group before
 2049  
         * it attempts to change the selection.
 2050  
         * @return True to allow the selection to change,
 2051  
         * false otherwise.
 2052  
         */
 2053  
         boolean displayGroupShouldChangeSelection (
 2054  
             EODisplayGroup aDisplayGroup,
 2055  
             List aSelectionList );
 2056  
 
 2057  
         /**
 2058  
         * Called by the specified display group before
 2059  
         * it attempts to delete the specified object.
 2060  
         * @return True to allow the object to be deleted
 2061  
         * false to prevent the deletion.
 2062  
         */
 2063  
         boolean displayGroupShouldDeleteObject (
 2064  
             EODisplayGroup aDisplayGroup,
 2065  
             Object anObject );
 2066  
 
 2067  
         /**
 2068  
         * Called by the specified display group before
 2069  
         * it attempts display the specified alert to
 2070  
         * the user.
 2071  
         * @return True to allow the message to be
 2072  
         * displayed, false if you want to handle the
 2073  
         * alert yourself and suppress the display group's
 2074  
         * notification.
 2075  
         */
 2076  
         boolean displayGroupShouldDisplayAlert (
 2077  
             EODisplayGroup aDisplayGroup,
 2078  
             String aTitle,
 2079  
             String aMessage );
 2080  
 
 2081  
         /**
 2082  
         * Called by the specified display group before
 2083  
         * it attempts fetch objects.
 2084  
         * @return True to allow the fetch to take place,
 2085  
         * false to prevent the fetch.
 2086  
         */
 2087  
         boolean displayGroupShouldFetch (
 2088  
             EODisplayGroup aDisplayGroup );
 2089  
 
 2090  
         /**
 2091  
         * Called by the specified display group before
 2092  
         * it attempts to insert the specified object.
 2093  
         * @return True to allow the object to be inserted
 2094  
         * false to prevent the insertion.
 2095  
         */
 2096  
         boolean displayGroupShouldInsertObject (
 2097  
             EODisplayGroup aDisplayGroup,
 2098  
             Object anObject,
 2099  
             int anIndex );
 2100  
 
 2101  
         /**
 2102  
         * Called by the specified display group when
 2103  
         * it receives the specified 
 2104  
         * ObjectsChangedInEditingContextNotification.
 2105  
         * @return True to allow the display group to
 2106  
         * update the display (recommended), false
 2107  
         * to prevent the update.
 2108  
         */
 2109  
         boolean displayGroupShouldRedisplay (
 2110  
             EODisplayGroup aDisplayGroup,
 2111  
             NSNotification aNotification );
 2112  
 
 2113  
         /**
 2114  
         * Called by the specified display group when
 2115  
         * it receives the specified 
 2116  
         * InvalidatedAllObjectsInStoreNotification.
 2117  
         * @return True to allow the display group to
 2118  
         * refetch (recommended), false to prevent the 
 2119  
         * refetch.
 2120  
         */
 2121  
         boolean displayGroupShouldRefetch (
 2122  
             EODisplayGroup aDisplayGroup,
 2123  
             NSNotification aNotification );
 2124  
 
 2125  
     }
 2126  
 
 2127  
 }
 2128  
 
 2129  
     /**
 2130  
     * A private class that will serve to clear the contentsChanged
 2131  
     * and selectionChanged flags after all Associations have been
 2132  
     * notified.
 2133  
     */
 2134  
     class LastGroupObserver extends EODelayedObserver
 2135  
     {
 2136  
         Reference ref;
 2137  
 
 2138  
         public LastGroupObserver( EODisplayGroup aDisplayGroup )
 2139  
         {
 2140  
             ref = new WeakReference( aDisplayGroup );
 2141  
         }
 2142  
 
 2143  
         /**
 2144  
         * We want to be informed last, after all Associations
 2145  
         * have been notified to changes in the DisplayGroup.
 2146  
         */
 2147  
         public int priority()
 2148  
         {
 2149  
             return ObserverPrioritySixth;
 2150  
         }
 2151  
 
 2152  
         /**
 2153  
         * After all Associations have been notified,
 2154  
         * clear the contentsChanged and selectionChanged flags.
 2155  
         */
 2156  
         public void subjectChanged ()
 2157  
         {
 2158  
             EODisplayGroup group = (EODisplayGroup) ref.get();
 2159  
             if ( group != null )
 2160  
             {
 2161  
                 group.processRecentChanges();
 2162  
             }
 2163  
         }
 2164  
     }
 2165  
 
 2166  
 /*
 2167  
  * $Log$
 2168  
  * Revision 1.2  2006/02/18 23:14:35  cgruber
 2169  
  * Update imports and maven dependencies.
 2170  
  *
 2171  
  * Revision 1.1  2006/02/16 13:22:22  cgruber
 2172  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 2173  
  *
 2174  
  * Revision 1.51  2004/01/28 18:35:40  mpowers
 2175  
  * Slight optimization: only comparing list in fetch if we have to.
 2176  
  *
 2177  
  * Revision 1.50  2004/01/27 20:42:30  mpowers
 2178  
  * No longer reselecting first after fetch if contents are identical.
 2179  
  *
 2180  
  * Revision 1.49  2003/12/18 11:37:45  mpowers
 2181  
  * Now calling qualifier internally.
 2182  
  *
 2183  
  * Revision 1.48  2003/08/06 23:07:52  chochos
 2184  
  * general code cleanup (mostly, removing unused imports)
 2185  
  *
 2186  
  * Revision 1.47  2003/01/18 23:30:42  mpowers
 2187  
  * WODisplayGroup now compiles.
 2188  
  *
 2189  
  * Revision 1.46  2002/10/24 21:15:36  mpowers
 2190  
  * New implementations of NSArray and subclasses.
 2191  
  *
 2192  
  * Revision 1.45  2002/10/24 18:20:20  mpowers
 2193  
  * Because NSArray is read-only, we are returning our internal representations
 2194  
  * to callers of allObjects(), displayedObjects(), and selectedObjects().
 2195  
  *
 2196  
  * Revision 1.44  2002/08/06 18:20:25  mpowers
 2197  
  * Now posting DisplayGroupWillFetch notifications before fetch.
 2198  
  * Implemented support for usesOptimisticRefresh.
 2199  
  * No longer supporting inserted/updated/deleted lists: not part of spec.
 2200  
  *
 2201  
  * Revision 1.43  2002/05/17 15:01:49  mpowers
 2202  
  * Implemented dynamic lookup of delegate methods so delegates no longer
 2203  
  * need to implement the DisplayGroup.Delegate interface.
 2204  
  *
 2205  
  * Revision 1.42  2002/03/26 21:46:06  mpowers
 2206  
  * Contributing EditingContext as a java-friendly convenience.
 2207  
  *
 2208  
  * Revision 1.41  2002/03/11 03:17:56  mpowers
 2209  
  * Provided control point for coalesced changes.
 2210  
  *
 2211  
  * Revision 1.40  2002/03/05 23:18:28  mpowers
 2212  
  * Added documentation.
 2213  
  * Added isSelectionPaintedImmediate and isSelectionTracking attributes
 2214  
  * to TableAssociation.
 2215  
  * Added getTableAssociation to TableColumnAssociation.
 2216  
  *
 2217  
  * Revision 1.39  2002/02/19 22:26:04  mpowers
 2218  
  * Implemented EOEditingContext.MessageHandler support.
 2219  
  *
 2220  
  * Revision 1.38  2002/02/19 16:37:38  mpowers
 2221  
  * Implemented support for EOEditingContext.Editor
 2222  
  *
 2223  
  * Revision 1.37  2001/12/11 22:17:48  mpowers
 2224  
  * Now properly handling exceptions in valueForObject.
 2225  
  * No longer trying to retain selection based only on index.
 2226  
  *
 2227  
  * Revision 1.36  2001/11/08 21:42:00  mpowers
 2228  
  * Now we know what to do with shouldRefetch and shouldRedisplay.
 2229  
  *
 2230  
  * Revision 1.35  2001/11/04 18:26:58  mpowers
 2231  
  * Fixed bug where exceptions were not properly reported when updating
 2232  
  * a value and the display group did not have a delegate.
 2233  
  *
 2234  
  * Revision 1.34  2001/11/02 20:59:36  mpowers
 2235  
  * Now correctly ensuring selected objects are a subset of displayed objects.
 2236  
  *
 2237  
  * Revision 1.33  2001/10/30 22:56:45  mpowers
 2238  
  * Added support for EOQualifier.
 2239  
  *
 2240  
  * Revision 1.32  2001/10/23 22:27:53  mpowers
 2241  
  * Now running at ObserverPrioritySixth.
 2242  
  *
 2243  
  * Revision 1.31  2001/10/23 18:45:05  mpowers
 2244  
  * Rolling back changes.
 2245  
  *
 2246  
  * Revision 1.28  2001/08/22 19:23:41  mpowers
 2247  
  * No longer asserting objects in all objects list.
 2248  
  *
 2249  
  * Revision 1.27  2001/07/30 16:17:01  mpowers
 2250  
  * Minor code cleanup.
 2251  
  *
 2252  
  * Revision 1.26  2001/07/10 22:49:07  mpowers
 2253  
  * Fixed bug in optimization for selectObjectsIdenticalTo (found by Dongzhi).
 2254  
  *
 2255  
  * Revision 1.25  2001/06/19 15:40:21  mpowers
 2256  
  * Now only changing the selection if the new selection is different
 2257  
  * from the old.
 2258  
  *
 2259  
  * Revision 1.24  2001/05/24 17:36:15  mpowers
 2260  
  * Fixed problem with selectedObjectsIdenticalTo: it was using compare
 2261  
  * by value instead of compare by reference.
 2262  
  *
 2263  
  * Revision 1.23  2001/05/18 21:09:19  mpowers
 2264  
  * Now throwing exceptions if the delegate cannot handle error from update.
 2265  
  *
 2266  
  * Revision 1.22  2001/05/14 15:26:12  mpowers
 2267  
  * Now checking for null delegate before and after selection change.
 2268  
  *
 2269  
  * Revision 1.21  2001/05/08 18:47:34  mpowers
 2270  
  * Minor fixes for d3.
 2271  
  *
 2272  
  * Revision 1.20  2001/04/29 22:02:45  mpowers
 2273  
  * Work on id transposing between editing contexts.
 2274  
  *
 2275  
  * Revision 1.19  2001/04/13 16:38:09  mpowers
 2276  
  * Alpha3 release.
 2277  
  *
 2278  
  * Revision 1.18  2001/04/03 20:36:01  mpowers
 2279  
  * Fixed refaulting/reverting/invalidating to be self-consistent.
 2280  
  *
 2281  
  * Revision 1.17  2001/03/29 03:31:13  mpowers
 2282  
  * No longer using Introspector.
 2283  
  *
 2284  
  * Revision 1.16  2001/02/27 03:32:18  mpowers
 2285  
  * Implemented default values for new objects.
 2286  
  *
 2287  
  * Revision 1.15  2001/02/27 02:11:17  mpowers
 2288  
  * Now throwing exception when cloning fails.
 2289  
  * Removed debugging printlns.
 2290  
  *
 2291  
  * Revision 1.14  2001/02/26 22:41:51  mpowers
 2292  
  * Implemented null placeholder classes.
 2293  
  * Duplicator now uses NSNull.
 2294  
  * No longer catching base exception class.
 2295  
  *
 2296  
  * Revision 1.13  2001/02/26 15:53:22  mpowers
 2297  
  * Fine-tuning notification firing.
 2298  
  * Child display groups now update properly after parent save or invalidate.
 2299  
  *
 2300  
  * Revision 1.12  2001/02/22 20:55:06  mpowers
 2301  
  * Implemented notification handling.
 2302  
  *
 2303  
  * Revision 1.11  2001/02/21 20:40:42  mpowers
 2304  
  * setObjectArray now falls back to index when trying to retain the
 2305  
  * same selection.
 2306  
  *
 2307  
  * Revision 1.10  2001/02/20 16:38:55  mpowers
 2308  
  * MasterDetailAssociations now observe their controlled display group's
 2309  
  * objects for changes to that the parent object will be marked as updated.
 2310  
  * Before, only inserts and deletes to an object's items are registered.
 2311  
  * Also, moved ObservableArray to package access.
 2312  
  *
 2313  
  * Revision 1.9  2001/02/17 17:23:49  mpowers
 2314  
  * More changes to support compiling with jdk1.1 collections.
 2315  
  *
 2316  
  * Revision 1.8  2001/02/17 16:52:05  mpowers
 2317  
  * Changes in imports to support building with jdk1.1 collections.
 2318  
  *
 2319  
  * Revision 1.7  2001/01/24 16:35:37  mpowers
 2320  
  * Improved documentation on TreeAssociation.
 2321  
  * SortOrderings are now inherited from parent nodes.
 2322  
  * Updates after sorting are still lost on TreeController.
 2323  
  *
 2324  
  * Revision 1.6  2001/01/24 14:23:05  mpowers
 2325  
  * Added support for OrderedDataSource.
 2326  
  *
 2327  
  * Revision 1.5  2001/01/12 17:21:37  mpowers
 2328  
  * Implicit creation of EOSortOrderings now happens in setSortOrderings.
 2329  
  *
 2330  
  * Revision 1.4  2001/01/11 20:34:26  mpowers
 2331  
  * Implemented EOSortOrdering and added support in framework.
 2332  
  * Added header-click to sort table columns.
 2333  
  *
 2334  
  * Revision 1.3  2001/01/10 22:49:44  mpowers
 2335  
  * Implemented similarly named selection methods instead of
 2336  
  * throwing exceptions.
 2337  
  *
 2338  
  * Revision 1.2  2001/01/09 20:12:52  mpowers
 2339  
  * Moved inner classes to package access.
 2340  
  *
 2341  
  * Revision 1.1.1.1  2000/12/21 15:48:20  mpowers
 2342  
  * Contributing wotonomy.
 2343  
  *
 2344  
  * Revision 1.21  2000/12/20 16:25:39  michael
 2345  
  * Added log to all files.
 2346  
  *
 2347  
  * Revision 1.20  2000/12/15 15:04:42  michael
 2348  
  * Added doc.
 2349  
  *
 2350  
  * Revision 1.19  2000/12/11 13:32:48  michael
 2351  
  * Finish the much better TreeAssociation implementation.
 2352  
  * TreeAssociation now has no gui dependencies.
 2353  
  *
 2354  
  * Revision 1.18  2000/12/05 17:41:46  michael
 2355  
  * Broadcasts selection change after delegate refuses selection change
 2356  
  * so the initiating association gets refreshed.
 2357  
  *
 2358  
  */
 2359