From 40a9d99496e098562f090fb7ffce9e749011b131 Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Mon, 20 May 2024 17:58:16 -0400 Subject: Formatting pass --- .../main/java/net/wotonomy/web/WODisplayGroup.java | 4219 +++++++++----------- 1 file changed, 1881 insertions(+), 2338 deletions(-) (limited to 'projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WODisplayGroup.java') diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WODisplayGroup.java b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WODisplayGroup.java index bda1dd5..0bdefdf 100644 --- a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WODisplayGroup.java +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WODisplayGroup.java @@ -49,2407 +49,1950 @@ import net.wotonomy.foundation.internal.Duplicator; import net.wotonomy.foundation.internal.WotonomyException; /** -* WODisplayGroup manages a set of objects, -* allowing them to be sorted, batched, and filtered. -* WODisplay also acts as a bridge to the wotonomy's -* control package, including WODisplayGroup and -* EOEditingContext. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 905 $ -*/ -public class WODisplayGroup extends Observable - implements EOObserving, EOEditingContext.Editor, - java.io.Serializable -{ - /** - * Notification sent when the display group is about to fetch. - */ - public static final String DisplayGroupWillFetchNotification - = "DisplayGroupWillFetchNotification"; - - private static boolean - globalDefaultForValidatesChangesImmediately = true; - private static String - globalDefaultStringMatchFormat = "caseInsensitiveLike"; - private static String - globalDefaultStringMatchOperator = "%@*"; - - protected NSMutableArray allObjects; - protected NSArray allObjectsProxy; - protected NSMutableArray displayedObjects; - protected NSArray displayedObjectsProxy; - protected NSMutableArray selectedObjects; - protected NSArray selectedObjectsProxy; - protected NSMutableArray selectedIndexes; - - private String defaultStringMatchOperator; - private String defaultStringMatchFormat; - - private boolean validatesChangesImmediately; - private Object delegate; - private EODataSource dataSource; - private EOQualifier qualifier; - private NSMutableArray sortOrderings; - private NSArray sortOrderingsProxy; - - private NSArray localKeys; - private NSDictionary insertedObjectDefaultValues; - private boolean fetchesOnLoad; - private boolean selectsFirstObjectAfterFetch; - private boolean usesOptimisticRefresh; - private boolean inQueryMode; - - // change detection: package access for helper classes - boolean selectionChanged; - int updatedObjectIndex; - - // batching - private int batchIndex; - private int batchSize; - - // this property is not in the spec - private boolean compareByReference = false; - - private EOObserving lastGroupObserver; - - /** - * Creates a new display group. - */ - public WODisplayGroup() - { - validatesChangesImmediately = - globalDefaultForValidatesChangesImmediately(); - defaultStringMatchOperator = - globalDefaultStringMatchFormat(); - defaultStringMatchFormat = - globalDefaultStringMatchOperator(); - - allObjects = new ObservableArray( this ); - allObjectsProxy = NSArray.arrayBackedByList( allObjects ); - displayedObjects = new NSMutableArray(); - displayedObjectsProxy = NSArray.arrayBackedByList( displayedObjects ); - selectedObjects = new NSMutableArray(); - selectedObjectsProxy = NSArray.arrayBackedByList( selectedObjects ); - sortOrderings = new NSMutableArray(); - sortOrderingsProxy = NSArray.arrayBackedByList( sortOrderings ); - selectedIndexes = new NSMutableArray(); - - delegate = null; - dataSource = null; - qualifier = null; - - localKeys = new NSArray(); // not implemented - insertedObjectDefaultValues = new NSDictionary(); - fetchesOnLoad = false; // not implemented - selectsFirstObjectAfterFetch = false; - usesOptimisticRefresh = false; - inQueryMode = false; // not implemented - - selectionChanged = false; - updatedObjectIndex = -1; - - batchIndex = 0; - batchSize = 0; - } - - - - // specify optional data source - - /** - * Sets the data source that will be used by - * this display group. - */ - public void setDataSource ( EODataSource aDataSource ) - { - if ( ( dataSource != null ) - && ( dataSource.editingContext() != null ) ) - { - // un-register for notifications from existing parent store - NSNotificationCenter.defaultCenter().removeObserver( - this, null, dataSource.editingContext() ); - dataSource.editingContext().removeEditor( this ); - if ( dataSource.editingContext().messageHandler() == this ) - { - dataSource.editingContext().setMessageHandler( null ); - } - - } - - dataSource = aDataSource; - - if ( ( dataSource != null ) - && ( dataSource.editingContext() != null ) ) - { - // register for notifications from parent store - NSNotificationCenter.defaultCenter().addObserver( - this, new NSSelector( "objectsInvalidatedInEditingContext", - new Class[] { NSNotification.class } ), - null, dataSource.editingContext() ); - - // add ourselves as editor - dataSource.editingContext().addEditor( this ); - - // add ourselves as message handler if no such handler exists - if ( dataSource.editingContext().messageHandler() == null ) - { - dataSource.editingContext().setMessageHandler( this ); - } - } - } - - /** - * Returns the current data source backing this display group, - * or null if no dataSource is currently used. - */ - public EODataSource dataSource() - { - return dataSource; - } - - /** - * Returns the key by which this display group is bound a master - * display group, or null if this is not a detail display group. - */ - public String detailKey() - { - if ( dataSource instanceof PropertyDataSource ) - { - return ((PropertyDataSource)dataSource).key(); - } - return null; - } - - /** - * Sets the key by which this display group is bound a master - * display group. Does nothing if this is not a detail display group. - */ - public void setDetailKey( String aKey ) - { - if ( dataSource instanceof PropertyDataSource ) - { - ((PropertyDataSource)dataSource).setKey( aKey ); - } - } - - /** - * Returns whether the data source is a detail data source, - * suggesting that this is a detail display group. - */ - public boolean hasDetailDataSource() - { - return ( dataSource instanceof PropertyDataSource ); - } - - /** - * Returns the selected object in the master display group, - * or null if this is not a detail display group. - */ - public Object masterObject() - { - if ( dataSource instanceof PropertyDataSource ) - { - return ((PropertyDataSource)dataSource).source(); - } - return null; - } - - /** - * Sets the master object in the detail data source. - * Does nothing if there is no detail data source. - */ - public void setMasterObject( Object anObject ) - { - if ( dataSource instanceof PropertyDataSource ) - { - ((PropertyDataSource)dataSource).setSource( anObject ); - } - } - - // specify optional delegate - - /** - * Sets the display group delegate that - * will be used by this display group. - */ - public void setDelegate ( Object aDelegate ) - { - delegate = aDelegate; - } - - /** - * Returns the current delegate for this display group, - * or null if no delegate is currently set. - */ - public Object delegate() - { - return delegate; - } - - - - // display group configuration - - /** - * Returns the current string matching format. - * If not set, defaults to "%@*". - */ - public String defaultStringMatchFormat() - { - return defaultStringMatchFormat; - } - - /** - * Returns the current string matching operator. - * If not set, defaults to "caseInsensitiveLike". - */ - public String defaultStringMatchOperator() - { - return defaultStringMatchOperator; - } - - /** - * Sets the display group and associations to edit a - * "query by example" query object. This method is - * used for target/action connections. - */ - public void enterQueryMode ( Object aSender ) - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns whether this display group should immediate - * fetch when loaded. - */ - public boolean fetchesOnLoad() - { - return fetchesOnLoad; - } - - /** - * Returns whether this display group is in "query by - * example" mode. - */ - public boolean inQueryMode() - { - return inQueryMode; - } - - /** - * Returns a Map of default values that are applied - * to new objects that are inserted into the list. - */ - public NSDictionary insertedObjectDefaultValues() - { - return insertedObjectDefaultValues; - } - - /** - * Returns the keys that were declared when read from - * an external resource file. - */ - public NSArray localKeys() - { - return localKeys; - } - - /** - * Sets whether this display group will select the - * first object in the list after a fetch. - */ - public boolean selectsFirstObjectAfterFetch() - { - return selectsFirstObjectAfterFetch; - } - - /** - * Sets the default string matching format that - * will be used by this display group. - */ - public void setDefaultStringMatchFormat ( String aFormat ) - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Sets the default string matching operator that - * will be used by this display group. - */ - public void setDefaultStringMatchOperator ( String anOperator ) - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Sets whether this display group will fetch objects - * from its data source on load. - */ - public void setFetchesOnLoad ( boolean willFetch ) - { - fetchesOnLoad = willFetch; - } - - /** - * Sets whether this display group is in "query by example" - * mode. If true, all associations will bind to a special - * "example" object. - */ - public void setInQueryMode ( boolean isInQueryMode ) - { - inQueryMode = isInQueryMode; - } - - /** - * Sets the mapping that contains the values that will - * be applied to new objects inserted into the display group. - */ - public void setInsertedObjectDefaultValues ( Map aMap ) - { - insertedObjectDefaultValues = new NSDictionary( aMap ); - } - - /** - * Sets the keys that are declared when instantiated from - * an external resource file. - */ - public void setLocalKeys ( List aKeyList ) - { - localKeys = new NSArray( (Collection) aKeyList ); - } - - /** - * Sets whether the first object in the list will be - * selected after a fetch. - */ - public void setSelectsFirstObjectAfterFetch ( - boolean selectsFirst ) - { - selectsFirstObjectAfterFetch = selectsFirst; - } - - /** - * Sets the order of the keys by which this display group - * will be ordered after a fetch or after a call to - * updateDisplayedObjects(). The elements in the display - * group will be sorted first by the first key, within - * the first key, by the second key, and so on. - */ - public void setSortOrderings ( List aList ) - { - sortOrderings.removeAllObjects(); - - Object o; - Iterator it = aList.iterator(); - while ( it.hasNext() ) - { - o = it.next(); - // handle the convenience of specifying just a key - if ( ! ( o instanceof EOSortOrdering ) ) - { - o = new EOSortOrdering( - o.toString(), EOSortOrdering.CompareAscending ); - } - sortOrderings.add( o ); - } - } - - /** - * Sets whether only changed objects are refreshed (optimistic), - * or whether all objects are refreshed (pessimistic, default). - * By default, when the display group receives notification that - * one of its objects has changed, updateDisplayedObjects is called. - */ - public void setUsesOptimisticRefresh ( boolean isOptimistic ) - { - usesOptimisticRefresh = isOptimistic; - } - - /** - * Sets whether changes made by associations are validated - * immediately, or when changes are saved. - */ - public void setValidatesChangesImmediately ( - boolean validatesImmediately ) - { - validatesChangesImmediately = validatesImmediately; - } - - /** - * Returns a read-only List of sort orderings for this display group. - */ - public NSArray sortOrderings() - { - return sortOrderingsProxy; - } - - /** - * Returns whether this display group refreshes only - * the changed objects or all objects on refresh. - */ - public boolean usesOptimisticRefresh() - { - return usesOptimisticRefresh; - } - - /** - * Returns whether this display group validates changes - * immediately. Otherwise, validation should occur when - * changes are saved. Default is the global default, - * which is initially true. - */ - public boolean validatesChangesImmediately() - { - return validatesChangesImmediately; - } - - - // qualification - - /** - * Returns a qualifier that will be applied all the objects - * in this display group to determine which objects will - * be displayed. - */ - public EOQualifier qualifier() - { - return qualifier; - } - - /** - * Returns a new qualifier built from the three query - * value maps: greater than, equal to, and less than. - */ - public EOQualifier qualifierFromQueryValues() - { - //TODO: assemble qualifier from query values - - return new EOQualifier() - { - // use inner class until we actually implement one - public EOQualifier qualifierWithBindings( - Map aMap, - boolean requireAll ) - { - return null; - } - public Throwable - validateKeysWithRootClassDescription( Class aClass ) - { - return null; - } - public boolean evaluateWithObject(Object o) - { - return false; - } - }; - } - - /** - * Calls qualifierFromQueryValues(), applies the result - * to the data source, and calls fetch(). - */ - public void qualifyDataSource() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Calls qualifierFromQueryValues(), sets the qualifier - * with setQualifier(), and calls updateDisplayedObjects(). - */ - public void qualifyDisplayGroup() - { - setQualifier( qualifierFromQueryValues() ); - updateDisplayedObjects(); - } - - /** - * Returns a Map containing the mappings of keys - * to binding query values. - */ - public NSDictionary queryBindingValues() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns a Map containing the mappings of keys - * to binding query values. - */ - public NSMutableDictionary queryBindings() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns a Map containing the mappings of keys - * to binding query values for a matching query. - */ - public NSMutableDictionary queryMatch() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns a Map containing the mappings of keys - * to binding query values for a minimum value query. - */ - public NSMutableDictionary queryMin() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns a Map containing the mappings of keys - * to binding query values for a maximum value query. - */ - public NSMutableDictionary queryMax() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns a Map containing the mappings of keys - * to operator values. - */ - public NSMutableDictionary queryOperator() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns a list containing all supported qualifier operators. - */ - public NSArray allQualifierOperators() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns a Map containing the mappings of keys - * to operator values. - */ - public NSDictionary queryOperatorValues() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Sets the qualifier that will be used by - * updateDisplayedObjects() to filter displayed objects. - */ - public void setQualifier ( EOQualifier aQualifier ) - { - qualifier = aQualifier; - } - - /** - * Sets the mapping that contains the mappings of keys - * to binding values. - */ - public void setQueryBindingValues ( Map aMap ) - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Sets the mapping that contains the mappings of keys - * to operator values. - */ - public void setQueryOperatorValues ( Map aMap ) - { - throw new RuntimeException( "Not implemented yet." ); - } - - - - // qualifier query values - - /** - * Returns a Map containing the mappings of keys - * to query values that will be used to test for equality. - */ - public NSDictionary equalToQueryValues() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns a Map containing the mappings of keys - * to query values that will be used to test for greater value. - */ - public NSDictionary greaterThanQueryValues() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Returns a Map containing the mappings of keys - * to query values that will be used to test for lesser value. - */ - public NSDictionary lessThanQueryValues() - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Sets the Map that contains the mappings of keys - * to query values that will be used to test for equality. - */ - public void setEqualToQueryValues ( Map aMap ) - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Sets the mapping that contains the mappings of keys - * to query values that will be used to test for greater value. - */ - public void setGreaterThanQueryValues ( Map aMap ) - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Sets the mapping that contains the mappings of keys - * to query values that will be used to test for lesser value. - */ - public void setLessThanQueryValues ( Map aMap ) - { - throw new RuntimeException( "Not implemented yet." ); - } - - /** - * Deprecated: returns true. - */ - public boolean endEditing() - { - return true; - } - - - // object management - - /** - * Returns a read-only List containing all objects managed by the display group. - * This includes those objects not visible due to disqualification. - */ - public NSArray allObjects() - { //System.out.println( "avoided allocation: allObjects" ); - return allObjectsProxy; - } - - /** - * Returns the total number of batches held by this display group. - */ - public int batchCount() - { - if ( batchSize < 1 ) return 1; - int count = displayedObjects.count(); - if ( count == 0 ) return 1; - return ((count-1) / batchSize) + 1; - } - - /** - * Returns the index of the currently displayed batch. - */ - public int currentBatchIndex() - { - return batchIndex; - } - - /** - * Sets the index of the currently displayed batch. - */ - public void setCurrentBatchIndex( int aBatchIndex ) - { - batchIndex = aBatchIndex; - updateDisplayedObjects(); - } - - /** - * Sets the displayed objects to the batch containing - * the first selected object, and updates the current - * batch index, and returns null to force a page reload. - * Displays the first batch is there is no selection. - */ - public Object displayBatchContainingSelectedObject() - { - if ( batchSize < 1 ) return null; - NSArray indexes = selectionIndexes(); - if ( indexes.count() > 0 ) - { - batchIndex = - ((Number)indexes.objectAtIndex( 0 )).intValue() / batchSize; - } - else - { - batchIndex = 0; - } - updateDisplayedObjects(); - return null; - } - - /** - * Sets the displayed objects to the next batch - * and updates the current batch index, and returns null - * to force a page reload. If there is no next batch - * the first batch is displayed. - */ - public Object displayNextBatch() - { - batchIndex = (batchIndex + 1) % batchCount(); - updateDisplayedObjects(); - return null; - } - - /** - * Sets the displayed objects to the next batch - * and updates the current batch index, and returns null - * to force a page reload. If there is no previous - * batch, the last batch is displayed. - */ - public Object displayPreviousBatch() - { - batchIndex--; - if ( batchIndex < 0 ) batchIndex = batchCount() - 1; - updateDisplayedObjects(); - return null; - } - - /** - * Returns whether the displayed objects have been batched. - */ - public boolean hasMultipleBatches() - { - return batchCount() > 1; - } - - /** - * Returns the one-based index within the displayed objects list - * of the first displayed object in the current batch. - */ - public int indexOfFirstDisplayedObject() - { - if ( batchSize < 1 ) return 1; - return batchIndex * batchSize + 1; - } - - /** - * Returns the one-based index within the displayed objects list - * of the first last object in the current batch. - */ - public int indexOfLastDisplayedObject() - { - if ( batchSize < 1 ) return displayedObjects.count(); - return Math.min( - ((batchIndex+1) * batchSize), - displayedObjects.count() ); - } - - /** - * Returns the number of objects per batch. - */ - public int numberOfObjectsPerBatch() - { - return batchSize; - } - - /** - * Returns the number of objects per batch. - */ - public void setNumberOfObjectsPerBatch( int aSize ) - { - batchSize = aSize; - updateDisplayedObjects(); - } - - /** - * Clears the current selection. - * @return True is the selection was cleared, - * False if the selection could not be cleared - * @see #setSelectionIndexes - */ - public boolean clearSelection() - { - Object result = notifyDelegate( - "displayGroupShouldChangeSelection", - new Class[] { WODisplayGroup.class, List.class }, - new Object[] { this, new NSArray( selectedObjects ) } ); - if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) ) - { - return false; - } - - selectionChanged = true; - willChange(); - - selectedObjects.removeAllObjects(); - selectedIndexes.removeAllObjects(); - - notifyDelegate( - "displayGroupDidChangeSelection", - new Class[] { WODisplayGroup.class }, - new Object[] { this } ); - notifyDelegate( - "displayGroupDidChangeSelectedObjects", - new Class[] { WODisplayGroup.class }, - new Object[] { this } ); - - return true; - } - - /** - * Convenience for binding to a component action: - * calls deleteSelection() and then displayBatchContainingSelectedObject() - * and returns null, which is a suitable result from a component action. - */ - public Object delete() - { - deleteSelection(); - displayBatchContainingSelectedObject(); - return null; - } - - /** - * Deletes the object at the specified index, - * notifying the delegate before and after the operation, - * and then updating the selection if needed. - * @return True if delete was successful, false if the - * object was not deleted. - */ - public boolean deleteObjectAtIndex ( int anIndex ) - { - Object target = displayedObjects.objectAtIndex( anIndex ); - - Object result = notifyDelegate( - "displayGroupShouldDeleteObject", - new Class[] { WODisplayGroup.class, Object.class }, - new Object[] { this, target } ); - if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) ) - { - return false; - } - - deleteObjectAtIndexNoNotify( anIndex ); - - if ( dataSource != null ) - { - dataSource.deleteObject( target ); - } - - notifyDelegate( - "displayGroupDidDeleteObject", - new Class[] { WODisplayGroup.class, Object.class }, - new Object[] { this, target } ); - - return true; - } - - private void deleteObjectAtIndexNoNotify ( int anIndex ) - { - Object target = displayedObjects.objectAtIndex( anIndex ); - - int i; - - // remove from selected objects if necessary - i = indexOf( selectedObjects, target ); - if ( i != NSArray.NotFound ) - { - selectionChanged = true; - willChange(); // notify before removing - selectedObjects.removeObjectAtIndex( i ); - selectedIndexes.remove( new Integer( i ) ); // comps by value - } - else // notify - no selection change needed - { - willChange(); - } - - // remove from all objects - i = indexOf( allObjects, target ); - if ( i != NSArray.NotFound ) - { - allObjects.removeObjectAtIndex( i ); - } - else // otherwise should never happen - { + * WODisplayGroup manages a set of objects, allowing them to be sorted, batched, + * and filtered. WODisplay also acts as a bridge to the wotonomy's control + * package, including WODisplayGroup and EOEditingContext. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 905 $ + */ +public class WODisplayGroup extends Observable implements EOObserving, EOEditingContext.Editor, java.io.Serializable { + /** + * Notification sent when the display group is about to fetch. + */ + public static final String DisplayGroupWillFetchNotification = "DisplayGroupWillFetchNotification"; + + private static boolean globalDefaultForValidatesChangesImmediately = true; + private static String globalDefaultStringMatchFormat = "caseInsensitiveLike"; + private static String globalDefaultStringMatchOperator = "%@*"; + + protected NSMutableArray allObjects; + protected NSArray allObjectsProxy; + protected NSMutableArray displayedObjects; + protected NSArray displayedObjectsProxy; + protected NSMutableArray selectedObjects; + protected NSArray selectedObjectsProxy; + protected NSMutableArray selectedIndexes; + + private String defaultStringMatchOperator; + private String defaultStringMatchFormat; + + private boolean validatesChangesImmediately; + private Object delegate; + private EODataSource dataSource; + private EOQualifier qualifier; + private NSMutableArray sortOrderings; + private NSArray sortOrderingsProxy; + + private NSArray localKeys; + private NSDictionary insertedObjectDefaultValues; + private boolean fetchesOnLoad; + private boolean selectsFirstObjectAfterFetch; + private boolean usesOptimisticRefresh; + private boolean inQueryMode; + + // change detection: package access for helper classes + boolean selectionChanged; + int updatedObjectIndex; + + // batching + private int batchIndex; + private int batchSize; + + // this property is not in the spec + private boolean compareByReference = false; + + private EOObserving lastGroupObserver; + + /** + * Creates a new display group. + */ + public WODisplayGroup() { + validatesChangesImmediately = globalDefaultForValidatesChangesImmediately(); + defaultStringMatchOperator = globalDefaultStringMatchFormat(); + defaultStringMatchFormat = globalDefaultStringMatchOperator(); + + allObjects = new ObservableArray(this); + allObjectsProxy = NSArray.arrayBackedByList(allObjects); + displayedObjects = new NSMutableArray(); + displayedObjectsProxy = NSArray.arrayBackedByList(displayedObjects); + selectedObjects = new NSMutableArray(); + selectedObjectsProxy = NSArray.arrayBackedByList(selectedObjects); + sortOrderings = new NSMutableArray(); + sortOrderingsProxy = NSArray.arrayBackedByList(sortOrderings); + selectedIndexes = new NSMutableArray(); + + delegate = null; + dataSource = null; + qualifier = null; + + localKeys = new NSArray(); // not implemented + insertedObjectDefaultValues = new NSDictionary(); + fetchesOnLoad = false; // not implemented + selectsFirstObjectAfterFetch = false; + usesOptimisticRefresh = false; + inQueryMode = false; // not implemented + + selectionChanged = false; + updatedObjectIndex = -1; + + batchIndex = 0; + batchSize = 0; + } + + // specify optional data source + + /** + * Sets the data source that will be used by this display group. + */ + public void setDataSource(EODataSource aDataSource) { + if ((dataSource != null) && (dataSource.editingContext() != null)) { + // un-register for notifications from existing parent store + NSNotificationCenter.defaultCenter().removeObserver(this, null, dataSource.editingContext()); + dataSource.editingContext().removeEditor(this); + if (dataSource.editingContext().messageHandler() == this) { + dataSource.editingContext().setMessageHandler(null); + } + + } + + dataSource = aDataSource; + + if ((dataSource != null) && (dataSource.editingContext() != null)) { + // register for notifications from parent store + NSNotificationCenter.defaultCenter().addObserver(this, + new NSSelector("objectsInvalidatedInEditingContext", new Class[] { NSNotification.class }), null, + dataSource.editingContext()); + + // add ourselves as editor + dataSource.editingContext().addEditor(this); + + // add ourselves as message handler if no such handler exists + if (dataSource.editingContext().messageHandler() == null) { + dataSource.editingContext().setMessageHandler(this); + } + } + } + + /** + * Returns the current data source backing this display group, or null if no + * dataSource is currently used. + */ + public EODataSource dataSource() { + return dataSource; + } + + /** + * Returns the key by which this display group is bound a master display group, + * or null if this is not a detail display group. + */ + public String detailKey() { + if (dataSource instanceof PropertyDataSource) { + return ((PropertyDataSource) dataSource).key(); + } + return null; + } + + /** + * Sets the key by which this display group is bound a master display group. + * Does nothing if this is not a detail display group. + */ + public void setDetailKey(String aKey) { + if (dataSource instanceof PropertyDataSource) { + ((PropertyDataSource) dataSource).setKey(aKey); + } + } + + /** + * Returns whether the data source is a detail data source, suggesting that this + * is a detail display group. + */ + public boolean hasDetailDataSource() { + return (dataSource instanceof PropertyDataSource); + } + + /** + * Returns the selected object in the master display group, or null if this is + * not a detail display group. + */ + public Object masterObject() { + if (dataSource instanceof PropertyDataSource) { + return ((PropertyDataSource) dataSource).source(); + } + return null; + } + + /** + * Sets the master object in the detail data source. Does nothing if there is no + * detail data source. + */ + public void setMasterObject(Object anObject) { + if (dataSource instanceof PropertyDataSource) { + ((PropertyDataSource) dataSource).setSource(anObject); + } + } + + // specify optional delegate + + /** + * Sets the display group delegate that will be used by this display group. + */ + public void setDelegate(Object aDelegate) { + delegate = aDelegate; + } + + /** + * Returns the current delegate for this display group, or null if no delegate + * is currently set. + */ + public Object delegate() { + return delegate; + } + + // display group configuration + + /** + * Returns the current string matching format. If not set, defaults to "%@*". + */ + public String defaultStringMatchFormat() { + return defaultStringMatchFormat; + } + + /** + * Returns the current string matching operator. If not set, defaults to + * "caseInsensitiveLike". + */ + public String defaultStringMatchOperator() { + return defaultStringMatchOperator; + } + + /** + * Sets the display group and associations to edit a "query by example" query + * object. This method is used for target/action connections. + */ + public void enterQueryMode(Object aSender) { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns whether this display group should immediate fetch when loaded. + */ + public boolean fetchesOnLoad() { + return fetchesOnLoad; + } + + /** + * Returns whether this display group is in "query by example" mode. + */ + public boolean inQueryMode() { + return inQueryMode; + } + + /** + * Returns a Map of default values that are applied to new objects that are + * inserted into the list. + */ + public NSDictionary insertedObjectDefaultValues() { + return insertedObjectDefaultValues; + } + + /** + * Returns the keys that were declared when read from an external resource file. + */ + public NSArray localKeys() { + return localKeys; + } + + /** + * Sets whether this display group will select the first object in the list + * after a fetch. + */ + public boolean selectsFirstObjectAfterFetch() { + return selectsFirstObjectAfterFetch; + } + + /** + * Sets the default string matching format that will be used by this display + * group. + */ + public void setDefaultStringMatchFormat(String aFormat) { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Sets the default string matching operator that will be used by this display + * group. + */ + public void setDefaultStringMatchOperator(String anOperator) { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Sets whether this display group will fetch objects from its data source on + * load. + */ + public void setFetchesOnLoad(boolean willFetch) { + fetchesOnLoad = willFetch; + } + + /** + * Sets whether this display group is in "query by example" mode. If true, all + * associations will bind to a special "example" object. + */ + public void setInQueryMode(boolean isInQueryMode) { + inQueryMode = isInQueryMode; + } + + /** + * Sets the mapping that contains the values that will be applied to new objects + * inserted into the display group. + */ + public void setInsertedObjectDefaultValues(Map aMap) { + insertedObjectDefaultValues = new NSDictionary(aMap); + } + + /** + * Sets the keys that are declared when instantiated from an external resource + * file. + */ + public void setLocalKeys(List aKeyList) { + localKeys = new NSArray((Collection) aKeyList); + } + + /** + * Sets whether the first object in the list will be selected after a fetch. + */ + public void setSelectsFirstObjectAfterFetch(boolean selectsFirst) { + selectsFirstObjectAfterFetch = selectsFirst; + } + + /** + * Sets the order of the keys by which this display group will be ordered after + * a fetch or after a call to updateDisplayedObjects(). The elements in the + * display group will be sorted first by the first key, within the first key, by + * the second key, and so on. + */ + public void setSortOrderings(List aList) { + sortOrderings.removeAllObjects(); + + Object o; + Iterator it = aList.iterator(); + while (it.hasNext()) { + o = it.next(); + // handle the convenience of specifying just a key + if (!(o instanceof EOSortOrdering)) { + o = new EOSortOrdering(o.toString(), EOSortOrdering.CompareAscending); + } + sortOrderings.add(o); + } + } + + /** + * Sets whether only changed objects are refreshed (optimistic), or whether all + * objects are refreshed (pessimistic, default). By default, when the display + * group receives notification that one of its objects has changed, + * updateDisplayedObjects is called. + */ + public void setUsesOptimisticRefresh(boolean isOptimistic) { + usesOptimisticRefresh = isOptimistic; + } + + /** + * Sets whether changes made by associations are validated immediately, or when + * changes are saved. + */ + public void setValidatesChangesImmediately(boolean validatesImmediately) { + validatesChangesImmediately = validatesImmediately; + } + + /** + * Returns a read-only List of sort orderings for this display group. + */ + public NSArray sortOrderings() { + return sortOrderingsProxy; + } + + /** + * Returns whether this display group refreshes only the changed objects or all + * objects on refresh. + */ + public boolean usesOptimisticRefresh() { + return usesOptimisticRefresh; + } + + /** + * Returns whether this display group validates changes immediately. Otherwise, + * validation should occur when changes are saved. Default is the global + * default, which is initially true. + */ + public boolean validatesChangesImmediately() { + return validatesChangesImmediately; + } + + // qualification + + /** + * Returns a qualifier that will be applied all the objects in this display + * group to determine which objects will be displayed. + */ + public EOQualifier qualifier() { + return qualifier; + } + + /** + * Returns a new qualifier built from the three query value maps: greater than, + * equal to, and less than. + */ + public EOQualifier qualifierFromQueryValues() { + // TODO: assemble qualifier from query values + + return new EOQualifier() { + // use inner class until we actually implement one + public EOQualifier qualifierWithBindings(Map aMap, boolean requireAll) { + return null; + } + + public Throwable validateKeysWithRootClassDescription(Class aClass) { + return null; + } + + public boolean evaluateWithObject(Object o) { + return false; + } + }; + } + + /** + * Calls qualifierFromQueryValues(), applies the result to the data source, and + * calls fetch(). + */ + public void qualifyDataSource() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Calls qualifierFromQueryValues(), sets the qualifier with setQualifier(), and + * calls updateDisplayedObjects(). + */ + public void qualifyDisplayGroup() { + setQualifier(qualifierFromQueryValues()); + updateDisplayedObjects(); + } + + /** + * Returns a Map containing the mappings of keys to binding query values. + */ + public NSDictionary queryBindingValues() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns a Map containing the mappings of keys to binding query values. + */ + public NSMutableDictionary queryBindings() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns a Map containing the mappings of keys to binding query values for a + * matching query. + */ + public NSMutableDictionary queryMatch() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns a Map containing the mappings of keys to binding query values for a + * minimum value query. + */ + public NSMutableDictionary queryMin() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns a Map containing the mappings of keys to binding query values for a + * maximum value query. + */ + public NSMutableDictionary queryMax() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns a Map containing the mappings of keys to operator values. + */ + public NSMutableDictionary queryOperator() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns a list containing all supported qualifier operators. + */ + public NSArray allQualifierOperators() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns a Map containing the mappings of keys to operator values. + */ + public NSDictionary queryOperatorValues() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Sets the qualifier that will be used by updateDisplayedObjects() to filter + * displayed objects. + */ + public void setQualifier(EOQualifier aQualifier) { + qualifier = aQualifier; + } + + /** + * Sets the mapping that contains the mappings of keys to binding values. + */ + public void setQueryBindingValues(Map aMap) { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Sets the mapping that contains the mappings of keys to operator values. + */ + public void setQueryOperatorValues(Map aMap) { + throw new RuntimeException("Not implemented yet."); + } + + // qualifier query values + + /** + * Returns a Map containing the mappings of keys to query values that will be + * used to test for equality. + */ + public NSDictionary equalToQueryValues() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns a Map containing the mappings of keys to query values that will be + * used to test for greater value. + */ + public NSDictionary greaterThanQueryValues() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Returns a Map containing the mappings of keys to query values that will be + * used to test for lesser value. + */ + public NSDictionary lessThanQueryValues() { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Sets the Map that contains the mappings of keys to query values that will be + * used to test for equality. + */ + public void setEqualToQueryValues(Map aMap) { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Sets the mapping that contains the mappings of keys to query values that will + * be used to test for greater value. + */ + public void setGreaterThanQueryValues(Map aMap) { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Sets the mapping that contains the mappings of keys to query values that will + * be used to test for lesser value. + */ + public void setLessThanQueryValues(Map aMap) { + throw new RuntimeException("Not implemented yet."); + } + + /** + * Deprecated: returns true. + */ + public boolean endEditing() { + return true; + } + + // object management + + /** + * Returns a read-only List containing all objects managed by the display group. + * This includes those objects not visible due to disqualification. + */ + public NSArray allObjects() { // System.out.println( "avoided allocation: allObjects" ); + return allObjectsProxy; + } + + /** + * Returns the total number of batches held by this display group. + */ + public int batchCount() { + if (batchSize < 1) + return 1; + int count = displayedObjects.count(); + if (count == 0) + return 1; + return ((count - 1) / batchSize) + 1; + } + + /** + * Returns the index of the currently displayed batch. + */ + public int currentBatchIndex() { + return batchIndex; + } + + /** + * Sets the index of the currently displayed batch. + */ + public void setCurrentBatchIndex(int aBatchIndex) { + batchIndex = aBatchIndex; + updateDisplayedObjects(); + } + + /** + * Sets the displayed objects to the batch containing the first selected object, + * and updates the current batch index, and returns null to force a page reload. + * Displays the first batch is there is no selection. + */ + public Object displayBatchContainingSelectedObject() { + if (batchSize < 1) + return null; + NSArray indexes = selectionIndexes(); + if (indexes.count() > 0) { + batchIndex = ((Number) indexes.objectAtIndex(0)).intValue() / batchSize; + } else { + batchIndex = 0; + } + updateDisplayedObjects(); + return null; + } + + /** + * Sets the displayed objects to the next batch and updates the current batch + * index, and returns null to force a page reload. If there is no next batch the + * first batch is displayed. + */ + public Object displayNextBatch() { + batchIndex = (batchIndex + 1) % batchCount(); + updateDisplayedObjects(); + return null; + } + + /** + * Sets the displayed objects to the next batch and updates the current batch + * index, and returns null to force a page reload. If there is no previous + * batch, the last batch is displayed. + */ + public Object displayPreviousBatch() { + batchIndex--; + if (batchIndex < 0) + batchIndex = batchCount() - 1; + updateDisplayedObjects(); + return null; + } + + /** + * Returns whether the displayed objects have been batched. + */ + public boolean hasMultipleBatches() { + return batchCount() > 1; + } + + /** + * Returns the one-based index within the displayed objects list of the first + * displayed object in the current batch. + */ + public int indexOfFirstDisplayedObject() { + if (batchSize < 1) + return 1; + return batchIndex * batchSize + 1; + } + + /** + * Returns the one-based index within the displayed objects list of the first + * last object in the current batch. + */ + public int indexOfLastDisplayedObject() { + if (batchSize < 1) + return displayedObjects.count(); + return Math.min(((batchIndex + 1) * batchSize), displayedObjects.count()); + } + + /** + * Returns the number of objects per batch. + */ + public int numberOfObjectsPerBatch() { + return batchSize; + } + + /** + * Returns the number of objects per batch. + */ + public void setNumberOfObjectsPerBatch(int aSize) { + batchSize = aSize; + updateDisplayedObjects(); + } + + /** + * Clears the current selection. + * + * @return True is the selection was cleared, False if the selection could not + * be cleared + * @see #setSelectionIndexes + */ + public boolean clearSelection() { + Object result = notifyDelegate("displayGroupShouldChangeSelection", + new Class[] { WODisplayGroup.class, List.class }, new Object[] { this, new NSArray(selectedObjects) }); + if ((result != null) && (Boolean.FALSE.equals(result))) { + return false; + } + + selectionChanged = true; + willChange(); + + selectedObjects.removeAllObjects(); + selectedIndexes.removeAllObjects(); + + notifyDelegate("displayGroupDidChangeSelection", new Class[] { WODisplayGroup.class }, new Object[] { this }); + notifyDelegate("displayGroupDidChangeSelectedObjects", new Class[] { WODisplayGroup.class }, + new Object[] { this }); + + return true; + } + + /** + * Convenience for binding to a component action: calls deleteSelection() and + * then displayBatchContainingSelectedObject() and returns null, which is a + * suitable result from a component action. + */ + public Object delete() { + deleteSelection(); + displayBatchContainingSelectedObject(); + return null; + } + + /** + * Deletes the object at the specified index, notifying the delegate before and + * after the operation, and then updating the selection if needed. + * + * @return True if delete was successful, false if the object was not deleted. + */ + public boolean deleteObjectAtIndex(int anIndex) { + Object target = displayedObjects.objectAtIndex(anIndex); + + Object result = notifyDelegate("displayGroupShouldDeleteObject", + new Class[] { WODisplayGroup.class, Object.class }, new Object[] { this, target }); + if ((result != null) && (Boolean.FALSE.equals(result))) { + return false; + } + + deleteObjectAtIndexNoNotify(anIndex); + + if (dataSource != null) { + dataSource.deleteObject(target); + } + + notifyDelegate("displayGroupDidDeleteObject", new Class[] { WODisplayGroup.class, Object.class }, + new Object[] { this, target }); + + return true; + } + + private void deleteObjectAtIndexNoNotify(int anIndex) { + Object target = displayedObjects.objectAtIndex(anIndex); + + int i; + + // remove from selected objects if necessary + i = indexOf(selectedObjects, target); + if (i != NSArray.NotFound) { + selectionChanged = true; + willChange(); // notify before removing + selectedObjects.removeObjectAtIndex(i); + selectedIndexes.remove(new Integer(i)); // comps by value + } else // notify - no selection change needed + { + willChange(); + } + + // remove from all objects + i = indexOf(allObjects, target); + if (i != NSArray.NotFound) { + allObjects.removeObjectAtIndex(i); + } else // otherwise should never happen + { // throw new WotonomyException( // "Displayed object not found in allObjects" ); - } - - // remove from displayed objects - displayedObjects.removeObjectAtIndex( anIndex ); - } - - /** - * Deletes the currently selected objects. - * This implementation calls deleteObjectAtIndex() for - * each index in the selection list, immediately returning - * false if any delete operation fails. - * @return True if all selected objects were deleted, - * false if any deletion failed. - */ - public boolean deleteSelection() - { - int i; - boolean result = true; - - Enumeration e = new NSArray( selectedObjects ).objectEnumerator(); - while ( e.hasMoreElements() ) - { - i = indexOf( displayedObjects, e.nextElement() ); - if ( i == NSArray.NotFound ) - { - // should never happen - throw new WotonomyException( - "Selected object not found in displayedObjects" ); - } - result = result && deleteObjectAtIndex( i ); - } - - return result; - } - - /** - * Returns a read-only List of all objects in the display group - * that are currently displayed by the associations. - */ - public NSArray displayedObjects() - { // System.out.println( "avoided allocation: displayedObjects" ); - if ( batchSize < 1 ) return displayedObjectsProxy; - return displayedObjectsProxy.subarrayWithRange( - new NSRange( batchIndex * batchSize, batchSize ) ); - } - - /** - * Requests a list of objects from the DataSource - * and calls setObjectArray to populate the list. - * More specifically, calls endEditing(), asks the - * delegate, fetches the objects, notifies the delegate, - * and populates the list. Returns null to force a - * page reload. - */ - public Object fetch() - { - endEditing(); - - if ( dataSource == null ) - { - return null; - } - - Object result = notifyDelegate( - "displayGroupShouldFetch", - new Class[] { WODisplayGroup.class }, - new Object[] { this } ); - if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) ) - { - return null; - } - - NSNotificationCenter.defaultCenter().postNotification( - DisplayGroupWillFetchNotification, this, new NSDictionary() ); - - NSArray objectList = dataSource.fetchObjects(); - - notifyDelegate( - "displayGroupDidFetchObjects", - new Class[] { WODisplayGroup.class, List.class }, - new Object[] { this, objectList } ); - - setObjectArray( objectList ); - - if ( ( selectsFirstObjectAfterFetch ) && ( displayedObjects.size() > 0 ) ) - { - setSelectionIndexes( new NSArray( new Integer( 0 ) ) ); - } - - return null; - } - - /** - * Convenience to call insertNewObjectAtIndex with the current selection plus one, - * or at the end of the list if there is no selection. - * Returns null to force a page reload. - */ - public Object insert() - { - NSArray indexes = selectionIndexes(); - int size = indexes.count(); - if ( size == 0 ) - { - insertNewObjectAtIndex( displayedObjects.count() ); - } - else - { - insertNewObjectAtIndex( - ((Number)selectedIndexes.objectAtIndex( size-1 )).intValue()+1 ); - } - return null; - } - - /** - * Creates a new object at the specified index. - * Calls insertObjectAtIndex() with the result - * from sending createObject() to the data source. - * @return the newly created object. - */ - public Object insertNewObjectAtIndex ( int anIndex ) - { - Object result = null; - if ( dataSource != null ) - { - result = dataSource.createObject(); - } - if ( result != null ) - { - if ( insertedObjectDefaultValues != null ) - { - Duplicator.writePropertiesForObject( - insertedObjectDefaultValues, result ); - } - insertObjectAtIndex( result, anIndex ); - } - else // create failed - { - if ( delegate() != null ) - { - NSSelector selector = new NSSelector( - "displayGroupCreateObjectFailed", - new Class[] { WODisplayGroup.class, EODataSource.class } ); - if ( selector.implementedByObject( delegate() ) ) - { - try - { - selector.invoke( delegate(), new Object[] { this, dataSource } ); - return result; - } - catch ( Exception exc ) - { - System.err.println( "Error notifying delegate: displayGroupCreateObjectFailed" ); - exc.printStackTrace(); - } - } - } - } - return result; - } - - /** - * Inserts the specified object into the list at - * the specified index. - */ - public void insertObjectAtIndex ( Object anObject, int anIndex ) - { - Object result = notifyDelegate( - "displayGroupShouldInsertObject", - new Class[] { WODisplayGroup.class, Object.class, int.class }, - new Object[] { this, anObject, new Integer(anIndex) } ); - if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) ) - { - return; - } - - updatedObjectIndex = anIndex; - willChange(); - - int i; - - // add to all objects - if ( anIndex == displayedObjects.size() ) - { - allObjects.addObject( anObject ); - } - else // insert before same object - { - Object target = displayedObjects.objectAtIndex( anIndex ); - int targetIndex = indexOf( allObjects, target ); - if ( targetIndex != NSArray.NotFound ) - { - allObjects.insertObjectAtIndex( anObject, targetIndex ); - } - else // should never happen - { - throw new WotonomyException( - "Could not find displayed object in all objects list: " - + target ); - } - } - - // add to displayed objects - displayedObjects.insertObjectAtIndex( anObject, anIndex ); - - if ( dataSource != null ) - { - if ( dataSource instanceof OrderedDataSource ) - { - ((OrderedDataSource)dataSource).insertObjectAtIndex( - anObject, anIndex ); - } - else - { - dataSource.insertObject( anObject ); - } - } - - notifyDelegate( - "displayGroupDidInsertObject", - new Class[] { WODisplayGroup.class, Object.class }, - new Object[] { this, anObject } ); - } - - /** - * Sets contentsChanged to true and notifies all observers. - */ - public void redisplay() - { - willChange(); - } - - /** - * Sets the selection to the next displayed object after the current - * selection. If the last object is selected, or if no object - * is selected, then the first object becomes selected. - * If multiple items are selected, the first selected item is - * considered the selected item for the purposes of this method. - * Does not call redisplay(). - * @return null to force a page reload. - */ - public Object selectNext() - { - int count = displayedObjects.count(); - if ( count == 0 ) return null; - if ( count == 1 ) - { - selectObject( displayedObjects.objectAtIndex( 0 ) ); - return null; - } - - int i = -1; - Object selectedObject = selectedObject(); - if ( selectedObject != null ) - { - i = indexOf( displayedObjects, selectedObject ); - } - if ( i == NSArray.NotFound ) i = -1; - - // select next object - i++; - if ( i != displayedObjects.count() ) - { - // set to next object - selectedObject = displayedObjects.objectAtIndex( i ); - } - else // out of range - { - // set to null - selectedObject = displayedObjects.objectAtIndex( 0 ); - } - - selectObject( selectedObject ); - return null; - } - - /** - * Sets the selection to the specified object. - * If the specified object is null or does not exist - * in the list of displayed objects, the selection - * will be cleared. - * @return true if the object was selected. - */ - public boolean selectObject ( Object anObject ) - { - if ( ( anObject == null ) || - ( indexOf( displayedObjects, anObject ) - == NSArray.NotFound ) ) - { - clearSelection(); - return false; - } - - selectObjectsIdenticalTo( new NSArray( new Object[] { anObject } ) ); - return true; - } - - /** - * Sets the selection to the specified objects. - * If the specified list is null or if none of the objects - * in the list exist in the list of displayed objects, the - * selection will be cleared. - * @return true if all specified objects were selected. - */ - public boolean selectObjectsIdenticalTo ( List anObjectList ) - { - // optimization: check for resetting of selection - if ( ( anObjectList != null ) && ( selectedObjects.size() == anObjectList.size() ) ) - { - boolean identical = true; - int size = selectedObjects.size(); - for ( int i = 0; ( i < size ) && identical; i++ ) - { - // compare by reference - if ( anObjectList.get( i ) != selectedObjects.get( i ) ) - { - identical = false; - } - else if ( displayedObjects.indexOfIdenticalObject( - anObjectList.get( i ) ) == NSArray.NotFound ) - { - identical = false; - } - } - if ( identical ) - { - return true; - } - } - - Object result = notifyDelegate( - "displayGroupShouldChangeSelection", - new Class[] { WODisplayGroup.class, List.class }, - new Object[] { this, anObjectList } ); - if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) ) - { - // need to notify the calling component - // to revert back to the previous selection - selectionChanged = true; - willChange(); - return false; - } - - int i; - selectionChanged = true; - willChange(); - Object o; - selectedObjects.removeAllObjects(); - selectedIndexes.removeAllObjects(); - Iterator it = anObjectList.iterator(); - while ( it.hasNext() ) - { - o = it.next(); - if ( ( i = displayedObjects.indexOfIdenticalObject( o ) ) - != NSArray.NotFound ) - { - selectedObjects.addObject( o ); - selectedIndexes.addObject( new Integer( i ) ); - } - } - - notifyDelegate( - "displayGroupDidChangeSelection", - new Class[] { WODisplayGroup.class }, - new Object[] { this } ); - notifyDelegate( - "displayGroupDidChangeSelectedObjects", - new Class[] { WODisplayGroup.class }, - new Object[] { this } ); - - return true; - } - - /** - * Calls selectObjectsIdenticalTo and if false is returned - * and selectFirstIfNoMatch is true, selects the first object. - */ - public boolean selectObjectsIdenticalToSelectFirstOnNoMatch( - List anObjectList, boolean selectFirstIfNoMatch ) - { - if ( selectObjectsIdenticalTo( anObjectList ) ) - { - return true; - } - if ( selectFirstIfNoMatch ) - { - clearSelection(); - selectNext(); - return true; - } - return false; - } - - /** - * Sets the selection to the previous displayed object before the current - * selection. If the first object is selected, or if no object - * is selected, then the last object becomes selected. - * If multiple items are selected, the first selected item is - * considered the selected item for the purposes of this method. - * Does not call redisplay(). - * @return null to force a page reload. - */ - public Object selectPrevious() - { - int i = displayedObjects.count(); - if ( i == 0 ) return null; - if ( i == 1 ) - { - selectObject( displayedObjects.objectAtIndex( 0 ) ); - return null; - } - - Object selectedObject = selectedObject(); - if ( selectedObject != null ) - { - i = indexOf( displayedObjects, selectedObject ); - } - if ( i == NSArray.NotFound ) i = displayedObjects.count(); - - // select next object - i--; - if ( i < 0 ) - { - // out of range - select last object - i = displayedObjects.count() - 1; - } - - selectObject( displayedObjects.objectAtIndex( i ) ); - return null; - } - - /** - * Returns the currently selected object, or null if - * there is no selection. - */ - public Object selectedObject() - { - if ( selectedObjects.count() == 0 ) - { - return null; - } - return selectedObjects.objectAtIndex( 0 ); - } - - /** - * Returns a read-only List containing all selected objects, if any. - * Returns an empty list if no objects are selected. - */ - public NSArray selectedObjects() - { // System.out.println( "avoided allocation: selectedObjects" ); - return selectedObjectsProxy; - } - - /** - * Returns a read-only List containing the indexes of all selected - * objects, if any. The list contains instances of - * java.lang.Number; call intValue() to retrieve the index. - */ - public NSArray selectionIndexes() - { + } + + // remove from displayed objects + displayedObjects.removeObjectAtIndex(anIndex); + } + + /** + * Deletes the currently selected objects. This implementation calls + * deleteObjectAtIndex() for each index in the selection list, immediately + * returning false if any delete operation fails. + * + * @return True if all selected objects were deleted, false if any deletion + * failed. + */ + public boolean deleteSelection() { + int i; + boolean result = true; + + Enumeration e = new NSArray(selectedObjects).objectEnumerator(); + while (e.hasMoreElements()) { + i = indexOf(displayedObjects, e.nextElement()); + if (i == NSArray.NotFound) { + // should never happen + throw new WotonomyException("Selected object not found in displayedObjects"); + } + result = result && deleteObjectAtIndex(i); + } + + return result; + } + + /** + * Returns a read-only List of all objects in the display group that are + * currently displayed by the associations. + */ + public NSArray displayedObjects() { // System.out.println( "avoided allocation: displayedObjects" ); + if (batchSize < 1) + return displayedObjectsProxy; + return displayedObjectsProxy.subarrayWithRange(new NSRange(batchIndex * batchSize, batchSize)); + } + + /** + * Requests a list of objects from the DataSource and calls setObjectArray to + * populate the list. More specifically, calls endEditing(), asks the delegate, + * fetches the objects, notifies the delegate, and populates the list. Returns + * null to force a page reload. + */ + public Object fetch() { + endEditing(); + + if (dataSource == null) { + return null; + } + + Object result = notifyDelegate("displayGroupShouldFetch", new Class[] { WODisplayGroup.class }, + new Object[] { this }); + if ((result != null) && (Boolean.FALSE.equals(result))) { + return null; + } + + NSNotificationCenter.defaultCenter().postNotification(DisplayGroupWillFetchNotification, this, + new NSDictionary()); + + NSArray objectList = dataSource.fetchObjects(); + + notifyDelegate("displayGroupDidFetchObjects", new Class[] { WODisplayGroup.class, List.class }, + new Object[] { this, objectList }); + + setObjectArray(objectList); + + if ((selectsFirstObjectAfterFetch) && (displayedObjects.size() > 0)) { + setSelectionIndexes(new NSArray(new Integer(0))); + } + + return null; + } + + /** + * Convenience to call insertNewObjectAtIndex with the current selection plus + * one, or at the end of the list if there is no selection. Returns null to + * force a page reload. + */ + public Object insert() { + NSArray indexes = selectionIndexes(); + int size = indexes.count(); + if (size == 0) { + insertNewObjectAtIndex(displayedObjects.count()); + } else { + insertNewObjectAtIndex(((Number) selectedIndexes.objectAtIndex(size - 1)).intValue() + 1); + } + return null; + } + + /** + * Creates a new object at the specified index. Calls insertObjectAtIndex() with + * the result from sending createObject() to the data source. + * + * @return the newly created object. + */ + public Object insertNewObjectAtIndex(int anIndex) { + Object result = null; + if (dataSource != null) { + result = dataSource.createObject(); + } + if (result != null) { + if (insertedObjectDefaultValues != null) { + Duplicator.writePropertiesForObject(insertedObjectDefaultValues, result); + } + insertObjectAtIndex(result, anIndex); + } else // create failed + { + if (delegate() != null) { + NSSelector selector = new NSSelector("displayGroupCreateObjectFailed", + new Class[] { WODisplayGroup.class, EODataSource.class }); + if (selector.implementedByObject(delegate())) { + try { + selector.invoke(delegate(), new Object[] { this, dataSource }); + return result; + } catch (Exception exc) { + System.err.println("Error notifying delegate: displayGroupCreateObjectFailed"); + exc.printStackTrace(); + } + } + } + } + return result; + } + + /** + * Inserts the specified object into the list at the specified index. + */ + public void insertObjectAtIndex(Object anObject, int anIndex) { + Object result = notifyDelegate("displayGroupShouldInsertObject", + new Class[] { WODisplayGroup.class, Object.class, int.class }, + new Object[] { this, anObject, new Integer(anIndex) }); + if ((result != null) && (Boolean.FALSE.equals(result))) { + return; + } + + updatedObjectIndex = anIndex; + willChange(); + + int i; + + // add to all objects + if (anIndex == displayedObjects.size()) { + allObjects.addObject(anObject); + } else // insert before same object + { + Object target = displayedObjects.objectAtIndex(anIndex); + int targetIndex = indexOf(allObjects, target); + if (targetIndex != NSArray.NotFound) { + allObjects.insertObjectAtIndex(anObject, targetIndex); + } else // should never happen + { + throw new WotonomyException("Could not find displayed object in all objects list: " + target); + } + } + + // add to displayed objects + displayedObjects.insertObjectAtIndex(anObject, anIndex); + + if (dataSource != null) { + if (dataSource instanceof OrderedDataSource) { + ((OrderedDataSource) dataSource).insertObjectAtIndex(anObject, anIndex); + } else { + dataSource.insertObject(anObject); + } + } + + notifyDelegate("displayGroupDidInsertObject", new Class[] { WODisplayGroup.class, Object.class }, + new Object[] { this, anObject }); + } + + /** + * Sets contentsChanged to true and notifies all observers. + */ + public void redisplay() { + willChange(); + } + + /** + * Sets the selection to the next displayed object after the current selection. + * If the last object is selected, or if no object is selected, then the first + * object becomes selected. If multiple items are selected, the first selected + * item is considered the selected item for the purposes of this method. Does + * not call redisplay(). + * + * @return null to force a page reload. + */ + public Object selectNext() { + int count = displayedObjects.count(); + if (count == 0) + return null; + if (count == 1) { + selectObject(displayedObjects.objectAtIndex(0)); + return null; + } + + int i = -1; + Object selectedObject = selectedObject(); + if (selectedObject != null) { + i = indexOf(displayedObjects, selectedObject); + } + if (i == NSArray.NotFound) + i = -1; + + // select next object + i++; + if (i != displayedObjects.count()) { + // set to next object + selectedObject = displayedObjects.objectAtIndex(i); + } else // out of range + { + // set to null + selectedObject = displayedObjects.objectAtIndex(0); + } + + selectObject(selectedObject); + return null; + } + + /** + * Sets the selection to the specified object. If the specified object is null + * or does not exist in the list of displayed objects, the selection will be + * cleared. + * + * @return true if the object was selected. + */ + public boolean selectObject(Object anObject) { + if ((anObject == null) || (indexOf(displayedObjects, anObject) == NSArray.NotFound)) { + clearSelection(); + return false; + } + + selectObjectsIdenticalTo(new NSArray(new Object[] { anObject })); + return true; + } + + /** + * Sets the selection to the specified objects. If the specified list is null or + * if none of the objects in the list exist in the list of displayed objects, + * the selection will be cleared. + * + * @return true if all specified objects were selected. + */ + public boolean selectObjectsIdenticalTo(List anObjectList) { + // optimization: check for resetting of selection + if ((anObjectList != null) && (selectedObjects.size() == anObjectList.size())) { + boolean identical = true; + int size = selectedObjects.size(); + for (int i = 0; (i < size) && identical; i++) { + // compare by reference + if (anObjectList.get(i) != selectedObjects.get(i)) { + identical = false; + } else if (displayedObjects.indexOfIdenticalObject(anObjectList.get(i)) == NSArray.NotFound) { + identical = false; + } + } + if (identical) { + return true; + } + } + + Object result = notifyDelegate("displayGroupShouldChangeSelection", + new Class[] { WODisplayGroup.class, List.class }, new Object[] { this, anObjectList }); + if ((result != null) && (Boolean.FALSE.equals(result))) { + // need to notify the calling component + // to revert back to the previous selection + selectionChanged = true; + willChange(); + return false; + } + + int i; + selectionChanged = true; + willChange(); + Object o; + selectedObjects.removeAllObjects(); + selectedIndexes.removeAllObjects(); + Iterator it = anObjectList.iterator(); + while (it.hasNext()) { + o = it.next(); + if ((i = displayedObjects.indexOfIdenticalObject(o)) != NSArray.NotFound) { + selectedObjects.addObject(o); + selectedIndexes.addObject(new Integer(i)); + } + } + + notifyDelegate("displayGroupDidChangeSelection", new Class[] { WODisplayGroup.class }, new Object[] { this }); + notifyDelegate("displayGroupDidChangeSelectedObjects", new Class[] { WODisplayGroup.class }, + new Object[] { this }); + + return true; + } + + /** + * Calls selectObjectsIdenticalTo and if false is returned and + * selectFirstIfNoMatch is true, selects the first object. + */ + public boolean selectObjectsIdenticalToSelectFirstOnNoMatch(List anObjectList, boolean selectFirstIfNoMatch) { + if (selectObjectsIdenticalTo(anObjectList)) { + return true; + } + if (selectFirstIfNoMatch) { + clearSelection(); + selectNext(); + return true; + } + return false; + } + + /** + * Sets the selection to the previous displayed object before the current + * selection. If the first object is selected, or if no object is selected, then + * the last object becomes selected. If multiple items are selected, the first + * selected item is considered the selected item for the purposes of this + * method. Does not call redisplay(). + * + * @return null to force a page reload. + */ + public Object selectPrevious() { + int i = displayedObjects.count(); + if (i == 0) + return null; + if (i == 1) { + selectObject(displayedObjects.objectAtIndex(0)); + return null; + } + + Object selectedObject = selectedObject(); + if (selectedObject != null) { + i = indexOf(displayedObjects, selectedObject); + } + if (i == NSArray.NotFound) + i = displayedObjects.count(); + + // select next object + i--; + if (i < 0) { + // out of range - select last object + i = displayedObjects.count() - 1; + } + + selectObject(displayedObjects.objectAtIndex(i)); + return null; + } + + /** + * Returns the currently selected object, or null if there is no selection. + */ + public Object selectedObject() { + if (selectedObjects.count() == 0) { + return null; + } + return selectedObjects.objectAtIndex(0); + } + + /** + * Returns a read-only List containing all selected objects, if any. Returns an + * empty list if no objects are selected. + */ + public NSArray selectedObjects() { // System.out.println( "avoided allocation: selectedObjects" ); + return selectedObjectsProxy; + } + + /** + * Returns a read-only List containing the indexes of all selected objects, if + * any. The list contains instances of java.lang.Number; call intValue() to + * retrieve the index. + */ + public NSArray selectionIndexes() { // return selectedIndexes; - int i; - NSMutableArray result = new NSMutableArray(); - Enumeration e = selectedObjects.objectEnumerator(); - while ( e.hasMoreElements() ) - { - i = indexOf( displayedObjects, e.nextElement() ); - if ( i != NSArray.NotFound ) - { - result.addObject( new Integer( i ) ); - } - else - { - System.err.println( - "Should never happen: selected objects not in displayed objects" ); - new RuntimeException().printStackTrace( System.err ); - } - } - return result; - } - - /** - * Sets the objects managed by this display group. - * updateDisplayedObjects() is called to filter the - * display objects. The previous selection will be - * maintained if possible. The data source is not - * notified. - */ - public void setObjectArray ( List anObjectList ) - { - if ( anObjectList == null ) anObjectList = new NSArray(); - - Object result = notifyDelegate( - "displayGroupDisplayArrayForObjects", - new Class[] { WODisplayGroup.class, List.class }, - new Object[] { this, anObjectList } ); - if ( result != null ) - { - anObjectList = (List) result; - } - - willChange(); - - NSArray oldSelectedObjects = new NSArray( selectedObjects ); // copy - - // reset allObjects to new list - allObjects.removeAllObjects(); - allObjects.addObjectsFromArray( anObjectList ); - - // update the displayed object list - updateDisplayedObjects(); - - // restore the selection if possible - selectObjectsIdenticalTo( oldSelectedObjects ); - - batchIndex = 0; - displayBatchContainingSelectedObject(); - } - - /** - * Sets the currently selected object, or clears the - * selection if the object is not found or is null. - * Note: it's not clear how this differs from - * selectObject in the spec. It is recommended that - * you call selectObject for now. - */ - public void setSelectedObject ( Object anObject ) - { - selectObject( anObject ); - } - - /** - * Sets the current selection to the specified objects. - * The previous selection is cleared, and any objects - * in the display group that are in the specified list - * are then selected. If no items in the specified list - * are found in the display group, then the selection is - * effectively cleared. - * Note: it's not clear how this differs from - * selectObjectsIdenticalTo in the spec. - * It is recommended that you call that method for now. - */ - public void setSelectedObjects ( List aList ) - { - selectObjectsIdenticalTo( aList ); - } - - /** - * Sets the current selection to the objects at the - * specified indexes. Items in the list are assumed - * to be instances of java.lang.Number. - * The previous selection is cleared, and any objects - * in the display group that are in the specified list - * are then selected. If no items in the specified list - * are found in the display group, then the selection is - * effectively cleared. - */ - public boolean setSelectionIndexes ( List aList ) - { - Object o; - int index; - NSMutableArray objects = new NSMutableArray(); - Iterator it = aList.iterator(); - while ( it.hasNext() ) - { - index = ((Number)it.next()).intValue(); - if ( index < displayedObjects.count() ) - { - o = displayedObjects.objectAtIndex( index ); - if ( o != null ) - { - objects.add( o ); - } - } - } - return selectObjectsIdenticalTo( objects ); - } - - /** - * Applies the qualifier to all objects and sorts - * the results to update the list of displayed objects. - * Observing associations are notified to reflect the changes. - */ - public void updateDisplayedObjects() - { - updatedObjectIndex = -1; - willChange(); - - displayedObjects.removeAllObjects(); - - displayedObjects.addObjectsFromArray( allObjects ); - - // apply qualifier, if any - if ( qualifier != null ) - { - EOQualifier.filterArrayWithQualifier( - displayedObjects, qualifier ); - } - - // apply sort orderings, if any - NSArray orderings = sortOrderings(); - if ( orderings != null ) - { - if ( orderings.count() > 0 ) - { - selectionChanged = true; - willChange(); - EOSortOrdering.sortArrayUsingKeyOrderArray( - displayedObjects, orderings ); - } - } - - // make sure the selectedObjects is a subset of displayedObjects - int i; - Object o; - Iterator it = new LinkedList( selectedObjects ).iterator(); - boolean removeflag = false; - selectedIndexes.removeAllObjects(); - while ( it.hasNext() ) - { - o = it.next(); - if ( ( i = displayedObjects.indexOfIdenticalObject( o ) ) - == NSArray.NotFound ) - { - selectedObjects.removeIdenticalObject( o ); - removeflag = true; - } - else - { - selectedIndexes.addObject( new Integer( i ) ); - } - } - - //Note: it is important to put the - //selectionChanged = true line below remove. - if (removeflag) - { - selectionChanged = true; - willChange(); - - notifyDelegate( - "displayGroupDidChangeSelection", - new Class[] { WODisplayGroup.class }, - new Object[] { this } ); - notifyDelegate( - "displayGroupDidChangeSelectedObjects", - new Class[] { WODisplayGroup.class }, - new Object[] { this } ); - } - } - - /** - * Returns the index of the changed object. If more than - * one object has changed, -1 is returned. - */ - public int updatedObjectIndex() - { - return updatedObjectIndex; - } - - // getting and setting values in objects - - /** - * Returns a value on the selected object for the specified key. - */ - public Object selectedObjectValueForKey ( String aKey ) - { - Object selectedObject = selectedObject(); - if ( selectedObject == null ) return null; - return valueForObject( selectedObject, aKey ); - } - - /** - * Sets the specified value for the specified key on - * all selected objects. - */ - public boolean setSelectedObjectValue ( - Object aValue, String aKey ) - { - Object selectedObject = selectedObject(); - if ( selectedObject == null ) return false; - return setValueForObject( aValue, selectedObject, aKey ); - } - - /** - * Sets the specified value for the specified key on - * the specified object. Validations may be triggered, - * and error dialogs may appear to the user. - * @return True if the value was set successfully, - * false if the value could not be set and the update - * operation should not continue. - */ - public boolean setValueForObject ( - Object aValue, Object anObject, String aKey ) - { - // notify object's observers: - // this includes us, and will notify our observers - EOObserverCenter.notifyObserversObjectWillChange( anObject ); - - //TODO: if key is null, need to remove old object - // and add new object instead of simply replacing it. - - try - { - if ( anObject instanceof EOKeyValueCoding ) - { - ((EOKeyValueCoding)anObject).takeValueForKey( aValue, aKey ); - } - else - { - EOKeyValueCodingSupport.takeValueForKey( anObject, aValue, aKey ); - } - } - catch ( RuntimeException exc ) - { - Object result = notifyDelegate( - "displayGroupShouldDisplayAlert", - new Class[] { WODisplayGroup.class, String.class, String.class }, - new Object[] { this, "Error", exc.getMessage() } ); - if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) ) - { - throw exc; - } - return false; - } - - notifyDelegate( - "displayGroupDidSetValueForObject", - new Class[] { WODisplayGroup.class, Object.class, Object.class, String.class }, - new Object[] { this, aValue, anObject, aKey } ); - - return true; - } - - /** - * Calls setValueForObject() for the object at - * the specified index. - */ - public boolean setValueForObjectAtIndex ( - Object aValue, int anIndex, String aKey ) - { - return setValueForObject( - aValue, displayedObjects.objectAtIndex( anIndex ), aKey ); - } - - /** - * Returns the value for the specified key on the specified object. - */ - public Object valueForObject ( Object anObject, String aKey ) - { - // empty string is considered the identity property - if ( aKey == null ) return anObject; - if ( aKey.equals( "" ) ) return anObject; - - try - { - if ( anObject instanceof EOKeyValueCoding ) - { - return ((EOKeyValueCoding)anObject).valueForKey( aKey ); - } - else - { - return EOKeyValueCodingSupport.valueForKey( anObject, aKey ); - } - } - catch ( RuntimeException exc ) - { - Object result = notifyDelegate( - "displayGroupShouldDisplayAlert", - new Class[] { WODisplayGroup.class, String.class, String.class }, - new Object[] { this, "Error", exc.getMessage() } ); - if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) ) - { - throw exc; - } - return null; - } - } - - /** - * Calls valueForObject() for the object at the specified index. - * Returns null if out of bounds. - */ - public Object valueForObjectAtIndex ( int anIndex, String aKey ) - { - if ( displayedObjects.count() <= anIndex ) return null; - Object o = displayedObjects.objectAtIndex( anIndex ); - return valueForObject( o, aKey ); - } - - /** - * Prints out the list of displayed objects. - */ - public String toString() - { - return displayedObjects.toString(); - } - - - /** - * Handles notifications from the data source's editing context, - * looking for InvalidatedAllObjectsInStoreNotification and - * ObjectsChangedInEditingContextNotification, refetching in - * the former case and updating displayed objects in the latter. - * Note: This method is not in the public specification. - */ - public void objectsInvalidatedInEditingContext( NSNotification aNotification ) - { - if ( EOObjectStore.InvalidatedAllObjectsInStoreNotification - .equals( aNotification.name() ) ) - { - Object result = notifyDelegate( - "displayGroupShouldRefetch", - new Class[] { WODisplayGroup.class, NSNotification.class }, - new Object[] { this, aNotification } ); - if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) ) - { - fetch(); - } - } - else - if ( EOEditingContext.ObjectsChangedInEditingContextNotification - .equals( aNotification.name() ) ) - { - Object result = notifyDelegate( - "displayGroupShouldRedisplay", - new Class[] { WODisplayGroup.class, NSNotification.class }, - new Object[] { this, aNotification } ); - if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) ) - { - int index; - Enumeration e; - boolean didChange = false; - NSDictionary userInfo = aNotification.userInfo(); - - // inserts are ignored - - // mark updated objects as updated - NSArray updates = (NSArray) userInfo.objectForKey( - EOObjectStore.UpdatedKey ); - e = updates.objectEnumerator(); - while ( e.hasMoreElements() ) - { - index = indexOf( displayedObjects, e.nextElement() ); - if ( index != NSArray.NotFound ) - { - //System.out.println( "WODisplayGroup: updated: " + index ); - if ( ! didChange ) - { - didChange = true; - willChange(); - updatedObjectIndex = index; - } - else - { - updatedObjectIndex = -1; - } - } - } - - // treat invalidated objects as updated - NSArray invalidates = (NSArray) userInfo.objectForKey( - EOObjectStore.InvalidatedKey ); - e = invalidates.objectEnumerator(); - while ( e.hasMoreElements() ) - { - index = indexOf( displayedObjects, e.nextElement() ); - if ( index != NSArray.NotFound ) - { - //System.out.println( "WODisplayGroup: invalidated: " + index ); - if ( ! didChange ) - { - didChange = true; - willChange(); - updatedObjectIndex = index; - } - else - { - updatedObjectIndex = -1; - } - } - } - - // remove deletes from display group if they exist - NSArray deletes = (NSArray) userInfo.objectForKey( - EOObjectStore.DeletedKey ); - e = deletes.objectEnumerator(); - Object o; - while ( e.hasMoreElements() ) - { - o = e.nextElement(); - index = indexOf( displayedObjects, o ); - if ( index != NSArray.NotFound ) - { - //System.out.println( "WODisplayGroup: deleted: " + o ); - deleteObjectAtIndexNoNotify( index ); - } - } - - if ( !usesOptimisticRefresh() ) - { - updateDisplayedObjects(); - } - } - } - - } - - // static methods - - /** - * Specifies the default behavior for whether changes - * should be validated immediately for all display groups. - */ - public static boolean - globalDefaultForValidatesChangesImmediately() - { - return globalDefaultForValidatesChangesImmediately; - } - - /** - * Specifies the default string matching format for all - * display groups. - */ - public static String globalDefaultStringMatchFormat() - { - return globalDefaultStringMatchFormat; - } - - /** - * Specifies the default string matching operator for all - * display groups. - */ - public static String globalDefaultStringMatchOperator() - { - return globalDefaultStringMatchOperator; - } - - /** - * Sets the default behavior for validating changes - * for all display groups. - */ - public static void - setGlobalDefaultForValidatesChangesImmediately ( - boolean validatesImmediately ) - { - globalDefaultForValidatesChangesImmediately = - validatesImmediately; - } - - /** - * Sets the default string matching format that - * will be used by all display groups. - */ - public static void - setGlobalDefaultStringMatchFormat ( String aFormat ) - { - globalDefaultStringMatchFormat = aFormat; - } - - /** - * Sets the default string matching operator that - * will be used by all display groups. - */ - public static void - setGlobalDefaultStringMatchOperator ( String anOperator ) - { - globalDefaultStringMatchOperator = anOperator; - } - - /** - * Needed because we don't inherit from NSObject. - * Calls EOObserverCenter.notifyObserversObjectWillChange. - */ - protected void willChange() - { - EOObserverCenter.notifyObserversObjectWillChange( this ); - } - - /** - * Returns the index of the specified object in the - * specified NSArray, comparing by value or by reference - * as determined by the private instance variable - * compareByReference. If not found, returns NSArray.NotFound. - */ - private int indexOf( NSArray anArray, Object anObject ) - { - if ( compareByReference ) - { - return anArray.indexOfIdenticalObject( anObject ); - } - else - { - return anArray.indexOf( anObject ); - } - } - - // interface EOObserving - - /** - * Receives notifications of changes from objects that - * are managed by this display group. This implementation - * sets updatedObjectIndex as appropriate. - */ - public void objectWillChange(Object anObject) - { - int index = indexOf( displayedObjects, anObject ); - if ( index != NSArray.NotFound ) - { - updatedObjectIndex = index; - willChange(); - } - } - - // interface EOEditingContext.Editor - - /** - * Called before the editing context begins to save changes. - * This implementation calls endEditing(). - */ - public void editingContextWillSaveChanges( - EOEditingContext anEditingContext ) - { - endEditing(); - } - - /** - * Called to determine whether this editor has changes - * that have not been committed to the object in the context. - * This implementation returns false. - */ - public boolean editorHasChangesForEditingContext( - EOEditingContext anEditingContext ) - { - return false; - } - - // interface EOEditingContext.MessageHandler - - /** - * Called to display a message for an error that occurred - * in the specified editing context. If the delegate allows, - * this implementation writes a message to the standard output. - * Override to customize. - */ - public void editingContextPresentErrorMessage( - EOEditingContext anEditingContext, - String aMessage ) - { - Object result = notifyDelegate( - "displayGroupShouldDisplayAlert", - new Class[] { WODisplayGroup.class, String.class, String.class }, - new Object[] { this, "Error", aMessage } ); - if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) ) - { - System.out.println( aMessage ); - } - } - - /** - * Called by the specified object store to determine whether - * fetching should continue, where count is the current count - * and limit is the limit as specified by the fetch specification. - * This implementation returns true. Override to customize. - */ - public boolean editingContextShouldContinueFetching( - EOEditingContext anEditingContext, - int count, - int limit, - EOObjectStore anObjectStore ) - { - return true; - } - - /** - * Sends the specified message to the delegate. - * Returns the return value of the method, - * or null if no return value or no delegate - * or no implementation. - */ - private Object notifyDelegate( - String aMethodName, Class[] types, Object[] params ) - { - try - { - Object delegate = delegate(); - if ( delegate == null ) return null; - return NSSelector.invoke( - aMethodName, types, delegate, params ); - } - catch ( NoSuchMethodException e ) - { - // ignore: not implemented - } - catch ( Exception exc ) - { - // log to standard error - System.err.println( - "Error while messaging delegate: " + - delegate + " : " + aMethodName ); - exc.printStackTrace(); - } - - return null; - } - - /** - * DisplayGroups can delegate important decisions to a Delegate. - * Note that DisplayGroup doesn't require its delegates to implement - * this interface: rather, this interface defines the methods that - * DisplayGroup will attempt to invoke dynamically on its delegate. - * The delegate may choose to implement only a subset of the methods - * on the interface. - */ - public interface Delegate - { - /** - * Called when the specified data source fails - * to create an object for the specified display group. - */ - void displayGroupCreateObjectFailed ( - WODisplayGroup aDisplayGroup, - EODataSource aDataSource ); - - /** - * Called after the specified display group's - * data source is changed. - */ - void displayGroupDidChangeDataSource ( - WODisplayGroup aDisplayGroup ); - - /** - * Called after a change occurs in the specified - * display group's selected objects. - */ - void displayGroupDidChangeSelectedObjects ( - WODisplayGroup aDisplayGroup ); - - /** - * Called after the specified display group's - * selection has changed. - */ - void displayGroupDidChangeSelection ( - WODisplayGroup aDisplayGroup ); - - /** - * Called after the specified display group has - * deleted the specified object. - */ - void displayGroupDidDeleteObject ( - WODisplayGroup aDisplayGroup, - Object anObject ); - - /** - * Called after the specified display group - * has fetched the specified object list. - */ - void displayGroupDidFetchObjects ( - WODisplayGroup aDisplayGroup, - List anObjectList ); - - /** - * Called after the specified display group - * has inserted the specified object into - * its internal object list. - */ - void displayGroupDidInsertObject ( - WODisplayGroup aDisplayGroup, - Object anObject ); - - /** - * Called after the specified display group - * has set the specified value for the specified - * object and key. - */ - void displayGroupDidSetValueForObject ( - WODisplayGroup aDisplayGroup, - Object aValue, - Object anObject, - String aKey ); - - /** - * Called by the specified display group to - * determine what objects should be displayed - * for the objects in the specified list. - * @return An NSArray containing the objects - * to be displayed for the objects in the - * specified list. - */ - NSArray displayGroupDisplayArrayForObjects ( - WODisplayGroup aDisplayGroup, - List aList ); - - /** - * Called by the specified display group before - * it attempts to change the selection. - * @return True to allow the selection to change, - * false otherwise. - */ - boolean displayGroupShouldChangeSelection ( - WODisplayGroup aDisplayGroup, - List aSelectionList ); - - /** - * Called by the specified display group before - * it attempts to delete the specified object. - * @return True to allow the object to be deleted - * false to prevent the deletion. - */ - boolean displayGroupShouldDeleteObject ( - WODisplayGroup aDisplayGroup, - Object anObject ); - - /** - * Called by the specified display group before - * it attempts display the specified alert to - * the user. - * @return True to allow the message to be - * displayed, false if you want to handle the - * alert yourself and suppress the display group's - * notification. - */ - boolean displayGroupShouldDisplayAlert ( - WODisplayGroup aDisplayGroup, - String aTitle, - String aMessage ); - - /** - * Called by the specified display group before - * it attempts fetch objects. - * @return True to allow the fetch to take place, - * false to prevent the fetch. - */ - boolean displayGroupShouldFetch ( - WODisplayGroup aDisplayGroup ); - - /** - * Called by the specified display group before - * it attempts to insert the specified object. - * @return True to allow the object to be inserted - * false to prevent the insertion. - */ - boolean displayGroupShouldInsertObject ( - WODisplayGroup aDisplayGroup, - Object anObject, - int anIndex ); - - /** - * Called by the specified display group when - * it receives the specified - * ObjectsChangedInEditingContextNotification. - * @return True to allow the display group to - * update the display (recommended), false - * to prevent the update. - */ - boolean displayGroupShouldRedisplay ( - WODisplayGroup aDisplayGroup, - NSNotification aNotification ); - - /** - * Called by the specified display group when - * it receives the specified - * InvalidatedAllObjectsInStoreNotification. - * @return True to allow the display group to - * refetch (recommended), false to prevent the - * refetch. - */ - boolean displayGroupShouldRefetch ( - WODisplayGroup aDisplayGroup, - NSNotification aNotification ); - - } + int i; + NSMutableArray result = new NSMutableArray(); + Enumeration e = selectedObjects.objectEnumerator(); + while (e.hasMoreElements()) { + i = indexOf(displayedObjects, e.nextElement()); + if (i != NSArray.NotFound) { + result.addObject(new Integer(i)); + } else { + System.err.println("Should never happen: selected objects not in displayed objects"); + new RuntimeException().printStackTrace(System.err); + } + } + return result; + } + + /** + * Sets the objects managed by this display group. updateDisplayedObjects() is + * called to filter the display objects. The previous selection will be + * maintained if possible. The data source is not notified. + */ + public void setObjectArray(List anObjectList) { + if (anObjectList == null) + anObjectList = new NSArray(); + + Object result = notifyDelegate("displayGroupDisplayArrayForObjects", + new Class[] { WODisplayGroup.class, List.class }, new Object[] { this, anObjectList }); + if (result != null) { + anObjectList = (List) result; + } + + willChange(); + + NSArray oldSelectedObjects = new NSArray(selectedObjects); // copy + + // reset allObjects to new list + allObjects.removeAllObjects(); + allObjects.addObjectsFromArray(anObjectList); + + // update the displayed object list + updateDisplayedObjects(); + + // restore the selection if possible + selectObjectsIdenticalTo(oldSelectedObjects); + + batchIndex = 0; + displayBatchContainingSelectedObject(); + } + + /** + * Sets the currently selected object, or clears the selection if the object is + * not found or is null. Note: it's not clear how this differs from selectObject + * in the spec. It is recommended that you call selectObject for now. + */ + public void setSelectedObject(Object anObject) { + selectObject(anObject); + } + + /** + * Sets the current selection to the specified objects. The previous selection + * is cleared, and any objects in the display group that are in the specified + * list are then selected. If no items in the specified list are found in the + * display group, then the selection is effectively cleared. Note: it's not + * clear how this differs from selectObjectsIdenticalTo in the spec. It is + * recommended that you call that method for now. + */ + public void setSelectedObjects(List aList) { + selectObjectsIdenticalTo(aList); + } + + /** + * Sets the current selection to the objects at the specified indexes. Items in + * the list are assumed to be instances of java.lang.Number. The previous + * selection is cleared, and any objects in the display group that are in the + * specified list are then selected. If no items in the specified list are found + * in the display group, then the selection is effectively cleared. + */ + public boolean setSelectionIndexes(List aList) { + Object o; + int index; + NSMutableArray objects = new NSMutableArray(); + Iterator it = aList.iterator(); + while (it.hasNext()) { + index = ((Number) it.next()).intValue(); + if (index < displayedObjects.count()) { + o = displayedObjects.objectAtIndex(index); + if (o != null) { + objects.add(o); + } + } + } + return selectObjectsIdenticalTo(objects); + } + + /** + * Applies the qualifier to all objects and sorts the results to update the list + * of displayed objects. Observing associations are notified to reflect the + * changes. + */ + public void updateDisplayedObjects() { + updatedObjectIndex = -1; + willChange(); + + displayedObjects.removeAllObjects(); + + displayedObjects.addObjectsFromArray(allObjects); + + // apply qualifier, if any + if (qualifier != null) { + EOQualifier.filterArrayWithQualifier(displayedObjects, qualifier); + } + + // apply sort orderings, if any + NSArray orderings = sortOrderings(); + if (orderings != null) { + if (orderings.count() > 0) { + selectionChanged = true; + willChange(); + EOSortOrdering.sortArrayUsingKeyOrderArray(displayedObjects, orderings); + } + } + + // make sure the selectedObjects is a subset of displayedObjects + int i; + Object o; + Iterator it = new LinkedList(selectedObjects).iterator(); + boolean removeflag = false; + selectedIndexes.removeAllObjects(); + while (it.hasNext()) { + o = it.next(); + if ((i = displayedObjects.indexOfIdenticalObject(o)) == NSArray.NotFound) { + selectedObjects.removeIdenticalObject(o); + removeflag = true; + } else { + selectedIndexes.addObject(new Integer(i)); + } + } + + // Note: it is important to put the + // selectionChanged = true line below remove. + if (removeflag) { + selectionChanged = true; + willChange(); + + notifyDelegate("displayGroupDidChangeSelection", new Class[] { WODisplayGroup.class }, + new Object[] { this }); + notifyDelegate("displayGroupDidChangeSelectedObjects", new Class[] { WODisplayGroup.class }, + new Object[] { this }); + } + } + + /** + * Returns the index of the changed object. If more than one object has changed, + * -1 is returned. + */ + public int updatedObjectIndex() { + return updatedObjectIndex; + } + + // getting and setting values in objects + + /** + * Returns a value on the selected object for the specified key. + */ + public Object selectedObjectValueForKey(String aKey) { + Object selectedObject = selectedObject(); + if (selectedObject == null) + return null; + return valueForObject(selectedObject, aKey); + } + + /** + * Sets the specified value for the specified key on all selected objects. + */ + public boolean setSelectedObjectValue(Object aValue, String aKey) { + Object selectedObject = selectedObject(); + if (selectedObject == null) + return false; + return setValueForObject(aValue, selectedObject, aKey); + } + + /** + * Sets the specified value for the specified key on the specified object. + * Validations may be triggered, and error dialogs may appear to the user. + * + * @return True if the value was set successfully, false if the value could not + * be set and the update operation should not continue. + */ + public boolean setValueForObject(Object aValue, Object anObject, String aKey) { + // notify object's observers: + // this includes us, and will notify our observers + EOObserverCenter.notifyObserversObjectWillChange(anObject); + + // TODO: if key is null, need to remove old object + // and add new object instead of simply replacing it. + + try { + if (anObject instanceof EOKeyValueCoding) { + ((EOKeyValueCoding) anObject).takeValueForKey(aValue, aKey); + } else { + EOKeyValueCodingSupport.takeValueForKey(anObject, aValue, aKey); + } + } catch (RuntimeException exc) { + Object result = notifyDelegate("displayGroupShouldDisplayAlert", + new Class[] { WODisplayGroup.class, String.class, String.class }, + new Object[] { this, "Error", exc.getMessage() }); + if ((result == null) || (Boolean.TRUE.equals(result))) { + throw exc; + } + return false; + } + + notifyDelegate("displayGroupDidSetValueForObject", + new Class[] { WODisplayGroup.class, Object.class, Object.class, String.class }, + new Object[] { this, aValue, anObject, aKey }); + + return true; + } + + /** + * Calls setValueForObject() for the object at the specified index. + */ + public boolean setValueForObjectAtIndex(Object aValue, int anIndex, String aKey) { + return setValueForObject(aValue, displayedObjects.objectAtIndex(anIndex), aKey); + } + + /** + * Returns the value for the specified key on the specified object. + */ + public Object valueForObject(Object anObject, String aKey) { + // empty string is considered the identity property + if (aKey == null) + return anObject; + if (aKey.equals("")) + return anObject; + + try { + if (anObject instanceof EOKeyValueCoding) { + return ((EOKeyValueCoding) anObject).valueForKey(aKey); + } else { + return EOKeyValueCodingSupport.valueForKey(anObject, aKey); + } + } catch (RuntimeException exc) { + Object result = notifyDelegate("displayGroupShouldDisplayAlert", + new Class[] { WODisplayGroup.class, String.class, String.class }, + new Object[] { this, "Error", exc.getMessage() }); + if ((result == null) || (Boolean.TRUE.equals(result))) { + throw exc; + } + return null; + } + } + + /** + * Calls valueForObject() for the object at the specified index. Returns null if + * out of bounds. + */ + public Object valueForObjectAtIndex(int anIndex, String aKey) { + if (displayedObjects.count() <= anIndex) + return null; + Object o = displayedObjects.objectAtIndex(anIndex); + return valueForObject(o, aKey); + } + + /** + * Prints out the list of displayed objects. + */ + public String toString() { + return displayedObjects.toString(); + } + + /** + * Handles notifications from the data source's editing context, looking for + * InvalidatedAllObjectsInStoreNotification and + * ObjectsChangedInEditingContextNotification, refetching in the former case and + * updating displayed objects in the latter. Note: This method is not in the + * public specification. + */ + public void objectsInvalidatedInEditingContext(NSNotification aNotification) { + if (EOObjectStore.InvalidatedAllObjectsInStoreNotification.equals(aNotification.name())) { + Object result = notifyDelegate("displayGroupShouldRefetch", + new Class[] { WODisplayGroup.class, NSNotification.class }, new Object[] { this, aNotification }); + if ((result == null) || (Boolean.TRUE.equals(result))) { + fetch(); + } + } else if (EOEditingContext.ObjectsChangedInEditingContextNotification.equals(aNotification.name())) { + Object result = notifyDelegate("displayGroupShouldRedisplay", + new Class[] { WODisplayGroup.class, NSNotification.class }, new Object[] { this, aNotification }); + if ((result == null) || (Boolean.TRUE.equals(result))) { + int index; + Enumeration e; + boolean didChange = false; + NSDictionary userInfo = aNotification.userInfo(); + + // inserts are ignored + + // mark updated objects as updated + NSArray updates = (NSArray) userInfo.objectForKey(EOObjectStore.UpdatedKey); + e = updates.objectEnumerator(); + while (e.hasMoreElements()) { + index = indexOf(displayedObjects, e.nextElement()); + if (index != NSArray.NotFound) { + // System.out.println( "WODisplayGroup: updated: " + index ); + if (!didChange) { + didChange = true; + willChange(); + updatedObjectIndex = index; + } else { + updatedObjectIndex = -1; + } + } + } + + // treat invalidated objects as updated + NSArray invalidates = (NSArray) userInfo.objectForKey(EOObjectStore.InvalidatedKey); + e = invalidates.objectEnumerator(); + while (e.hasMoreElements()) { + index = indexOf(displayedObjects, e.nextElement()); + if (index != NSArray.NotFound) { + // System.out.println( "WODisplayGroup: invalidated: " + index ); + if (!didChange) { + didChange = true; + willChange(); + updatedObjectIndex = index; + } else { + updatedObjectIndex = -1; + } + } + } + + // remove deletes from display group if they exist + NSArray deletes = (NSArray) userInfo.objectForKey(EOObjectStore.DeletedKey); + e = deletes.objectEnumerator(); + Object o; + while (e.hasMoreElements()) { + o = e.nextElement(); + index = indexOf(displayedObjects, o); + if (index != NSArray.NotFound) { + // System.out.println( "WODisplayGroup: deleted: " + o ); + deleteObjectAtIndexNoNotify(index); + } + } + + if (!usesOptimisticRefresh()) { + updateDisplayedObjects(); + } + } + } + + } + + // static methods + + /** + * Specifies the default behavior for whether changes should be validated + * immediately for all display groups. + */ + public static boolean globalDefaultForValidatesChangesImmediately() { + return globalDefaultForValidatesChangesImmediately; + } + + /** + * Specifies the default string matching format for all display groups. + */ + public static String globalDefaultStringMatchFormat() { + return globalDefaultStringMatchFormat; + } + + /** + * Specifies the default string matching operator for all display groups. + */ + public static String globalDefaultStringMatchOperator() { + return globalDefaultStringMatchOperator; + } + + /** + * Sets the default behavior for validating changes for all display groups. + */ + public static void setGlobalDefaultForValidatesChangesImmediately(boolean validatesImmediately) { + globalDefaultForValidatesChangesImmediately = validatesImmediately; + } + + /** + * Sets the default string matching format that will be used by all display + * groups. + */ + public static void setGlobalDefaultStringMatchFormat(String aFormat) { + globalDefaultStringMatchFormat = aFormat; + } + + /** + * Sets the default string matching operator that will be used by all display + * groups. + */ + public static void setGlobalDefaultStringMatchOperator(String anOperator) { + globalDefaultStringMatchOperator = anOperator; + } + + /** + * Needed because we don't inherit from NSObject. Calls + * EOObserverCenter.notifyObserversObjectWillChange. + */ + protected void willChange() { + EOObserverCenter.notifyObserversObjectWillChange(this); + } + + /** + * Returns the index of the specified object in the specified NSArray, comparing + * by value or by reference as determined by the private instance variable + * compareByReference. If not found, returns NSArray.NotFound. + */ + private int indexOf(NSArray anArray, Object anObject) { + if (compareByReference) { + return anArray.indexOfIdenticalObject(anObject); + } else { + return anArray.indexOf(anObject); + } + } + + // interface EOObserving + + /** + * Receives notifications of changes from objects that are managed by this + * display group. This implementation sets updatedObjectIndex as appropriate. + */ + public void objectWillChange(Object anObject) { + int index = indexOf(displayedObjects, anObject); + if (index != NSArray.NotFound) { + updatedObjectIndex = index; + willChange(); + } + } + + // interface EOEditingContext.Editor + + /** + * Called before the editing context begins to save changes. This implementation + * calls endEditing(). + */ + public void editingContextWillSaveChanges(EOEditingContext anEditingContext) { + endEditing(); + } + + /** + * Called to determine whether this editor has changes that have not been + * committed to the object in the context. This implementation returns false. + */ + public boolean editorHasChangesForEditingContext(EOEditingContext anEditingContext) { + return false; + } + + // interface EOEditingContext.MessageHandler + + /** + * Called to display a message for an error that occurred in the specified + * editing context. If the delegate allows, this implementation writes a message + * to the standard output. Override to customize. + */ + public void editingContextPresentErrorMessage(EOEditingContext anEditingContext, String aMessage) { + Object result = notifyDelegate("displayGroupShouldDisplayAlert", + new Class[] { WODisplayGroup.class, String.class, String.class }, + new Object[] { this, "Error", aMessage }); + if ((result == null) || (Boolean.TRUE.equals(result))) { + System.out.println(aMessage); + } + } + + /** + * Called by the specified object store to determine whether fetching should + * continue, where count is the current count and limit is the limit as + * specified by the fetch specification. This implementation returns true. + * Override to customize. + */ + public boolean editingContextShouldContinueFetching(EOEditingContext anEditingContext, int count, int limit, + EOObjectStore anObjectStore) { + return true; + } + + /** + * Sends the specified message to the delegate. Returns the return value of the + * method, or null if no return value or no delegate or no implementation. + */ + private Object notifyDelegate(String aMethodName, Class[] types, Object[] params) { + try { + Object delegate = delegate(); + if (delegate == null) + return null; + return NSSelector.invoke(aMethodName, types, delegate, params); + } catch (NoSuchMethodException e) { + // ignore: not implemented + } catch (Exception exc) { + // log to standard error + System.err.println("Error while messaging delegate: " + delegate + " : " + aMethodName); + exc.printStackTrace(); + } + + return null; + } + + /** + * DisplayGroups can delegate important decisions to a Delegate. Note that + * DisplayGroup doesn't require its delegates to implement this interface: + * rather, this interface defines the methods that DisplayGroup will attempt to + * invoke dynamically on its delegate. The delegate may choose to implement only + * a subset of the methods on the interface. + */ + public interface Delegate { + /** + * Called when the specified data source fails to create an object for the + * specified display group. + */ + void displayGroupCreateObjectFailed(WODisplayGroup aDisplayGroup, EODataSource aDataSource); + + /** + * Called after the specified display group's data source is changed. + */ + void displayGroupDidChangeDataSource(WODisplayGroup aDisplayGroup); + + /** + * Called after a change occurs in the specified display group's selected + * objects. + */ + void displayGroupDidChangeSelectedObjects(WODisplayGroup aDisplayGroup); + + /** + * Called after the specified display group's selection has changed. + */ + void displayGroupDidChangeSelection(WODisplayGroup aDisplayGroup); + + /** + * Called after the specified display group has deleted the specified object. + */ + void displayGroupDidDeleteObject(WODisplayGroup aDisplayGroup, Object anObject); + + /** + * Called after the specified display group has fetched the specified object + * list. + */ + void displayGroupDidFetchObjects(WODisplayGroup aDisplayGroup, List anObjectList); + + /** + * Called after the specified display group has inserted the specified object + * into its internal object list. + */ + void displayGroupDidInsertObject(WODisplayGroup aDisplayGroup, Object anObject); + + /** + * Called after the specified display group has set the specified value for the + * specified object and key. + */ + void displayGroupDidSetValueForObject(WODisplayGroup aDisplayGroup, Object aValue, Object anObject, + String aKey); + + /** + * Called by the specified display group to determine what objects should be + * displayed for the objects in the specified list. + * + * @return An NSArray containing the objects to be displayed for the objects in + * the specified list. + */ + NSArray displayGroupDisplayArrayForObjects(WODisplayGroup aDisplayGroup, List aList); + + /** + * Called by the specified display group before it attempts to change the + * selection. + * + * @return True to allow the selection to change, false otherwise. + */ + boolean displayGroupShouldChangeSelection(WODisplayGroup aDisplayGroup, List aSelectionList); + + /** + * Called by the specified display group before it attempts to delete the + * specified object. + * + * @return True to allow the object to be deleted false to prevent the deletion. + */ + boolean displayGroupShouldDeleteObject(WODisplayGroup aDisplayGroup, Object anObject); + + /** + * Called by the specified display group before it attempts display the + * specified alert to the user. + * + * @return True to allow the message to be displayed, false if you want to + * handle the alert yourself and suppress the display group's + * notification. + */ + boolean displayGroupShouldDisplayAlert(WODisplayGroup aDisplayGroup, String aTitle, String aMessage); + + /** + * Called by the specified display group before it attempts fetch objects. + * + * @return True to allow the fetch to take place, false to prevent the fetch. + */ + boolean displayGroupShouldFetch(WODisplayGroup aDisplayGroup); + + /** + * Called by the specified display group before it attempts to insert the + * specified object. + * + * @return True to allow the object to be inserted false to prevent the + * insertion. + */ + boolean displayGroupShouldInsertObject(WODisplayGroup aDisplayGroup, Object anObject, int anIndex); + + /** + * Called by the specified display group when it receives the specified + * ObjectsChangedInEditingContextNotification. + * + * @return True to allow the display group to update the display (recommended), + * false to prevent the update. + */ + boolean displayGroupShouldRedisplay(WODisplayGroup aDisplayGroup, NSNotification aNotification); + + /** + * Called by the specified display group when it receives the specified + * InvalidatedAllObjectsInStoreNotification. + * + * @return True to allow the display group to refetch (recommended), false to + * prevent the refetch. + */ + boolean displayGroupShouldRefetch(WODisplayGroup aDisplayGroup, NSNotification aNotification); + + } } /* - * $Log$ - * Revision 1.2 2006/02/19 01:44:02 cgruber - * Add xmlrpc files - * Remove jclark and replace with dom4j and javax.xml.sax stuff - * Re-work dependencies and imports so it all compiles. + * $Log$ Revision 1.2 2006/02/19 01:44:02 cgruber Add xmlrpc files Remove jclark + * and replace with dom4j and javax.xml.sax stuff Re-work dependencies and + * imports so it all compiles. * - * Revision 1.1 2006/02/16 13:22:22 cgruber - * Check in all sources in eclipse-friendly maven-enabled packages. + * Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in + * eclipse-friendly maven-enabled packages. * - * Revision 1.6 2003/08/07 00:15:15 chochos - * general cleanup (mostly removing unused imports) + * Revision 1.6 2003/08/07 00:15:15 chochos general cleanup (mostly removing + * unused imports) * - * Revision 1.5 2003/01/22 23:01:06 mpowers - * Better handling for index out of bounds. + * Revision 1.5 2003/01/22 23:01:06 mpowers Better handling for index out of + * bounds. * - * Revision 1.4 2003/01/21 22:27:02 mpowers - * Corrected context id usage. + * Revision 1.4 2003/01/21 22:27:02 mpowers Corrected context id usage. * Implemented backtracking. * - * Revision 1.3 2003/01/21 17:54:01 mpowers - * Batch indices are one-based, not zero-based. + * Revision 1.3 2003/01/21 17:54:01 mpowers Batch indices are one-based, not + * zero-based. * - * Revision 1.2 2003/01/21 14:38:42 mpowers - * Fixed batching. + * Revision 1.2 2003/01/21 14:38:42 mpowers Fixed batching. * - * Revision 1.1 2003/01/18 23:30:42 mpowers - * WODisplayGroup now compiles. + * Revision 1.1 2003/01/18 23:30:42 mpowers WODisplayGroup now compiles. * - * Revision 1.46 2002/10/24 21:15:36 mpowers - * New implementations of NSArray and subclasses. + * Revision 1.46 2002/10/24 21:15:36 mpowers New implementations of NSArray and + * subclasses. * - * Revision 1.45 2002/10/24 18:20:20 mpowers - * Because NSArray is read-only, we are returning our internal representations - * to callers of allObjects(), displayedObjects(), and selectedObjects(). + * Revision 1.45 2002/10/24 18:20:20 mpowers Because NSArray is read-only, we + * are returning our internal representations to callers of allObjects(), + * displayedObjects(), and selectedObjects(). * - * Revision 1.44 2002/08/06 18:20:25 mpowers - * Now posting DisplayGroupWillFetch notifications before fetch. - * Implemented support for usesOptimisticRefresh. - * No longer supporting inserted/updated/deleted lists: not part of spec. + * Revision 1.44 2002/08/06 18:20:25 mpowers Now posting DisplayGroupWillFetch + * notifications before fetch. Implemented support for usesOptimisticRefresh. No + * longer supporting inserted/updated/deleted lists: not part of spec. * - * Revision 1.43 2002/05/17 15:01:49 mpowers - * Implemented dynamic lookup of delegate methods so delegates no longer - * need to implement the DisplayGroup.Delegate interface. + * Revision 1.43 2002/05/17 15:01:49 mpowers Implemented dynamic lookup of + * delegate methods so delegates no longer need to implement the + * DisplayGroup.Delegate interface. * - * Revision 1.42 2002/03/26 21:46:06 mpowers - * Contributing EditingContext as a java-friendly convenience. + * Revision 1.42 2002/03/26 21:46:06 mpowers Contributing EditingContext as a + * java-friendly convenience. * - * Revision 1.41 2002/03/11 03:17:56 mpowers - * Provided control point for coalesced changes. + * Revision 1.41 2002/03/11 03:17:56 mpowers Provided control point for + * coalesced changes. * - * Revision 1.40 2002/03/05 23:18:28 mpowers - * Added documentation. - * Added isSelectionPaintedImmediate and isSelectionTracking attributes - * to TableAssociation. - * Added getTableAssociation to TableColumnAssociation. + * Revision 1.40 2002/03/05 23:18:28 mpowers Added documentation. Added + * isSelectionPaintedImmediate and isSelectionTracking attributes to + * TableAssociation. Added getTableAssociation to TableColumnAssociation. * - * Revision 1.39 2002/02/19 22:26:04 mpowers - * Implemented EOEditingContext.MessageHandler support. + * Revision 1.39 2002/02/19 22:26:04 mpowers Implemented + * EOEditingContext.MessageHandler support. * - * Revision 1.38 2002/02/19 16:37:38 mpowers - * Implemented support for EOEditingContext.Editor + * Revision 1.38 2002/02/19 16:37:38 mpowers Implemented support for + * EOEditingContext.Editor * - * Revision 1.37 2001/12/11 22:17:48 mpowers - * Now properly handling exceptions in valueForObject. - * No longer trying to retain selection based only on index. + * Revision 1.37 2001/12/11 22:17:48 mpowers Now properly handling exceptions in + * valueForObject. No longer trying to retain selection based only on index. * - * Revision 1.36 2001/11/08 21:42:00 mpowers - * Now we know what to do with shouldRefetch and shouldRedisplay. + * Revision 1.36 2001/11/08 21:42:00 mpowers Now we know what to do with + * shouldRefetch and shouldRedisplay. * - * Revision 1.35 2001/11/04 18:26:58 mpowers - * Fixed bug where exceptions were not properly reported when updating - * a value and the display group did not have a delegate. + * Revision 1.35 2001/11/04 18:26:58 mpowers Fixed bug where exceptions were not + * properly reported when updating a value and the display group did not have a + * delegate. * - * Revision 1.34 2001/11/02 20:59:36 mpowers - * Now correctly ensuring selected objects are a subset of displayed objects. + * Revision 1.34 2001/11/02 20:59:36 mpowers Now correctly ensuring selected + * objects are a subset of displayed objects. * - * Revision 1.33 2001/10/30 22:56:45 mpowers - * Added support for EOQualifier. + * Revision 1.33 2001/10/30 22:56:45 mpowers Added support for EOQualifier. * - * Revision 1.32 2001/10/23 22:27:53 mpowers - * Now running at ObserverPrioritySixth. + * Revision 1.32 2001/10/23 22:27:53 mpowers Now running at + * ObserverPrioritySixth. * - * Revision 1.31 2001/10/23 18:45:05 mpowers - * Rolling back changes. + * Revision 1.31 2001/10/23 18:45:05 mpowers Rolling back changes. * - * Revision 1.28 2001/08/22 19:23:41 mpowers - * No longer asserting objects in all objects list. + * Revision 1.28 2001/08/22 19:23:41 mpowers No longer asserting objects in all + * objects list. * - * Revision 1.27 2001/07/30 16:17:01 mpowers - * Minor code cleanup. + * Revision 1.27 2001/07/30 16:17:01 mpowers Minor code cleanup. * - * Revision 1.26 2001/07/10 22:49:07 mpowers - * Fixed bug in optimization for selectObjectsIdenticalTo (found by Dongzhi). + * Revision 1.26 2001/07/10 22:49:07 mpowers Fixed bug in optimization for + * selectObjectsIdenticalTo (found by Dongzhi). * - * Revision 1.25 2001/06/19 15:40:21 mpowers - * Now only changing the selection if the new selection is different - * from the old. + * Revision 1.25 2001/06/19 15:40:21 mpowers Now only changing the selection if + * the new selection is different from the old. * - * Revision 1.24 2001/05/24 17:36:15 mpowers - * Fixed problem with selectedObjectsIdenticalTo: it was using compare - * by value instead of compare by reference. + * Revision 1.24 2001/05/24 17:36:15 mpowers Fixed problem with + * selectedObjectsIdenticalTo: it was using compare by value instead of compare + * by reference. * - * Revision 1.23 2001/05/18 21:09:19 mpowers - * Now throwing exceptions if the delegate cannot handle error from update. + * Revision 1.23 2001/05/18 21:09:19 mpowers Now throwing exceptions if the + * delegate cannot handle error from update. * - * Revision 1.22 2001/05/14 15:26:12 mpowers - * Now checking for null delegate before and after selection change. + * Revision 1.22 2001/05/14 15:26:12 mpowers Now checking for null delegate + * before and after selection change. * - * Revision 1.21 2001/05/08 18:47:34 mpowers - * Minor fixes for d3. + * Revision 1.21 2001/05/08 18:47:34 mpowers Minor fixes for d3. * - * Revision 1.20 2001/04/29 22:02:45 mpowers - * Work on id transposing between editing contexts. + * Revision 1.20 2001/04/29 22:02:45 mpowers Work on id transposing between + * editing contexts. * - * Revision 1.19 2001/04/13 16:38:09 mpowers - * Alpha3 release. + * Revision 1.19 2001/04/13 16:38:09 mpowers Alpha3 release. * - * Revision 1.18 2001/04/03 20:36:01 mpowers - * Fixed refaulting/reverting/invalidating to be self-consistent. + * Revision 1.18 2001/04/03 20:36:01 mpowers Fixed + * refaulting/reverting/invalidating to be self-consistent. * - * Revision 1.17 2001/03/29 03:31:13 mpowers - * No longer using Introspector. + * Revision 1.17 2001/03/29 03:31:13 mpowers No longer using Introspector. * - * Revision 1.16 2001/02/27 03:32:18 mpowers - * Implemented default values for new objects. + * Revision 1.16 2001/02/27 03:32:18 mpowers Implemented default values for new + * objects. * - * Revision 1.15 2001/02/27 02:11:17 mpowers - * Now throwing exception when cloning fails. - * Removed debugging printlns. + * Revision 1.15 2001/02/27 02:11:17 mpowers Now throwing exception when cloning + * fails. Removed debugging printlns. * - * Revision 1.14 2001/02/26 22:41:51 mpowers - * Implemented null placeholder classes. - * Duplicator now uses NSNull. - * No longer catching base exception class. + * Revision 1.14 2001/02/26 22:41:51 mpowers Implemented null placeholder + * classes. Duplicator now uses NSNull. No longer catching base exception class. * - * Revision 1.13 2001/02/26 15:53:22 mpowers - * Fine-tuning notification firing. + * Revision 1.13 2001/02/26 15:53:22 mpowers Fine-tuning notification firing. * Child display groups now update properly after parent save or invalidate. * - * Revision 1.12 2001/02/22 20:55:06 mpowers - * Implemented notification handling. + * Revision 1.12 2001/02/22 20:55:06 mpowers Implemented notification handling. * - * Revision 1.11 2001/02/21 20:40:42 mpowers - * setObjectArray now falls back to index when trying to retain the - * same selection. + * Revision 1.11 2001/02/21 20:40:42 mpowers setObjectArray now falls back to + * index when trying to retain the same selection. * - * Revision 1.10 2001/02/20 16:38:55 mpowers - * MasterDetailAssociations now observe their controlled display group's - * objects for changes to that the parent object will be marked as updated. - * Before, only inserts and deletes to an object's items are registered. - * Also, moved ObservableArray to package access. + * Revision 1.10 2001/02/20 16:38:55 mpowers MasterDetailAssociations now + * observe their controlled display group's objects for changes to that the + * parent object will be marked as updated. Before, only inserts and deletes to + * an object's items are registered. Also, moved ObservableArray to package + * access. * - * Revision 1.9 2001/02/17 17:23:49 mpowers - * More changes to support compiling with jdk1.1 collections. + * Revision 1.9 2001/02/17 17:23:49 mpowers More changes to support compiling + * with jdk1.1 collections. * - * Revision 1.8 2001/02/17 16:52:05 mpowers - * Changes in imports to support building with jdk1.1 collections. + * Revision 1.8 2001/02/17 16:52:05 mpowers Changes in imports to support + * building with jdk1.1 collections. * - * Revision 1.7 2001/01/24 16:35:37 mpowers - * Improved documentation on TreeAssociation. - * SortOrderings are now inherited from parent nodes. - * Updates after sorting are still lost on TreeController. + * Revision 1.7 2001/01/24 16:35:37 mpowers Improved documentation on + * TreeAssociation. SortOrderings are now inherited from parent nodes. Updates + * after sorting are still lost on TreeController. * - * Revision 1.6 2001/01/24 14:23:05 mpowers - * Added support for OrderedDataSource. + * Revision 1.6 2001/01/24 14:23:05 mpowers Added support for OrderedDataSource. * - * Revision 1.5 2001/01/12 17:21:37 mpowers - * Implicit creation of EOSortOrderings now happens in setSortOrderings. + * Revision 1.5 2001/01/12 17:21:37 mpowers Implicit creation of EOSortOrderings + * now happens in setSortOrderings. * - * Revision 1.4 2001/01/11 20:34:26 mpowers - * Implemented EOSortOrdering and added support in framework. - * Added header-click to sort table columns. + * Revision 1.4 2001/01/11 20:34:26 mpowers Implemented EOSortOrdering and added + * support in framework. Added header-click to sort table columns. * - * Revision 1.3 2001/01/10 22:49:44 mpowers - * Implemented similarly named selection methods instead of - * throwing exceptions. + * Revision 1.3 2001/01/10 22:49:44 mpowers Implemented similarly named + * selection methods instead of throwing exceptions. * - * Revision 1.2 2001/01/09 20:12:52 mpowers - * Moved inner classes to package access. + * Revision 1.2 2001/01/09 20:12:52 mpowers Moved inner classes to package + * access. * - * Revision 1.1.1.1 2000/12/21 15:48:20 mpowers - * Contributing wotonomy. + * Revision 1.1.1.1 2000/12/21 15:48:20 mpowers Contributing wotonomy. * - * Revision 1.21 2000/12/20 16:25:39 michael - * Added log to all files. + * Revision 1.21 2000/12/20 16:25:39 michael Added log to all files. * - * Revision 1.20 2000/12/15 15:04:42 michael - * Added doc. + * Revision 1.20 2000/12/15 15:04:42 michael Added doc. * - * Revision 1.19 2000/12/11 13:32:48 michael - * Finish the much better TreeAssociation implementation. - * TreeAssociation now has no gui dependencies. + * Revision 1.19 2000/12/11 13:32:48 michael Finish the much better + * TreeAssociation implementation. TreeAssociation now has no gui dependencies. * - * Revision 1.18 2000/12/05 17:41:46 michael - * Broadcasts selection change after delegate refuses selection change - * so the initiating association gets refreshed. + * Revision 1.18 2000/12/05 17:41:46 michael Broadcasts selection change after + * delegate refuses selection change so the initiating association gets + * refreshed. * */ - -- cgit v1.2.3