From 40a9d99496e098562f090fb7ffce9e749011b131 Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Mon, 20 May 2024 17:58:16 -0400 Subject: Formatting pass --- .../net/wotonomy/ui/swing/DisplayGroupNode.java | 2452 +++++++++----------- 1 file changed, 1094 insertions(+), 1358 deletions(-) (limited to 'projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java') diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java index 1756285..d530f36 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java @@ -46,901 +46,727 @@ import net.wotonomy.ui.EODisplayGroup; import net.wotonomy.ui.swing.TreeModelAssociation.DelegatingTreeDataSource; /** -* DisplayGroupNodes are used as nodes in the -* TreeModelAssociation's implementation of TreeModel, -* and is tightly coupled with TreeModelAssociation -* and MasterDetailAssociation.

-* -* Even though it is no longer package access, -* don't rely on this class because we want to -* have the option of completely replacing this -* approach in the future. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ - abstract public class DisplayGroupNode - extends EODisplayGroup - { - protected TreeModelAssociation parentAssociation; - protected EODelayedObserver targetObserver; - protected NSMutableDictionary childNodes; - protected EODisplayGroup parentGroup; - protected Object target; - protected boolean isFetched; - protected boolean isFetchNeeded; - protected boolean useParentOrderings; - protected boolean useParentQualifier; - - /** - * Constructor for all nodes. - * Root node must have a null target. - */ - public DisplayGroupNode( - TreeModelAssociation aParentAssociation, - EODisplayGroup aParentGroup, - Object aTarget ) - { + * DisplayGroupNodes are used as nodes in the TreeModelAssociation's + * implementation of TreeModel, and is tightly coupled with TreeModelAssociation + * and MasterDetailAssociation.
+ *
+ * + * Even though it is no longer package access, don't rely on this class because + * we want to have the option of completely replacing this approach in the + * future. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ +abstract public class DisplayGroupNode extends EODisplayGroup { + protected TreeModelAssociation parentAssociation; + protected EODelayedObserver targetObserver; + protected NSMutableDictionary childNodes; + protected EODisplayGroup parentGroup; + protected Object target; + protected boolean isFetched; + protected boolean isFetchNeeded; + protected boolean useParentOrderings; + protected boolean useParentQualifier; + + /** + * Constructor for all nodes. Root node must have a null target. + */ + public DisplayGroupNode(TreeModelAssociation aParentAssociation, EODisplayGroup aParentGroup, Object aTarget) { //new net.wotonomy.ui.swing.util.StackTraceInspector( ""+aTarget ); //System.out.println( "DisplayGroupNode.new: " + aTarget ); - parentAssociation = aParentAssociation; - target = null; - targetObserver = null; - parentGroup = aParentGroup; - childNodes = new NSMutableDictionary(); - isFetched = false; - isFetchNeeded = false; - useParentOrderings = true; - useParentQualifier = true; - - EODataSource parentSource = null; - if ( parentGroup != null ) - { - parentSource = parentGroup.dataSource(); - } - else - if ( parentAssociation.titlesDisplayGroup != null ) - { - parentSource = parentAssociation.titlesDisplayGroup.dataSource(); - } - - // create child datasource - if ( aTarget != null ) // not root node - { - if ( parentAssociation.childrenKey != null ) - { - if ( parentSource == null ) - { - throw new WotonomyException( - "Need a data source when children aspect is bound." ); - } - - NSArray displayedObjects = parentGroup.displayedObjects(); - EODataSource childSource = parentSource.dataSourceQualifiedByKey( - parentAssociation.childrenKey ); - childSource.qualifyWithRelationshipKey( - parentAssociation.childrenKey, aTarget ); - - // create new display group using child data source - this.setDataSource( childSource ); - - // establish observer for target object - setTarget( aTarget ); - } - else // only titles is bound - { - // establish observer for target object - setTarget( aTarget ); - - setDataSource( new PropertyDataSource() - { - public NSArray fetchObjects() - { - return new NSArray(); - } - } ); - } - } - else // else root node - { - // root node uses PropertyDataSource by default - if ( parentSource == null ) - { - setDataSource( new PropertyDataSource() - { - public NSArray fetchObjects() - { - if ( parentGroup != null ) - { - return parentGroup.displayedObjects(); - } - return null; - } - } ); - } - else - { - // root node uses parent source directly - setDataSource( parentSource ); - } - } - } - - /** - * Overridden to unregister as an editor of the editing context, - * since we don't directly present a user interface. - */ - public void setDataSource ( EODataSource aDataSource ) - { - super.setDataSource( aDataSource ); - if ( ( aDataSource != null ) - && ( aDataSource.editingContext() != null ) ) - { - aDataSource.editingContext().removeEditor( this ); - } - } - - /** - * Returns whether the node should call fetch(). - */ - protected boolean isFetched() - { - if ( isFetchNeeded() ) - { - setFetchNeeded( false ); - fetch(); - } - return isFetched; - } - - /** - * Sets whether the node should call fetch(). - */ - protected void setFetched( boolean fetched ) - { + parentAssociation = aParentAssociation; + target = null; + targetObserver = null; + parentGroup = aParentGroup; + childNodes = new NSMutableDictionary(); + isFetched = false; + isFetchNeeded = false; + useParentOrderings = true; + useParentQualifier = true; + + EODataSource parentSource = null; + if (parentGroup != null) { + parentSource = parentGroup.dataSource(); + } else if (parentAssociation.titlesDisplayGroup != null) { + parentSource = parentAssociation.titlesDisplayGroup.dataSource(); + } + + // create child datasource + if (aTarget != null) // not root node + { + if (parentAssociation.childrenKey != null) { + if (parentSource == null) { + throw new WotonomyException("Need a data source when children aspect is bound."); + } + + NSArray displayedObjects = parentGroup.displayedObjects(); + EODataSource childSource = parentSource.dataSourceQualifiedByKey(parentAssociation.childrenKey); + childSource.qualifyWithRelationshipKey(parentAssociation.childrenKey, aTarget); + + // create new display group using child data source + this.setDataSource(childSource); + + // establish observer for target object + setTarget(aTarget); + } else // only titles is bound + { + // establish observer for target object + setTarget(aTarget); + + setDataSource(new PropertyDataSource() { + public NSArray fetchObjects() { + return new NSArray(); + } + }); + } + } else // else root node + { + // root node uses PropertyDataSource by default + if (parentSource == null) { + setDataSource(new PropertyDataSource() { + public NSArray fetchObjects() { + if (parentGroup != null) { + return parentGroup.displayedObjects(); + } + return null; + } + }); + } else { + // root node uses parent source directly + setDataSource(parentSource); + } + } + } + + /** + * Overridden to unregister as an editor of the editing context, since we don't + * directly present a user interface. + */ + public void setDataSource(EODataSource aDataSource) { + super.setDataSource(aDataSource); + if ((aDataSource != null) && (aDataSource.editingContext() != null)) { + aDataSource.editingContext().removeEditor(this); + } + } + + /** + * Returns whether the node should call fetch(). + */ + protected boolean isFetched() { + if (isFetchNeeded()) { + setFetchNeeded(false); + fetch(); + } + return isFetched; + } + + /** + * Sets whether the node should call fetch(). + */ + protected void setFetched(boolean fetched) { //System.out.println( "DisplayGroupNode.setFetched: " + fetched + " : " + this + " : " + target ); //net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace(); - isFetched = fetched; - } - - /** - * Returns whether the node is in need of a refetch. - */ - protected boolean isFetchNeeded() - { - return isFetchNeeded; - } - - /** - * Returns whether the node should call fetch(). - */ - protected void setFetchNeeded( boolean fetchNeeded ) - { + isFetched = fetched; + } + + /** + * Returns whether the node is in need of a refetch. + */ + protected boolean isFetchNeeded() { + return isFetchNeeded; + } + + /** + * Returns whether the node should call fetch(). + */ + protected void setFetchNeeded(boolean fetchNeeded) { //System.out.println( "DisplayGroupNode.setFetchNeeded: " + fetchNeeded + " : " + this + " : " + target ); //net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace(); - isFetchNeeded = fetchNeeded; - } - - /** - * Subclasses should override this method to fire an appropriate insertion event. - */ - protected void fireNodesInserted( Object[] path, int[] indexes, Object[] objects ) - { + isFetchNeeded = fetchNeeded; + } + + /** + * Subclasses should override this method to fire an appropriate insertion + * event. + */ + protected void fireNodesInserted(Object[] path, int[] indexes, Object[] objects) { //System.out.println( "fireNodesInserted: " + this ); - parentAssociation.fireTreeNodesInserted( - this, path, indexes, objects ); - } - - /** - * Subclasses should override this method to fire an appropriate change event. - */ - protected void fireNodesChanged( Object[] path, int[] indexes, Object[] objects ) - { + parentAssociation.fireTreeNodesInserted(this, path, indexes, objects); + } + + /** + * Subclasses should override this method to fire an appropriate change event. + */ + protected void fireNodesChanged(Object[] path, int[] indexes, Object[] objects) { //System.out.println( "fireNodesChanged: " + this ); - parentAssociation.fireTreeNodesChanged( - this, path, indexes, objects ); - } - - /** - * Subclasses should override this method to fire an appropriate deletion event. - */ - protected void fireNodesRemoved( Object[] path, int[] indexes, Object[] objects ) - { + parentAssociation.fireTreeNodesChanged(this, path, indexes, objects); + } + + /** + * Subclasses should override this method to fire an appropriate deletion event. + */ + protected void fireNodesRemoved(Object[] path, int[] indexes, Object[] objects) { //System.out.println( "fireNodesRemoved: " + this ); - parentAssociation.fireTreeNodesRemoved( - this, path, indexes, objects ); - } - - /** - * Subclasses should override this method to fire an appropriate event. - */ - protected void fireStructureChanged( Object[] path, int[] indexes, Object[] objects ) - { - parentAssociation.fireTreeStructureChanged( - this, path, indexes, objects ); - } - - /** - * Overridden to broadcast a tree event after super executes. - */ - public void insertObjectAtIndex ( Object anObject, int anIndex ) - { - int count = getChildCount(); // gets old count - if ( target == null ) - { - // if root node, forward to parent: - // circumventing delegating data source, if any - EODataSource dataSource = parentGroup.dataSource(); - if ( dataSource instanceof DelegatingTreeDataSource ) - { - parentGroup.setDataSource( - ((DelegatingTreeDataSource)dataSource).delegateDataSource ); - } - parentGroup.insertObjectAtIndex( anObject, anIndex ); - if ( dataSource instanceof DelegatingTreeDataSource ) - { - parentGroup.setDataSource( dataSource ); - } - return; // prevent event from firing (?) - } - else // not root node - { - super.insertObjectAtIndex( anObject, anIndex ); - } - } - - /** - * Overridden to broadcast a tree event after super executes. - */ - public boolean deleteObjectAtIndex ( int anIndex ) - { - boolean result; - Object node = getChildNodeAt( anIndex ); - if ( target == null ) - { - // if root node, forward to parent: - result = parentGroup.deleteObjectAtIndex( anIndex ); - } - else // not root node - { - result = super.deleteObjectAtIndex( anIndex ); - } - - return result; - } - - /** - * Returns the child node that corresponds to the - * specified index, creating it if necessary. - * The index must be within bounds or an exception - * is thrown. - */ - public DisplayGroupNode getChildNodeAt( int anIndex ) - { - boolean wasFetched = isFetched(); - if ( ! wasFetched ) fetch(); - Object o = displayedObjects.objectAtIndex( anIndex ); - DisplayGroupNode result = getChildNodeForObject( o ); - if ( result == null ) - { - result = createChildNodeForObject( o ); - } - return result; - } - - /** - * Returns a child node that corresponds to the - * specified object, returning null if not found. - */ - protected DisplayGroupNode getChildNodeForObject( Object anObject ) - { - return (DisplayGroupNode) - childNodes.objectForKey( new ReferenceKey( anObject ) ); - } - - /** - * Creates a child node that corresponds to the - * specified object. - */ - private DisplayGroupNode createChildNodeForObject( Object anObject ) - { - DisplayGroupNode result = parentAssociation.createNode( this, anObject ); - childNodes.setObjectForKey( result, new ReferenceKey( anObject ) ); - return result; - } - - /** - * Returns a tree path of all DisplayGroupNodes leading - * to this node, including the root node (but excluding the - * titles display group). - */ - public TreePath treePath() - { - List path = new LinkedList(); - EODisplayGroup node = this; - while ( node instanceof DisplayGroupNode ) - { - // insert at head of list - path.add( 0, node ); - node = ((DisplayGroupNode)node).parentGroup; - } - return new TreePath( path.toArray() ); - } - - /** - * Overridden to return the parent group's - * sort ordering if useParentOrderings is true. - * useParentOrderings is true by default. - */ - public NSArray sortOrderings() - { - if ( ( useParentOrderings ) - && ( parentGroup != null ) ) - { - return parentGroup.sortOrderings(); - } - return super.sortOrderings(); - } - - /** - * Overridden to set useParentOrderings to false, - * or true if aList is null. - */ - public void setSortOrderings ( List aList ) - { - if ( aList == null ) - { - useParentOrderings = true; - } - else - { - useParentOrderings = false; - super.setSortOrderings( aList ); - } - } - - /** - * Overridden to return the parent group's - * qualifier if useParentQualifier is true. - * useParentQualifier is true by default. - */ - public EOQualifier qualifier() - { - if ( ( useParentQualifier ) - && ( parentGroup != null ) ) - { - return parentGroup.qualifier(); - } - return super.qualifier(); - } - - /** - * Overridden to set useParentQualifier to false, - * or true if aList is null. - */ - public void setQualifier ( EOQualifier aQualifier ) - { - if ( aQualifier == null ) - { - useParentQualifier = true; - } - else - { - useParentQualifier = false; - super.setQualifier( aQualifier ); - } - } - - /** - * Overridden to set isFetched to true. - */ - public boolean fetch() - { + parentAssociation.fireTreeNodesRemoved(this, path, indexes, objects); + } + + /** + * Subclasses should override this method to fire an appropriate event. + */ + protected void fireStructureChanged(Object[] path, int[] indexes, Object[] objects) { + parentAssociation.fireTreeStructureChanged(this, path, indexes, objects); + } + + /** + * Overridden to broadcast a tree event after super executes. + */ + public void insertObjectAtIndex(Object anObject, int anIndex) { + int count = getChildCount(); // gets old count + if (target == null) { + // if root node, forward to parent: + // circumventing delegating data source, if any + EODataSource dataSource = parentGroup.dataSource(); + if (dataSource instanceof DelegatingTreeDataSource) { + parentGroup.setDataSource(((DelegatingTreeDataSource) dataSource).delegateDataSource); + } + parentGroup.insertObjectAtIndex(anObject, anIndex); + if (dataSource instanceof DelegatingTreeDataSource) { + parentGroup.setDataSource(dataSource); + } + return; // prevent event from firing (?) + } else // not root node + { + super.insertObjectAtIndex(anObject, anIndex); + } + } + + /** + * Overridden to broadcast a tree event after super executes. + */ + public boolean deleteObjectAtIndex(int anIndex) { + boolean result; + Object node = getChildNodeAt(anIndex); + if (target == null) { + // if root node, forward to parent: + result = parentGroup.deleteObjectAtIndex(anIndex); + } else // not root node + { + result = super.deleteObjectAtIndex(anIndex); + } + + return result; + } + + /** + * Returns the child node that corresponds to the specified index, creating it + * if necessary. The index must be within bounds or an exception is thrown. + */ + public DisplayGroupNode getChildNodeAt(int anIndex) { + boolean wasFetched = isFetched(); + if (!wasFetched) + fetch(); + Object o = displayedObjects.objectAtIndex(anIndex); + DisplayGroupNode result = getChildNodeForObject(o); + if (result == null) { + result = createChildNodeForObject(o); + } + return result; + } + + /** + * Returns a child node that corresponds to the specified object, returning null + * if not found. + */ + protected DisplayGroupNode getChildNodeForObject(Object anObject) { + return (DisplayGroupNode) childNodes.objectForKey(new ReferenceKey(anObject)); + } + + /** + * Creates a child node that corresponds to the specified object. + */ + private DisplayGroupNode createChildNodeForObject(Object anObject) { + DisplayGroupNode result = parentAssociation.createNode(this, anObject); + childNodes.setObjectForKey(result, new ReferenceKey(anObject)); + return result; + } + + /** + * Returns a tree path of all DisplayGroupNodes leading to this node, including + * the root node (but excluding the titles display group). + */ + public TreePath treePath() { + List path = new LinkedList(); + EODisplayGroup node = this; + while (node instanceof DisplayGroupNode) { + // insert at head of list + path.add(0, node); + node = ((DisplayGroupNode) node).parentGroup; + } + return new TreePath(path.toArray()); + } + + /** + * Overridden to return the parent group's sort ordering if useParentOrderings + * is true. useParentOrderings is true by default. + */ + public NSArray sortOrderings() { + if ((useParentOrderings) && (parentGroup != null)) { + return parentGroup.sortOrderings(); + } + return super.sortOrderings(); + } + + /** + * Overridden to set useParentOrderings to false, or true if aList is null. + */ + public void setSortOrderings(List aList) { + if (aList == null) { + useParentOrderings = true; + } else { + useParentOrderings = false; + super.setSortOrderings(aList); + } + } + + /** + * Overridden to return the parent group's qualifier if useParentQualifier is + * true. useParentQualifier is true by default. + */ + public EOQualifier qualifier() { + if ((useParentQualifier) && (parentGroup != null)) { + return parentGroup.qualifier(); + } + return super.qualifier(); + } + + /** + * Overridden to set useParentQualifier to false, or true if aList is null. + */ + public void setQualifier(EOQualifier aQualifier) { + if (aQualifier == null) { + useParentQualifier = true; + } else { + useParentQualifier = false; + super.setQualifier(aQualifier); + } + } + + /** + * Overridden to set isFetched to true. + */ + public boolean fetch() { //System.out.println( "DisplayGroupNode.fetch: " + this + " : " ); //if ( getClass().getName().indexOf( "Activity" ) != -1 ) //{ // new net.wotonomy.ui.swing.util.StackTraceInspector( this.toString() ); //} - // set flag - setFetched( true ); + // set flag + setFetched(true); - // skip root node - if ( target == null ) return true; + // skip root node + if (target == null) + return true; - // requalify - dataSource().qualifyWithRelationshipKey( - parentAssociation.childrenKey, target ); + // requalify + dataSource().qualifyWithRelationshipKey(parentAssociation.childrenKey, target); - // call to super - return super.fetch(); + // call to super + return super.fetch(); //boolean result = super.fetch(); //System.out.println( displayedObjects() ); //return result; - } - - /** - * Returns the object at the appropriate index - * in the parent display group. - */ - public Object object() - { - // if root node - if ( target == null ) - { - return parentAssociation.rootLabel(); - } - return target; - } - - /** - * Returns the string value of the title property - * on the object in the parent display group corresponding - * to this index. The tree renderer asks JTrees to - * call this method to retrieve a value for display. - */ - public String toString() - { - Object result = getUserObject(); - if ( result == null ) result = "[null]"; - return result.toString(); - } - - // parts of interface TreeNode - - public int getChildCount() - { - if ( ! isFetched() ) fetch(); + } + + /** + * Returns the object at the appropriate index in the parent display group. + */ + public Object object() { + // if root node + if (target == null) { + return parentAssociation.rootLabel(); + } + return target; + } + + /** + * Returns the string value of the title property on the object in the parent + * display group corresponding to this index. The tree renderer asks JTrees to + * call this method to retrieve a value for display. + */ + public String toString() { + Object result = getUserObject(); + if (result == null) + result = "[null]"; + return result.toString(); + } + + // parts of interface TreeNode + + public int getChildCount() { + if (!isFetched()) + fetch(); //if ( toString().indexOf("154.16406")!=-1){ //System.out.println( "getChildCount: " + displayedObjects.count() + " : " + this ); //new RuntimeException().printStackTrace(); //net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace(); //} - return displayedObjects.count(); - } - - public int getIndex(DisplayGroupNode node) - { - if ( ! isFetched() ) fetch(); - return displayedObjects.indexOfObject( - ((DisplayGroupNode)node).target ); - } - - public boolean getAllowsChildren() - { - return true; - } - - public boolean isLeaf() - { - // if not root node and isLeaf aspect is bound - if ( ( target != null ) - && ( parentGroup != null ) - && ( parentAssociation.leafKey != null ) ) - { - Object value; - if ( parentAssociation.leafDisplayGroup != null ) - { - value = parentGroup.valueForObject( - target, parentAssociation.leafKey ); - } - else - { - value = parentAssociation.leafKey; - } - - // getBoolean returns true for zero, among other things - Object result = ValueConverter.getBoolean( value ); - if ( result != null ) - { - return ((Boolean)result).booleanValue(); - } - } - - // otherwise, we have to fetch and return count - return ( getChildCount() == 0 ); - } - - public Enumeration children() - { - int count = getChildCount(); - Vector v = new Vector(); - for ( int i = 0; i < count; i++ ) - { - v.add( getChildNodeAt( i ) ); - } - return v.elements(); - } - - // parts of interface MutableTreeNode - - public void insert(DisplayGroupNode aChild, int anIndex) - { - insertObjectAtIndex( - ((DisplayGroupNode)aChild).object(), anIndex ); - } - - public void remove(int index) - { - deleteObjectAtIndex( index ); - } - - /** - * Removes the node at the index corresponding - * to the index of the object. - */ - public void remove(DisplayGroupNode node) - { - remove( getIndex( node ) ); - } - - /** - * Removes our object from the parent display group. - */ - public void removeFromParent() - { - int index = parentGroup.displayedObjects().indexOfIdenticalObject( target ); - if ( index != NSArray.NotFound ) - { - parentGroup.deleteObjectAtIndex( index ); - } - else - { - throw new WotonomyException( - "Object not found in parent group: " + target ); - } - } - - /** - * Removes our object from the parent display group - * and adds it to the end of the specified node's children. - */ - public void setParent(DisplayGroupNode newParent) - { - removeFromParent(); - newParent.insertObjectAtIndex( - object(), newParent.displayedObjects.size() ); - } - - /** - * Returns the value of the displayed property in the parent display group - * at the index that corresponds to the index of this node. - */ - public Object getUserObject() - { - return valueForKey( parentAssociation.titlesKey ); - } - - /** - * Sets the value of the displayed property in the parent display group - * at the index that corresponds to the index of this node. - */ - public void setUserObject( Object aValue ) - { - setValueForKey( aValue, parentAssociation.titlesKey ); - } - - /** - * Returns a value from the object in the parent display group - * at the index that corresponds to the index of this node. - * For the root node, if the titles key is specified, the root - * label is returned, otherwise null is returned. - */ - public Object valueForKey( String aKey ) - { - // if root node - if ( target == null ) - { - // compare by ref is okay for strings - if ( aKey == parentAssociation.titlesKey ) - { - return parentAssociation.rootLabel(); - } - return null; - } - return parentGroup.valueForObject( target, aKey ); - } - - /** - * Sets a value on the object in the parent display group - * at the index that corresponds to the index of this node. - * For the root node, this method only works if aKey is the - * titlesAspect's key, otherwise does nothing. - */ - public void setValueForKey(Object aValue, String aKey) - { - // if root node, return. - if ( target == null ) - { - // compare by ref is okay for strings - if ( aKey == parentAssociation.titlesKey ) - { - parentAssociation.setRootLabel( aValue ); - - // how to handle root node? tree event docs don't say. - fireNodesChanged ( treePath().getPath(), - new int[] { 0 }, - new Object[] { this } ); - } - return; - } - - parentGroup.setValueForObject( - aValue, target, aKey ); - } - - /** - * Perform any clean up in this method. - * The node will not be reused after this method is called. - * This implementation removes itself from the parent's - * set of child nodes, sets target and datasource to null, - * and then calls disposeChildNodes(). - */ - protected void dispose() - { //System.out.println( "dispose: " + this.getClass().getName() + " : " + this ); - if ( parentGroup != null ) - { - ((DisplayGroupNode)parentGroup).childNodes.remove( - new ReferenceKey( target ) ); - } - setTarget( (Object) null ); - setDataSource( null ); - disposeChildNodes(); - } - - /** - * Calls dispose() on all child nodes. - */ - protected void disposeChildNodes() - { - Iterator i = new LinkedList(childNodes.values()).iterator(); - while ( i.hasNext() ) - { - ((DisplayGroupNode) i.next()).dispose(); - } - } - - /** - * Called after the target object posts a change notification. - * This implementation re-fetches which triggers - * updateDisplayedObjects to broadcast any tree events. - * This method marks the parent object as changed if: - * (1) this object is not registered in the editing context - * of the titles display group's data source (if any), AND - * (2) the children key is not in the list of attributes - * of the parent object's EOClassDescription. - */ - public void targetChanged() - { - // if not root node - if ( target != null ) - { - // if we're not root and not fetched, stop here. - //FIXME: with this, some nodes have old values when moved. - //FIXME: without this, nodes are unnecessarily fetched. - //FIXME: might have parent modify isFetched of certain child nodes. - if ( isFetched() ) - { - fetch(); - } - else // not fetched - just update the display - { - updateDisplayedObjects(); - } -/* -//disabling this for performance reasons: -//might reenable later or find an alternate approach - // check to see if we need to mark the parent object as changed - EOEditingContext context = dataSource().editingContext(); - if ( ( context == null ) - || ( context.globalIDForObject( target ) == null ) ) - { - DisplayGroupNode parentNode = (DisplayGroupNode) parentGroup; - if ( parentNode.target != null ) - { - // only notify if childrenKey is an attribute of parentDesc - // (and therefore not a toOne or toMany relationship) - EOClassDescription parentDesc = - EOClassDescription.classDescriptionForClass( - parentNode.target.getClass() ); - if ( parentDesc.attributeKeys().contains( parentAssociation.childrenKey ) ) - { - // only notify if no context is already observing the object - // and we are an attribute key - EOObserverCenter.notifyObserversObjectWillChange( parentNode.target ); - } - } - } -*/ - } - else // root node - { - setObjectArray( parentAssociation.titlesDisplayGroup.displayedObjects() ); - } - - // finally, broadcast change event for this node - // even though we're not sure if the displayed value changed. - fireNodeChanged(); - } - - /** - * Fires a change event for this node. - */ - public void fireNodeChanged() - { - // if not root node - if ( target != null ) - { - int index = ((DisplayGroupNode)parentGroup).getIndex( this ); - if ( ( index != -1 ) - && ( treePath().getParentPath() != null ) ) - { - fireNodesChanged ( - treePath().getParentPath().getPath(), - new int[] { index }, - new Object[] { this } ); - } - } - } - -Object[] previouslyDisplayedObjects = new Object[0]; - /** - * Overridden to call to super, fire any tree events, and then - * call updateDisplayedObjects on all fetched child nodes. - * This method compares this node's displayed objects against - * the list of child nodes, synchronizes them, and then broadcasts - * only the necessary events to bring the view component up to date. - */ - public void updateDisplayedObjects() - { + return displayedObjects.count(); + } + + public int getIndex(DisplayGroupNode node) { + if (!isFetched()) + fetch(); + return displayedObjects.indexOfObject(((DisplayGroupNode) node).target); + } + + public boolean getAllowsChildren() { + return true; + } + + public boolean isLeaf() { + // if not root node and isLeaf aspect is bound + if ((target != null) && (parentGroup != null) && (parentAssociation.leafKey != null)) { + Object value; + if (parentAssociation.leafDisplayGroup != null) { + value = parentGroup.valueForObject(target, parentAssociation.leafKey); + } else { + value = parentAssociation.leafKey; + } + + // getBoolean returns true for zero, among other things + Object result = ValueConverter.getBoolean(value); + if (result != null) { + return ((Boolean) result).booleanValue(); + } + } + + // otherwise, we have to fetch and return count + return (getChildCount() == 0); + } + + public Enumeration children() { + int count = getChildCount(); + Vector v = new Vector(); + for (int i = 0; i < count; i++) { + v.add(getChildNodeAt(i)); + } + return v.elements(); + } + + // parts of interface MutableTreeNode + + public void insert(DisplayGroupNode aChild, int anIndex) { + insertObjectAtIndex(((DisplayGroupNode) aChild).object(), anIndex); + } + + public void remove(int index) { + deleteObjectAtIndex(index); + } + + /** + * Removes the node at the index corresponding to the index of the object. + */ + public void remove(DisplayGroupNode node) { + remove(getIndex(node)); + } + + /** + * Removes our object from the parent display group. + */ + public void removeFromParent() { + int index = parentGroup.displayedObjects().indexOfIdenticalObject(target); + if (index != NSArray.NotFound) { + parentGroup.deleteObjectAtIndex(index); + } else { + throw new WotonomyException("Object not found in parent group: " + target); + } + } + + /** + * Removes our object from the parent display group and adds it to the end of + * the specified node's children. + */ + public void setParent(DisplayGroupNode newParent) { + removeFromParent(); + newParent.insertObjectAtIndex(object(), newParent.displayedObjects.size()); + } + + /** + * Returns the value of the displayed property in the parent display group at + * the index that corresponds to the index of this node. + */ + public Object getUserObject() { + return valueForKey(parentAssociation.titlesKey); + } + + /** + * Sets the value of the displayed property in the parent display group at the + * index that corresponds to the index of this node. + */ + public void setUserObject(Object aValue) { + setValueForKey(aValue, parentAssociation.titlesKey); + } + + /** + * Returns a value from the object in the parent display group at the index that + * corresponds to the index of this node. For the root node, if the titles key + * is specified, the root label is returned, otherwise null is returned. + */ + public Object valueForKey(String aKey) { + // if root node + if (target == null) { + // compare by ref is okay for strings + if (aKey == parentAssociation.titlesKey) { + return parentAssociation.rootLabel(); + } + return null; + } + return parentGroup.valueForObject(target, aKey); + } + + /** + * Sets a value on the object in the parent display group at the index that + * corresponds to the index of this node. For the root node, this method only + * works if aKey is the titlesAspect's key, otherwise does nothing. + */ + public void setValueForKey(Object aValue, String aKey) { + // if root node, return. + if (target == null) { + // compare by ref is okay for strings + if (aKey == parentAssociation.titlesKey) { + parentAssociation.setRootLabel(aValue); + + // how to handle root node? tree event docs don't say. + fireNodesChanged(treePath().getPath(), new int[] { 0 }, new Object[] { this }); + } + return; + } + + parentGroup.setValueForObject(aValue, target, aKey); + } + + /** + * Perform any clean up in this method. The node will not be reused after this + * method is called. This implementation removes itself from the parent's set of + * child nodes, sets target and datasource to null, and then calls + * disposeChildNodes(). + */ + protected void dispose() { // System.out.println( "dispose: " + this.getClass().getName() + " : " + this ); + if (parentGroup != null) { + ((DisplayGroupNode) parentGroup).childNodes.remove(new ReferenceKey(target)); + } + setTarget((Object) null); + setDataSource(null); + disposeChildNodes(); + } + + /** + * Calls dispose() on all child nodes. + */ + protected void disposeChildNodes() { + Iterator i = new LinkedList(childNodes.values()).iterator(); + while (i.hasNext()) { + ((DisplayGroupNode) i.next()).dispose(); + } + } + + /** + * Called after the target object posts a change notification. This + * implementation re-fetches which triggers updateDisplayedObjects to broadcast + * any tree events. This method marks the parent object as changed if: (1) this + * object is not registered in the editing context of the titles display group's + * data source (if any), AND (2) the children key is not in the list of + * attributes of the parent object's EOClassDescription. + */ + public void targetChanged() { + // if not root node + if (target != null) { + // if we're not root and not fetched, stop here. + // FIXME: with this, some nodes have old values when moved. + // FIXME: without this, nodes are unnecessarily fetched. + // FIXME: might have parent modify isFetched of certain child nodes. + if (isFetched()) { + fetch(); + } else // not fetched - just update the display + { + updateDisplayedObjects(); + } + /* + * //disabling this for performance reasons: //might reenable later or find an + * alternate approach // check to see if we need to mark the parent object as + * changed EOEditingContext context = dataSource().editingContext(); if ( ( + * context == null ) || ( context.globalIDForObject( target ) == null ) ) { + * DisplayGroupNode parentNode = (DisplayGroupNode) parentGroup; if ( + * parentNode.target != null ) { // only notify if childrenKey is an attribute + * of parentDesc // (and therefore not a toOne or toMany relationship) + * EOClassDescription parentDesc = EOClassDescription.classDescriptionForClass( + * parentNode.target.getClass() ); if ( parentDesc.attributeKeys().contains( + * parentAssociation.childrenKey ) ) { // only notify if no context is already + * observing the object // and we are an attribute key + * EOObserverCenter.notifyObserversObjectWillChange( parentNode.target ); } } } + */ + } else // root node + { + setObjectArray(parentAssociation.titlesDisplayGroup.displayedObjects()); + } + + // finally, broadcast change event for this node + // even though we're not sure if the displayed value changed. + fireNodeChanged(); + } + + /** + * Fires a change event for this node. + */ + public void fireNodeChanged() { + // if not root node + if (target != null) { + int index = ((DisplayGroupNode) parentGroup).getIndex(this); + if ((index != -1) && (treePath().getParentPath() != null)) { + fireNodesChanged(treePath().getParentPath().getPath(), new int[] { index }, new Object[] { this }); + } + } + } + + Object[] previouslyDisplayedObjects = new Object[0]; + + /** + * Overridden to call to super, fire any tree events, and then call + * updateDisplayedObjects on all fetched child nodes. This method compares this + * node's displayed objects against the list of child nodes, synchronizes them, + * and then broadcasts only the necessary events to bring the view component up + * to date. + */ + public void updateDisplayedObjects() { //System.out.println( "updateDisplayedObjects: " + " : " + this ); //net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace(); //new RuntimeException().printStackTrace(); - super.updateDisplayedObjects(); - - // diff lists - boolean proceed = true; - Object[] oldObjects = previouslyDisplayedObjects; - Object[] newObjects = displayedObjects.toArray(); - if ( oldObjects.length == newObjects.length ) - { - proceed = false; - for ( int i = 0; i < newObjects.length; i++ ) - { - if ( oldObjects[i] != newObjects[i] ) - { - proceed = true; - break; - } - } - } - - // this should be set before firing the change events - // in case some clients end up calling this again. - previouslyDisplayedObjects = newObjects; - - DisplayGroupNode node; - Iterator i = childNodes.values().iterator(); - while ( i.hasNext() ) - { - node = (DisplayGroupNode) i.next(); - if ( !node.isFetchNeeded() ) - { - node.updateDisplayedObjects(); - } - } - - if ( proceed ) - { + super.updateDisplayedObjects(); + + // diff lists + boolean proceed = true; + Object[] oldObjects = previouslyDisplayedObjects; + Object[] newObjects = displayedObjects.toArray(); + if (oldObjects.length == newObjects.length) { + proceed = false; + for (int i = 0; i < newObjects.length; i++) { + if (oldObjects[i] != newObjects[i]) { + proceed = true; + break; + } + } + } + + // this should be set before firing the change events + // in case some clients end up calling this again. + previouslyDisplayedObjects = newObjects; + + DisplayGroupNode node; + Iterator i = childNodes.values().iterator(); + while (i.hasNext()) { + node = (DisplayGroupNode) i.next(); + if (!node.isFetchNeeded()) { + node.updateDisplayedObjects(); + } + } + + if (proceed) { //System.out.println( "DisplayGroupNode.firingEventsForChanges: " ); //new RuntimeException().printStackTrace(); - fireEventsForChanges( oldObjects, newObjects ); - } - - } - - /** - * Called by processRecentChanges to analyze the - * differences between the lists and broadcast the - * appropriate events. - */ - protected void fireEventsForChanges( - Object[] oldObjects, Object[] newObjects ) - { - // structure changed causes havoc while - // establishing connection in some cases - //if ( oldObjects.length == 0 || newObjects.length == 0 ) - //{ - // fireStructureChanged( treePath().getPath(), null, null ); - // return; - //} - - int insertCount = 0; - int deleteCount = 0; - Object[] inserts = new Object[ newObjects.length ]; - Object[] deletes = new Object[ oldObjects.length ]; - - int i; - int n = -1, o = -1; // last match - int n1 = 0, o1 = 0; // current match test - int n2 = 0, o2 = 0; // scan ahead - - while ( o1 < oldObjects.length && n1 < newObjects.length ) - { - if ( newObjects[n1] == oldObjects[o1] ) - { - // mark as match and continue - o = o1; - n = n1; - } - else - { - // scan ahead for the next match, if any - o2 = o1; - n2 = n1; - - while ( o2 < oldObjects.length || n2 < newObjects.length ) - { - if ( o2 < oldObjects.length && newObjects[n1] == oldObjects[o2] ) - { - // run o1 to o2: mark as deletes - for ( i = o1; i < o2; i++ ) - { // System.out.println( "delete : " + i ); - deletes[i] = oldObjects[i]; - deleteCount++; - } - o1 = o2; // reset test - o = o1; // set match - n = n1; // set match - break; - } - if ( n2 < newObjects.length && newObjects[n2] == oldObjects[o1] ) - { - // run n1 to n2: mark as inserts - for ( i = n1; i < n2; i++ ) - { // System.out.println( "insert : " + i ); - inserts[i] = newObjects[i]; - insertCount++; - } - n1 = n2; // reset test - n = n1; // set match - o = o1; // set match - break; - } - o2++; - n2++; - } - } - if (n != n1) - { - inserts[n1] = newObjects[n1]; - insertCount++; - deletes[o1] = oldObjects[o1]; - deleteCount++; - //increment even though no match: - //the new object was marked as inserted and - //the old object was marked as deleted. - n = n1; - o = o1; - } - o1++; - n1++; - } - - // run o to end of oldObjects: mark as deletes - for ( i = o+1; i < oldObjects.length; i++ ) - { // System.out.println( "delete : " + i ); - deletes[i] = oldObjects[i]; - deleteCount++; - } - - // run n to end of newObjects: mark as inserts - for ( i = n+1; i < newObjects.length; i++ ) - { // System.out.println( "insert : " + i ); - inserts[i] = newObjects[i]; - insertCount++; - } + fireEventsForChanges(oldObjects, newObjects); + } + + } + + /** + * Called by processRecentChanges to analyze the differences between the lists + * and broadcast the appropriate events. + */ + protected void fireEventsForChanges(Object[] oldObjects, Object[] newObjects) { + // structure changed causes havoc while + // establishing connection in some cases + // if ( oldObjects.length == 0 || newObjects.length == 0 ) + // { + // fireStructureChanged( treePath().getPath(), null, null ); + // return; + // } + + int insertCount = 0; + int deleteCount = 0; + Object[] inserts = new Object[newObjects.length]; + Object[] deletes = new Object[oldObjects.length]; + + int i; + int n = -1, o = -1; // last match + int n1 = 0, o1 = 0; // current match test + int n2 = 0, o2 = 0; // scan ahead + + while (o1 < oldObjects.length && n1 < newObjects.length) { + if (newObjects[n1] == oldObjects[o1]) { + // mark as match and continue + o = o1; + n = n1; + } else { + // scan ahead for the next match, if any + o2 = o1; + n2 = n1; + + while (o2 < oldObjects.length || n2 < newObjects.length) { + if (o2 < oldObjects.length && newObjects[n1] == oldObjects[o2]) { + // run o1 to o2: mark as deletes + for (i = o1; i < o2; i++) { // System.out.println( "delete : " + i ); + deletes[i] = oldObjects[i]; + deleteCount++; + } + o1 = o2; // reset test + o = o1; // set match + n = n1; // set match + break; + } + if (n2 < newObjects.length && newObjects[n2] == oldObjects[o1]) { + // run n1 to n2: mark as inserts + for (i = n1; i < n2; i++) { // System.out.println( "insert : " + i ); + inserts[i] = newObjects[i]; + insertCount++; + } + n1 = n2; // reset test + n = n1; // set match + o = o1; // set match + break; + } + o2++; + n2++; + } + } + if (n != n1) { + inserts[n1] = newObjects[n1]; + insertCount++; + deletes[o1] = oldObjects[o1]; + deleteCount++; + // increment even though no match: + // the new object was marked as inserted and + // the old object was marked as deleted. + n = n1; + o = o1; + } + o1++; + n1++; + } + + // run o to end of oldObjects: mark as deletes + for (i = o + 1; i < oldObjects.length; i++) { // System.out.println( "delete : " + i ); + deletes[i] = oldObjects[i]; + deleteCount++; + } + + // run n to end of newObjects: mark as inserts + for (i = n + 1; i < newObjects.length; i++) { // System.out.println( "insert : " + i ); + inserts[i] = newObjects[i]; + insertCount++; + } //System.out.println( "done : " //+ o + " : " + o1 + " : " + o2 + " :: " + n + " : " + n1 + " : " + n2 ); @@ -948,571 +774,481 @@ Object[] previouslyDisplayedObjects = new Object[0]; //System.out.println( new NSArray( inserts ) ); //System.out.println( new NSArray( deletes ) ); //System.out.println( new NSArray( oldObjects ) ); - - int c; - Object[] nodes; - int[] indices; - - // broadcast delete event - c = 0; - nodes = new Object[ deleteCount ]; - indices = new int[ deleteCount ]; - for ( i = 0; i < deletes.length; i++ ) - { - if ( deletes[i] != null ) - { - indices[c] = i; - nodes[c] = getChildNodeForObject( deletes[i] ); - c++; - } - } - if ( c > 0 ) - { + + int c; + Object[] nodes; + int[] indices; + + // broadcast delete event + c = 0; + nodes = new Object[deleteCount]; + indices = new int[deleteCount]; + for (i = 0; i < deletes.length; i++) { + if (deletes[i] != null) { + indices[c] = i; + nodes[c] = getChildNodeForObject(deletes[i]); + c++; + } + } + if (c > 0) { // fireNodeChanged(); // force the jtree to get the correct child count - fireNodesRemoved( treePath().getPath(), indices, nodes ); - } - deletes = nodes; // retain for dispose check - - // broadcast insert event - c = 0; - nodes = new Object[ insertCount ]; - indices = new int[ insertCount ]; - for ( i = 0; i < inserts.length; i++ ) - { - if ( inserts[i] != null ) - { - indices[c] = i; - nodes[c] = getChildNodeForObject( inserts[i] ); - if ( nodes[c] == null ) - { - nodes[c] = createChildNodeForObject( newObjects[i] ); - } - c++; - } - } - if ( c > 0 ) - { - fireNodesInserted( treePath().getPath(), indices, nodes ); - } - - // dispose any delete nodes not on insert list - int j; - boolean found; - for ( i = 0; i < deletes.length; i++ ) - { - for ( j = 0; j < nodes.length; j++ ) - { - if ( deletes[i] == nodes[j] ) break; - } - - // did not break early, so not found, so dispose - if ( j == nodes.length ) - { - ((DisplayGroupNode)deletes[i]).dispose(); - } - } - } - - /** - * Sets the target object and creates an registers a target observer. - * If target was not previously null, the existing observer is unregistered. - * Protected access so subclasses and TreeModelAssociation can update our target. - */ - public void setTarget( Object aTarget ) - { - if ( target != null ) - { - EOObserverCenter.removeObserver( targetObserver, target ); - targetObserver.discardPendingNotification(); - } - - if ( aTarget != null ) - { - target = aTarget; - targetObserver = new TargetObserver( this ); - EOObserverCenter.addObserver( targetObserver, target ); - } - } - - /** - * Returns the parent display group, or null if parent is root. - */ - public DisplayGroupNode getParentGroup() - { - if ( parentGroup instanceof DisplayGroupNode ) - { - return (DisplayGroupNode)parentGroup; - } - // presumably the root node - return null; - } - - /** - * Gets all descendants of the this node. - */ - public List getDescendants() - { - return getDescendants( this, true ); - } - - /** - * Gets only the descendants of the this node - * whose children has been loaded - no fetching - * will occur. Useful for load-on-demand trees. - */ - public List getLoadedDescendants() - { - return getDescendants( this, false ); - } - - // breadth first traversal implementation - - /** - * Returns a list of all descendants of the - * specified node. Unfetched nodes are traversed - * only if forceLoad is true. - * This implementation is a breadth-first traversal - * of the nodes starting at the specified node. - */ - static private List getDescendants( DisplayGroupNode aNode, boolean forceLoad ) - { - if ( !forceLoad && !aNode.isFetched ) return NSArray.EmptyArray; - - LinkedList result = new LinkedList(); - LinkedList queue = new LinkedList(); - - queue.add( aNode ); - while ( ! queue.isEmpty() ) - { - checkNode( (DisplayGroupNode) queue.removeFirst(), - queue, result, forceLoad ); - } - - return result; - } - - /** - * Adds each fetched child node of the specified node to - * the result set (optionally forcing the child node to load) - * and adding child node to the end of the queue. - */ - static private void checkNode( DisplayGroupNode aNode, - LinkedList aQueue, LinkedList aResult, boolean forceLoad ) - { - DisplayGroupNode child; - int count = aNode.getChildCount(); - - for ( int i = 0; i < count; i++ ) - { - child = aNode.getChildNodeAt( i ); - - // add to queue if node has fetched children - if ( ( !child.isFetched ) && ( forceLoad ) ) - { - child.fetch(); - } - if ( child.isFetched ) - { - aQueue.addLast( child ); - } - - aResult.add( child ); - } - } - - /** - * Overridden to not fetch on InvalidateAllObjectsInStoreNotification - * unless we've already been fetched, preserving the load-on-demand - * functionality. - */ - public void objectsInvalidatedInEditingContext( NSNotification aNotification ) - { - if ( EOObjectStore.InvalidatedAllObjectsInStoreNotification - .equals( aNotification.name() ) ) - { + fireNodesRemoved(treePath().getPath(), indices, nodes); + } + deletes = nodes; // retain for dispose check + + // broadcast insert event + c = 0; + nodes = new Object[insertCount]; + indices = new int[insertCount]; + for (i = 0; i < inserts.length; i++) { + if (inserts[i] != null) { + indices[c] = i; + nodes[c] = getChildNodeForObject(inserts[i]); + if (nodes[c] == null) { + nodes[c] = createChildNodeForObject(newObjects[i]); + } + c++; + } + } + if (c > 0) { + fireNodesInserted(treePath().getPath(), indices, nodes); + } + + // dispose any delete nodes not on insert list + int j; + boolean found; + for (i = 0; i < deletes.length; i++) { + for (j = 0; j < nodes.length; j++) { + if (deletes[i] == nodes[j]) + break; + } + + // did not break early, so not found, so dispose + if (j == nodes.length) { + ((DisplayGroupNode) deletes[i]).dispose(); + } + } + } + + /** + * Sets the target object and creates an registers a target observer. If target + * was not previously null, the existing observer is unregistered. Protected + * access so subclasses and TreeModelAssociation can update our target. + */ + public void setTarget(Object aTarget) { + if (target != null) { + EOObserverCenter.removeObserver(targetObserver, target); + targetObserver.discardPendingNotification(); + } + + if (aTarget != null) { + target = aTarget; + targetObserver = new TargetObserver(this); + EOObserverCenter.addObserver(targetObserver, target); + } + } + + /** + * Returns the parent display group, or null if parent is root. + */ + public DisplayGroupNode getParentGroup() { + if (parentGroup instanceof DisplayGroupNode) { + return (DisplayGroupNode) parentGroup; + } + // presumably the root node + return null; + } + + /** + * Gets all descendants of the this node. + */ + public List getDescendants() { + return getDescendants(this, true); + } + + /** + * Gets only the descendants of the this node whose children has been loaded - + * no fetching will occur. Useful for load-on-demand trees. + */ + public List getLoadedDescendants() { + return getDescendants(this, false); + } + + // breadth first traversal implementation + + /** + * Returns a list of all descendants of the specified node. Unfetched nodes are + * traversed only if forceLoad is true. This implementation is a breadth-first + * traversal of the nodes starting at the specified node. + */ + static private List getDescendants(DisplayGroupNode aNode, boolean forceLoad) { + if (!forceLoad && !aNode.isFetched) + return NSArray.EmptyArray; + + LinkedList result = new LinkedList(); + LinkedList queue = new LinkedList(); + + queue.add(aNode); + while (!queue.isEmpty()) { + checkNode((DisplayGroupNode) queue.removeFirst(), queue, result, forceLoad); + } + + return result; + } + + /** + * Adds each fetched child node of the specified node to the result set + * (optionally forcing the child node to load) and adding child node to the end + * of the queue. + */ + static private void checkNode(DisplayGroupNode aNode, LinkedList aQueue, LinkedList aResult, boolean forceLoad) { + DisplayGroupNode child; + int count = aNode.getChildCount(); + + for (int i = 0; i < count; i++) { + child = aNode.getChildNodeAt(i); + + // add to queue if node has fetched children + if ((!child.isFetched) && (forceLoad)) { + child.fetch(); + } + if (child.isFetched) { + aQueue.addLast(child); + } + + aResult.add(child); + } + } + + /** + * Overridden to not fetch on InvalidateAllObjectsInStoreNotification unless + * we've already been fetched, preserving the load-on-demand functionality. + */ + public void objectsInvalidatedInEditingContext(NSNotification aNotification) { + if (EOObjectStore.InvalidatedAllObjectsInStoreNotification.equals(aNotification.name())) { //System.out.println( "DisplayGroupNode.objectsInvalidatedInEditingContext: " + aNotification.name() ); - if ( parentAssociation.isVisible( this ) && targetObserver != null ) - { - targetObserver.objectWillChange( target ); // force ui to update - fireNodeChanged(); - } - else // make sure we fetch children when we do become visible - setFetchNeeded( true ); - return; - } - else - if ( ( EOEditingContext.ObjectsChangedInEditingContextNotification - .equals( aNotification.name() ) ) - || ( EOEditingContext.EditingContextDidSaveChangesNotification - .equals( aNotification.name() ) ) ) - { - int index; - Enumeration e; - boolean didChange = false; - NSDictionary userInfo = aNotification.userInfo(); - - // if our target object was deleted - NSArray deletes = (NSArray) userInfo.objectForKey( - EOObjectStore.DeletedKey ); - if ( deletes.indexOfIdenticalObject( target ) != NSArray.NotFound ) - { + if (parentAssociation.isVisible(this) && targetObserver != null) { + targetObserver.objectWillChange(target); // force ui to update + fireNodeChanged(); + } else // make sure we fetch children when we do become visible + setFetchNeeded(true); + return; + } else if ((EOEditingContext.ObjectsChangedInEditingContextNotification.equals(aNotification.name())) + || (EOEditingContext.EditingContextDidSaveChangesNotification.equals(aNotification.name()))) { + int index; + Enumeration e; + boolean didChange = false; + NSDictionary userInfo = aNotification.userInfo(); + + // if our target object was deleted + NSArray deletes = (NSArray) userInfo.objectForKey(EOObjectStore.DeletedKey); + if (deletes.indexOfIdenticalObject(target) != NSArray.NotFound) { //System.out.println( "DisplayGroupNode.objectsInvalidatedInEditingContext: delete: " + this + " : " + aNotification.name() ); - if ( parentAssociation.isVisible( this ) && targetObserver != null ) - { - targetObserver.objectWillChange( target ); // force ui to update - fireNodeChanged(); - } - else // make sure we fetch children when we do become visible - setFetchNeeded( true ); - return; - } - - // if our target object was invalidated - NSArray invalidates = (NSArray) userInfo.objectForKey( - EOObjectStore.InvalidatedKey ); - if ( invalidates != null && - invalidates.indexOfIdenticalObject( target ) != NSArray.NotFound ) - { + if (parentAssociation.isVisible(this) && targetObserver != null) { + targetObserver.objectWillChange(target); // force ui to update + fireNodeChanged(); + } else // make sure we fetch children when we do become visible + setFetchNeeded(true); + return; + } + + // if our target object was invalidated + NSArray invalidates = (NSArray) userInfo.objectForKey(EOObjectStore.InvalidatedKey); + if (invalidates != null && invalidates.indexOfIdenticalObject(target) != NSArray.NotFound) { //System.out.println( "DisplayGroupNode.objectsInvalidatedInEditingContext: invalidate: " + this + " : " + aNotification.name() ); - if ( parentAssociation.isVisible( this ) && targetObserver != null ) - { - targetObserver.objectWillChange( target ); // force ui to update - fireNodeChanged(); - } - else // make sure we fetch children when we do become visible - setFetchNeeded( true ); - return; - } - - // if our target object was updated, set fetchNeeded plus fire changed event - NSArray updates = (NSArray) userInfo.objectForKey( - EOObjectStore.UpdatedKey ); - if ( updates.indexOfIdenticalObject( target ) != NSArray.NotFound ) - { - if ( parentAssociation.isVisible( this ) && targetObserver != null ) - { - targetObserver.objectWillChange( target ); // force ui to update - fireNodeChanged(); - if ( object() instanceof Component ) ((Component)object()).repaint(); - } - else // make sure we fetch children when we do become visible - setFetchNeeded( true ); - return; - } - } - - super.objectsInvalidatedInEditingContext( aNotification ); - - } - - // inner classes - - /** - * Private class used to force a hashmap to - * perform key comparisons by reference. - */ - private class ReferenceKey - { - private int hashCode; - private Object referent; - - public ReferenceKey( Object anObject ) - { - referent = anObject; - hashCode = anObject.hashCode(); - } - - /** - * Returns the actual key's hash code. - */ - public int hashCode() - { - return hashCode; - } - - /** - * Compares by reference. - */ - public boolean equals( Object anObject ) - { - if ( anObject instanceof ReferenceKey ) - { - return ((ReferenceKey)anObject).referent == referent; - } - return false; - } - } - - /** - * A private class to observe the target object of this node. - */ - private class TargetObserver extends EODelayedObserver - { - Reference ref; - - /** - * Pass in the display group node that will be updated - * when the target changes. - */ - public TargetObserver( DisplayGroupNode aDisplayGroup ) - { - ref = new WeakReference( aDisplayGroup ); - } - - /** - * Repopulate our display group, and calculate the deltas - * so we can broadcast appropriate events. - */ - public void subjectChanged () - { - DisplayGroupNode node = (DisplayGroupNode) ref.get(); - if ( node == null ) return; // node is null if gc'd. - //FIXME: should un-register self from observer center?? - - node.targetChanged(); - } - } + if (parentAssociation.isVisible(this) && targetObserver != null) { + targetObserver.objectWillChange(target); // force ui to update + fireNodeChanged(); + } else // make sure we fetch children when we do become visible + setFetchNeeded(true); + return; + } + + // if our target object was updated, set fetchNeeded plus fire changed event + NSArray updates = (NSArray) userInfo.objectForKey(EOObjectStore.UpdatedKey); + if (updates.indexOfIdenticalObject(target) != NSArray.NotFound) { + if (parentAssociation.isVisible(this) && targetObserver != null) { + targetObserver.objectWillChange(target); // force ui to update + fireNodeChanged(); + if (object() instanceof Component) + ((Component) object()).repaint(); + } else // make sure we fetch children when we do become visible + setFetchNeeded(true); + return; + } + } + + super.objectsInvalidatedInEditingContext(aNotification); + + } + + // inner classes + + /** + * Private class used to force a hashmap to perform key comparisons by + * reference. + */ + private class ReferenceKey { + private int hashCode; + private Object referent; + + public ReferenceKey(Object anObject) { + referent = anObject; + hashCode = anObject.hashCode(); + } + + /** + * Returns the actual key's hash code. + */ + public int hashCode() { + return hashCode; + } + + /** + * Compares by reference. + */ + public boolean equals(Object anObject) { + if (anObject instanceof ReferenceKey) { + return ((ReferenceKey) anObject).referent == referent; + } + return false; + } + } + + /** + * A private class to observe the target object of this node. + */ + private class TargetObserver extends EODelayedObserver { + Reference ref; + + /** + * Pass in the display group node that will be updated when the target changes. + */ + public TargetObserver(DisplayGroupNode aDisplayGroup) { + ref = new WeakReference(aDisplayGroup); + } + + /** + * Repopulate our display group, and calculate the deltas so we can broadcast + * appropriate events. + */ + public void subjectChanged() { + DisplayGroupNode node = (DisplayGroupNode) ref.get(); + if (node == null) + return; // node is null if gc'd. + // FIXME: should un-register self from observer center?? + + node.targetChanged(); + } + } } /* - * $Log$ - * Revision 1.2 2006/02/18 23:19:05 cgruber - * Update imports and maven dependencies. + * $Log$ Revision 1.2 2006/02/18 23:19:05 cgruber Update imports and maven + * dependencies. * - * 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.64 2003/08/06 23:07:52 chochos - * general code cleanup (mostly, removing unused imports) + * Revision 1.64 2003/08/06 23:07:52 chochos general code cleanup (mostly, + * removing unused imports) * - * Revision 1.63 2003/06/06 14:20:07 mpowers - * getLoadedDescendants was forcing a fetch of the node it was called on. + * Revision 1.63 2003/06/06 14:20:07 mpowers getLoadedDescendants was forcing a + * fetch of the node it was called on. * - * Revision 1.62 2003/06/03 14:48:33 mpowers - * Clean-up of notification handling for updates/invalidation/etc. - * Now fetching immediately on notification if the node is visible. - * This averts the infamous IndexOutOfBoundsException that occurs - * if fetching happens during repaint, because the BasicTreeUI is - * caching the number of child nodes before painting begins. + * Revision 1.62 2003/06/03 14:48:33 mpowers Clean-up of notification handling + * for updates/invalidation/etc. Now fetching immediately on notification if the + * node is visible. This averts the infamous IndexOutOfBoundsException that + * occurs if fetching happens during repaint, because the BasicTreeUI is caching + * the number of child nodes before painting begins. * - * Revision 1.61 2003/01/18 23:33:29 mpowers - * Fixing the build. + * Revision 1.61 2003/01/18 23:33:29 mpowers Fixing the build. * - * Revision 1.60 2002/05/31 15:03:10 mpowers - * Fixes for the previous fix. Fat props to yjcheung. + * Revision 1.60 2002/05/31 15:03:10 mpowers Fixes for the previous fix. Fat + * props to yjcheung. * - * Revision 1.59 2002/05/28 15:31:36 mpowers - * Fix for updateDisplayedObjects for a subtle case where a node appears in - * the position that another node was moved from. + * Revision 1.59 2002/05/28 15:31:36 mpowers Fix for updateDisplayedObjects for + * a subtle case where a node appears in the position that another node was + * moved from. * - * Revision 1.58 2002/05/24 14:42:02 mpowers - * Prevent repeat events from firing if firing events loops back. + * Revision 1.58 2002/05/24 14:42:02 mpowers Prevent repeat events from firing + * if firing events loops back. * - * Revision 1.57 2002/04/23 19:12:28 mpowers - * Reimplemented fireEventsForChanges. Fitter and happier. + * Revision 1.57 2002/04/23 19:12:28 mpowers Reimplemented fireEventsForChanges. + * Fitter and happier. * - * Revision 1.56 2002/04/19 21:18:45 mpowers - * Removed tree event coalescing, which was causing way too many problems. - * The fireChangeEvent algorithm is way faster than before, so we should - * still be better off than before. At least now, we don't have to track - * whether the view component has encountered a particular node. + * Revision 1.56 2002/04/19 21:18:45 mpowers Removed tree event coalescing, + * which was causing way too many problems. The fireChangeEvent algorithm is way + * faster than before, so we should still be better off than before. At least + * now, we don't have to track whether the view component has encountered a + * particular node. * - * Revision 1.55 2002/04/19 20:53:22 mpowers - * Now firing event fewer events in fireEventsForChanges. + * Revision 1.55 2002/04/19 20:53:22 mpowers Now firing event fewer events in + * fireEventsForChanges. * - * Revision 1.54 2002/04/15 21:52:50 mpowers - * Tightening up TreeModelAssociation and DisplayGroupNode. - * Now only firing root structure changed once. - * Now disposing of root's children. - * Better event coalescing. + * Revision 1.54 2002/04/15 21:52:50 mpowers Tightening up TreeModelAssociation + * and DisplayGroupNode. Now only firing root structure changed once. Now + * disposing of root's children. Better event coalescing. * - * Revision 1.53 2002/04/12 20:35:20 mpowers - * Now correctly setting parent display group and data source on creation. + * Revision 1.53 2002/04/12 20:35:20 mpowers Now correctly setting parent + * display group and data source on creation. * - * Revision 1.52 2002/04/10 21:20:04 mpowers - * Better handling for tree nodes when working with editing contexts. - * Better handling for invalidation. No longer broadcasting events - * when nodes have not been "registered" in the tree. + * Revision 1.52 2002/04/10 21:20:04 mpowers Better handling for tree nodes when + * working with editing contexts. Better handling for invalidation. No longer + * broadcasting events when nodes have not been "registered" in the tree. * - * Revision 1.51 2002/04/03 20:13:36 mpowers - * Now differentiating between node instantiation caused by model expansion - * (user initiated) and by modifications to the model. - * Dispose now disposes all children. + * Revision 1.51 2002/04/03 20:13:36 mpowers Now differentiating between node + * instantiation caused by model expansion (user initiated) and by modifications + * to the model. Dispose now disposes all children. * - * Revision 1.50 2002/03/23 16:20:27 mpowers - * Optimized processRecentChanges, minimized tree events. + * Revision 1.50 2002/03/23 16:20:27 mpowers Optimized processRecentChanges, + * minimized tree events. * - * Revision 1.49 2002/03/11 03:15:06 mpowers - * Optimized processRecentChanges, minimize event firing, coalescing changes. - * Still need a better diff algorithm to avoid removing nodes. + * Revision 1.49 2002/03/11 03:15:06 mpowers Optimized processRecentChanges, + * minimize event firing, coalescing changes. Still need a better diff algorithm + * to avoid removing nodes. * - * Revision 1.48 2002/03/10 00:59:39 mpowers - * Interim version: coalesces calls to process recent changes. - * Still does not handle rearranged nodes. + * Revision 1.48 2002/03/10 00:59:39 mpowers Interim version: coalesces calls to + * process recent changes. Still does not handle rearranged nodes. * - * Revision 1.47 2002/03/09 17:33:45 mpowers - * Nodes now track their child nodes by reference, not index. + * Revision 1.47 2002/03/09 17:33:45 mpowers Nodes now track their child nodes + * by reference, not index. * - * Revision 1.46 2002/03/08 23:19:07 mpowers - * Added getParentGroup to DisplayGroupNode. + * Revision 1.46 2002/03/08 23:19:07 mpowers Added getParentGroup to + * DisplayGroupNode. * - * Revision 1.45 2002/03/06 13:04:16 mpowers - * Implemented cascading qualifiers in tree nodes. + * Revision 1.45 2002/03/06 13:04:16 mpowers Implemented cascading qualifiers in + * tree nodes. * - * Revision 1.44 2002/02/27 23:19:17 mpowers - * Refactoring of TreeAssociation to create TreeModelAssociation parent. + * Revision 1.44 2002/02/27 23:19:17 mpowers Refactoring of TreeAssociation to + * create TreeModelAssociation parent. * - * Revision 1.42 2002/02/19 22:28:46 mpowers - * DisplayGroupNodes immediately unregister themselves as editors. + * Revision 1.42 2002/02/19 22:28:46 mpowers DisplayGroupNodes immediately + * unregister themselves as editors. * - * Revision 1.41 2002/02/13 16:27:38 mpowers - * Exposing setTarget. + * Revision 1.41 2002/02/13 16:27:38 mpowers Exposing setTarget. * - * Revision 1.40 2001/11/02 20:55:46 mpowers - * Now using fixed index to send node removed events. This preserves the - * expanded state of the nodes in the corresponding jtree. + * Revision 1.40 2001/11/02 20:55:46 mpowers Now using fixed index to send node + * removed events. This preserves the expanded state of the nodes in the + * corresponding jtree. * - * Revision 1.39 2001/09/21 21:09:25 mpowers - * Exposed more fields as protected. + * Revision 1.39 2001/09/21 21:09:25 mpowers Exposed more fields as protected. * - * Revision 1.38 2001/09/19 15:36:08 mpowers - * Refined behavior for isFetched after notification handling. + * Revision 1.38 2001/09/19 15:36:08 mpowers Refined behavior for isFetched + * after notification handling. * - * Revision 1.37 2001/09/13 14:51:18 mpowers - * DisplayGroupNodes now dispose themselves and mark their parent for update - * when they receive notification that their target has been deleted. + * Revision 1.37 2001/09/13 14:51:18 mpowers DisplayGroupNodes now dispose + * themselves and mark their parent for update when they receive notification + * that their target has been deleted. * - * Revision 1.36 2001/09/10 14:10:24 mpowers - * Fix for notification handling. + * Revision 1.36 2001/09/10 14:10:24 mpowers Fix for notification handling. * - * Revision 1.35 2001/07/30 16:17:01 mpowers - * Minor code cleanup. + * Revision 1.35 2001/07/30 16:17:01 mpowers Minor code cleanup. * - * Revision 1.34 2001/07/18 22:13:39 mpowers - * getLoadedDescendants now works as advertised. - * Now correctly handling invalidateAllObjects notification. + * Revision 1.34 2001/07/18 22:13:39 mpowers getLoadedDescendants now works as + * advertised. Now correctly handling invalidateAllObjects notification. * - * Revision 1.33 2001/07/18 13:03:32 mpowers - * TreeNodes now refetch only on demand. Previously, once a node had - * been fetched, it was always refetched after an invalidate, even if - * the node was not being displayed. + * Revision 1.33 2001/07/18 13:03:32 mpowers TreeNodes now refetch only on + * demand. Previously, once a node had been fetched, it was always refetched + * after an invalidate, even if the node was not being displayed. * - * Revision 1.32 2001/06/18 14:10:28 mpowers - * Cleaned up event firing: no longer firing insert or remove events twice. + * Revision 1.32 2001/06/18 14:10:28 mpowers Cleaned up event firing: no longer + * firing insert or remove events twice. * - * Revision 1.31 2001/06/09 16:15:39 mpowers - * Revised the targetChanged scheme because oldObjects and newObjects were - * identical after the target object is invalidated. + * Revision 1.31 2001/06/09 16:15:39 mpowers Revised the targetChanged scheme + * because oldObjects and newObjects were identical after the target object is + * invalidated. * - * Revision 1.30 2001/05/21 22:17:19 mpowers - * Fix for tree out-of-synch problems when nodes are inserted. + * Revision 1.30 2001/05/21 22:17:19 mpowers Fix for tree out-of-synch problems + * when nodes are inserted. * - * Revision 1.29 2001/05/18 21:07:46 mpowers - * Playing with refresh options. + * Revision 1.29 2001/05/18 21:07:46 mpowers Playing with refresh options. * - * Revision 1.28 2001/05/14 15:25:43 mpowers - * DisplayGroupNodes now only respond to InvalidateAllObjectsInStore - * if they are already fetched. + * Revision 1.28 2001/05/14 15:25:43 mpowers DisplayGroupNodes now only respond + * to InvalidateAllObjectsInStore if they are already fetched. * - * Revision 1.27 2001/05/08 19:55:58 mpowers - * Fix for node children not refreshing after sibling was inserted. + * Revision 1.27 2001/05/08 19:55:58 mpowers Fix for node children not + * refreshing after sibling was inserted. * - * Revision 1.26 2001/05/08 18:47:34 mpowers - * Minor fixes for d3. + * Revision 1.26 2001/05/08 18:47:34 mpowers Minor fixes for d3. * - * Revision 1.25 2001/05/06 22:22:55 mpowers - * Debugging. + * Revision 1.25 2001/05/06 22:22:55 mpowers Debugging. * - * Revision 1.24 2001/05/04 14:42:58 mpowers - * Now getting stored values in KeyValueCoding. - * MasterDetail now marks dirty based on whether it's an attribute - * or relation. - * Implemented editing context marker. + * Revision 1.24 2001/05/04 14:42:58 mpowers Now getting stored values in + * KeyValueCoding. MasterDetail now marks dirty based on whether it's an + * attribute or relation. Implemented editing context marker. * - * Revision 1.23 2001/05/02 18:00:43 mpowers - * Removed debug code. + * Revision 1.23 2001/05/02 18:00:43 mpowers Removed debug code. * - * Revision 1.22 2001/05/02 17:31:20 mpowers - * DisplayGroupNode now does a better job determining when to mark its - * parent dirty. + * Revision 1.22 2001/05/02 17:31:20 mpowers DisplayGroupNode now does a better + * job determining when to mark its parent dirty. * - * Revision 1.21 2001/05/01 00:52:32 mpowers - * Implemented breadth-first traversal of tree for node. + * Revision 1.21 2001/05/01 00:52:32 mpowers Implemented breadth-first traversal + * of tree for node. * - * Revision 1.20 2001/04/26 01:15:19 mpowers - * Major clean-up of DisplayGroupNode: fitter, happier, more productive. + * Revision 1.20 2001/04/26 01:15:19 mpowers Major clean-up of DisplayGroupNode: + * fitter, happier, more productive. * - * Revision 1.19 2001/04/22 23:13:35 mpowers - * Minor bug. + * Revision 1.19 2001/04/22 23:13:35 mpowers Minor bug. * - * Revision 1.18 2001/04/22 23:05:33 mpowers - * Totally revised DisplayGroupNode so each object gets its own node - * (so the nodes are no longer fixed by index). + * Revision 1.18 2001/04/22 23:05:33 mpowers Totally revised DisplayGroupNode so + * each object gets its own node (so the nodes are no longer fixed by index). * - * Revision 1.17 2001/04/21 23:05:12 mpowers - * A fairly major revisiting. I've decided to scrap the pass-thru approach - * where every node simply represents an index and not an object. - * The next update will have each node correspond to a specific object. + * Revision 1.17 2001/04/21 23:05:12 mpowers A fairly major revisiting. I've + * decided to scrap the pass-thru approach where every node simply represents an + * index and not an object. The next update will have each node correspond to a + * specific object. * - * Revision 1.16 2001/04/13 16:37:37 mpowers - * Handling bounds checking. + * Revision 1.16 2001/04/13 16:37:37 mpowers Handling bounds checking. * - * Revision 1.15 2001/04/03 20:36:01 mpowers - * Fixed refaulting/reverting/invalidating to be self-consistent. + * Revision 1.15 2001/04/03 20:36:01 mpowers Fixed + * refaulting/reverting/invalidating to be self-consistent. * - * Revision 1.14 2001/03/27 17:45:51 mpowers - * More index bounds checking. + * Revision 1.14 2001/03/27 17:45:51 mpowers More index bounds checking. * - * Revision 1.13 2001/03/22 21:25:42 mpowers - * Fixed some nasty issues with jtree's internal state and array bounds. + * Revision 1.13 2001/03/22 21:25:42 mpowers Fixed some nasty issues with + * jtree's internal state and array bounds. * - * Revision 1.12 2001/03/19 22:18:58 mpowers - * Root node now mirrors contents of titles display group. + * Revision 1.12 2001/03/19 22:18:58 mpowers Root node now mirrors contents of + * titles display group. * - * Revision 1.11 2001/03/19 21:38:36 mpowers - * Improved redisplay after edit. Editing nodes off root now works. + * Revision 1.11 2001/03/19 21:38:36 mpowers Improved redisplay after edit. + * Editing nodes off root now works. * - * Revision 1.10 2001/03/09 22:08:38 mpowers - * Removed unused line. + * Revision 1.10 2001/03/09 22:08:38 mpowers Removed unused line. * - * Revision 1.9 2001/03/07 16:41:04 mpowers - * Now checking size of parent displayed objects array so that we don't - * get array out of bounds execeptions from isLeaf() or object() when - * those messages are called after the TreeAssociation fires a - * nodesDeleted event. I believe that JTree is mistakenly rendering - * those nodes one last time before erasing them. + * Revision 1.9 2001/03/07 16:41:04 mpowers Now checking size of parent + * displayed objects array so that we don't get array out of bounds execeptions + * from isLeaf() or object() when those messages are called after the + * TreeAssociation fires a nodesDeleted event. I believe that JTree is + * mistakenly rendering those nodes one last time before erasing them. * - * Revision 1.8 2001/03/06 23:21:27 mpowers - * Now only notifying parent if the object is not registered in the - * editing context, if any. + * Revision 1.8 2001/03/06 23:21:27 mpowers Now only notifying parent if the + * object is not registered in the editing context, if any. * - * Revision 1.7 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.7 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.6 2001/02/17 16:52:05 mpowers - * Changes in imports to support building with jdk1.1 collections. + * Revision 1.6 2001/02/17 16:52:05 mpowers Changes in imports to support + * building with jdk1.1 collections. * - * Revision 1.5 2001/01/31 17:59:52 mpowers - * Fixed isLeaf aspect of TreeAssociation. + * Revision 1.5 2001/01/31 17:59:52 mpowers Fixed isLeaf aspect of + * TreeAssociation. * - * Revision 1.4 2001/01/25 02:16:25 mpowers - * TreeAssociation now returns DisplayGroupNode.getUserObject. + * Revision 1.4 2001/01/25 02:16:25 mpowers TreeAssociation now returns + * DisplayGroupNode.getUserObject. * - * Revision 1.3 2001/01/24 18:14:40 mpowers - * Fixed problem with leaving children aspect unspecified. + * Revision 1.3 2001/01/24 18:14:40 mpowers Fixed problem with leaving children + * aspect unspecified. * - * Revision 1.2 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.2 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.1 2001/01/24 14:17:12 mpowers - * Major revision to TreeAssociation. Can now add and remove nodes. - * DisplayGroupNode is now it's own class. + * Revision 1.1 2001/01/24 14:17:12 mpowers Major revision to TreeAssociation. + * Can now add and remove nodes. DisplayGroupNode is now it's own class. * * */ - -- cgit v1.2.3