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