From aedc34d55462a75e329bbf342251ff6504cd117e Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Sun, 19 May 2024 17:56:33 -0400 Subject: Initial import from SVN --- .../main/java/net/wotonomy/ui/EOAssociation.java | 565 +++++++++++++++++++++ 1 file changed, 565 insertions(+) create mode 100644 projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EOAssociation.java (limited to 'projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EOAssociation.java') diff --git a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EOAssociation.java b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EOAssociation.java new file mode 100644 index 0000000..635c5b9 --- /dev/null +++ b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EOAssociation.java @@ -0,0 +1,565 @@ +/* +Wotonomy: OpenStep design patterns for pure Java applications. +Copyright (C) 2000 Michael Powers + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, see http://www.gnu.org +*/ + +package net.wotonomy.ui; + +import java.util.Enumeration; + +import net.wotonomy.control.EODelayedObserver; +import net.wotonomy.control.EOObserverCenter; +import net.wotonomy.foundation.NSArray; +import net.wotonomy.foundation.NSMutableArray; +import net.wotonomy.foundation.NSMutableDictionary; + +/** +* Associations observe DisplayGroups and associate +* a user interface component with one or more keys +* on the objects in the display group.

+* +* Associations are created with a ui component in +* the constructor. Then, one or more aspects are +* bound to display groups and/or property keys with +* the bindAspect() method. Finally, the association +* is initialized with the establishConnection() +* method.

+* +* Per the openstep convention, you do not need to +* retain a reference to the association after it is +* created; the association will be garbage-collected +* when the ui component is garbage-collected.

+* +* (Because java components don't have delegates like +* openstep components do, java-based associations +* will likely need to implement some sort of listener +* and add itself to the component's list of listeners +* so that the component will have a strong reference +* (and the only reference) to the association.)

+* +* @author michael@mpowers.net +* @author $Author: cgruber $ +* @version $Revision: 904 $ +*/ +public class EOAssociation extends EODelayedObserver +{ + // aspect constants + public static final String ActionAspect = "action"; + public static final String EnabledAspect = "enabled"; + public static final String SourceAspect = "source"; + public static final String ArgumentAspect = "argument"; + public static final String ParentAspect = "parent"; + public static final String TitlesAspect = "titles"; + public static final String BoldAspect = "bold"; + public static final String SelectedObjectAspect = "selectedObject"; + public static final String ValueAspect = "value"; + public static final String DestinationAspect = "destination"; + public static final String SelectedTitleAspect = "selectedTitle"; + public static final String URLAspect = "URL"; + public static final String ItalicAspect = "italic"; + public static final String ChildrenAspect = "children"; // not in spec + public static final String IsLeafAspect = "isLeaf"; // not in spec + public static final String EditableAspect = "editable"; // not in spec + public static final String VisibleAspect = "visible"; // not in spec + public static final String ObjectsAspect = "objects"; // not in spec + public static final String LabelAspect = "label"; // not in spec + public static final String IconAspect = "icon"; // not in spec + + public static final String AttributeAspectSignature = "A"; + public static final String NullAspectSignature = ""; + public static final String AttributeToOneAspectSignature = "A1"; + public static final String ToOneAspectSignature = "1"; + public static final String AttributeToOneToManyAspectSignature = "A1M"; + public static final String ToOneToManyAspectSignature = "1M"; + public static final String AttributeToManyAspectSignature = "AM"; + public static final String ToManyAspectSignature = "M"; + + protected Object control; + protected NSMutableDictionary aspectToGroup; + protected NSMutableDictionary aspectToKey; + + /** + * Default constructor. + */ + public EOAssociation () + { + aspectToGroup = new NSMutableDictionary(); + aspectToKey = new NSMutableDictionary(); + control = null; + } + + /** + * Constructor specifying the object to be controlled by this + * association. Does not establish connection. + */ + public EOAssociation ( Object anObject ) + { + this(); + control = anObject; + } + + /** + * Returns a List of aspect signatures whose contents + * correspond with the aspects list. Each element is + * a string whose characters represent a capability of + * the corresponding aspect. + * An empty signature "" means that the aspect can + * bind without needing a key. + * This implementation returns "A1M" for each + * element in the aspects array. + */ + public static NSArray aspectSignatures () + { + int size = aspects().count(); + NSMutableArray result = new NSMutableArray(); + for ( int i = 0; i < size; i++ ) + { + result.addObject( AttributeToOneToManyAspectSignature ); + } + return result; + } + + /** + * Returns a List that describes the aspects supported + * by this class. Each element in the list is the string + * name of the aspect. This implementation returns an + * empty list. + * + * TODO: Is this static in the WebObjects published API? If not, fix. + */ + public static NSArray aspects () + { + return new NSArray(); + } + + /** + * Returns all registered subclasses of EOAssociation + * for which usableWithObject with the specified object + * returns true. You should only call this method on + * the EOAssociation class. + */ + public static NSArray associationClassesForObject ( + Object anObject ) + { + throw new RuntimeException( "Not implemented yet." ); + } + + /** + * Returns a List of EOAssociation subclasses that, + * for the objects that are usable for this association, + * are less suitable than this association. + * This implementation returns an empty list. + */ + public static NSArray associationClassesSuperseded () + { + return new NSArray(); + } + + + /** + * Binds the specified aspect of this association to the + * specified key on the specified display group. + */ + public void bindAspect ( + String anAspect, EODisplayGroup aDisplayGroup, String aKey ) + { + // unattach old group, if any + EODisplayGroup oldGroup = displayGroupForAspect( anAspect ); + if ( oldGroup != null ) + { + EOObserverCenter.removeObserver( this, oldGroup ); + } + + // attach new group + if ( aDisplayGroup != null ) + { + aspectToGroup.setObjectForKey( aDisplayGroup, anAspect ); + } + else + { + aspectToGroup.removeObjectForKey( anAspect ); + } + // attach new key + if ( aKey != null ) + { + aspectToKey.setObjectForKey( aKey, anAspect ); + } + else + { + aspectToKey.removeObjectForKey( anAspect ); + } + } + + /** + * Breaks the connection between this association and + * its object. Override to stop listening for events + * from the object, but remember to call this method. + * This implementation unregisters this association + * from observing each bound display group. + */ + public void breakConnection () + { + discardPendingNotification(); + Enumeration e = aspectToGroup.objectEnumerator(); + while ( e.hasMoreElements() ) + { + EOObserverCenter.removeObserver( this, e.nextElement() ); + } + } + + /** + * Returns whether this association can bind to the + * specified display group on the specified key for + * the specified aspect. + * This implementation returns false. + */ + public boolean canBindAspect ( + String anAspect, EODisplayGroup aDisplayGroup, String aKey) + { + return false; + } + + /** + * Copies the binding for each aspect in this association + * that has a matching aspect in the specified association. + */ + public void copyMatchingBindingsFromAssociation ( + EOAssociation anAssociation ) + { + //FIXME: this is broken: aspects() returns EOAssociation.aspects() + //NOTE: This is actually quite crazy - should this even be static? -ceg + NSMutableArray ourAspects = new NSMutableArray( this.aspects() ); + NSArray theirAspects = anAssociation.aspects(); + + String aspect; + Enumeration e = ourAspects.objectEnumerator(); + while ( e.hasMoreElements() ) + { + aspect = e.nextElement().toString(); + if ( theirAspects.containsObject( aspect ) ) + { + this.bindAspect( + aspect, + anAssociation.displayGroupForAspect( aspect ), + anAssociation.displayGroupKeyForAspect( aspect ) ); + } + } + } + + /** + * Returns the display group that is bound the specified + * aspect, or null if no display group is currently + * bound to that aspect. + */ + public EODisplayGroup displayGroupForAspect ( String anAspect ) + { + return (EODisplayGroup) aspectToGroup.objectForKey( anAspect ); + } + + /** + * Returns the key for the display group bound to the + * specified aspect, or null if no display group is currently + * bound to that aspect. + */ + public String displayGroupKeyForAspect ( String anAspect ) + { + return (String) aspectToKey.objectForKey( anAspect ); + } + + /** + * The human-readable descriptive name for this association. + * This implementation returns the class name. + */ + public String displayName () // was static - can static method get class name? + { + String className = getClass().getName(); + int index = className.lastIndexOf( "." ); + if ( index == -1 ) return className; + return className.substring( index+1 ); + } + + /** + * Forces this association to cause the object to + * stop editing and validate the user's input. + * This implementation returns true. + * @return false if there were problems validating, + * or true to continue. + */ + public boolean endEditing () + { + return true; + } + + /** + * Establishes a connection between this association + * and the controlled object. Subclasses should populate + * their controlled object from the display group and begin + * listening for events from their controlled object here, + * but remember to call this method. Any existing value + * in the controlled object will be overwritten. + * This implementation registers this assocation to + * observer changes to each of the bound display groups. + */ + public void establishConnection () + { + Enumeration e = aspectToGroup.objectEnumerator(); + while ( e.hasMoreElements() ) + { + EOObserverCenter.addObserver( this, e.nextElement() ); + } + } + + /** + * Returns whether this class can control the specified + * object. This implementation returns false. + */ + public static boolean isUsableWithObject ( Object anObject ) + { + return false; + } + + /** + * Returns the object that is currently controlled by this + * association, or null if no object is currently controlled. + */ + public Object object () + { + return control; + } + + /** + * Returns a List of properties of the controlled object + * that are controlled by this class. For example, + * "stringValue", or "selected". + * This implementation returns an empty list. + */ + public static NSArray objectKeysTaken () + { + return new NSArray(); + } + + /** + * Returns the aspect that is considered primary + * or default. This is typically "value" or somesuch. + * This implementation returns null. + */ + public static String primaryAspect () + { + return null; + } + + /** + * Writes the specified value for the display group + * and key currently bound to the specified aspect. + * This implementation calls + * EODisplayGroup.setSelectedObjectValue(). + * @return True if the value was successfully set, + * otherwise false. + */ + public boolean setValueForAspect ( + Object aValue, + String anAspect ) + { + EODisplayGroup group = (EODisplayGroup) + aspectToGroup.objectForKey( anAspect ); + if ( group == null ) return false; + String key = (String) + aspectToKey.objectForKey( anAspect ); + if ( key == null ) return false; + + //FIXME: is this the right method to call + return group.setSelectedObjectValue( aValue, key ); + } + + + /** + * Writes the specified value for the display group + * and key currently bound to the specified aspect, + * at the specified index (assuming it's an indexed + * property). + * This implementation calls + * EODisplayGroup.setValueForObjectAtIndex(). + * @return True if the value was successfully set, + * otherwise false. + */ + public boolean setValueForAspectAtIndex ( + Object aValue, + String anAspect, + int anIndex ) + { + EODisplayGroup group = (EODisplayGroup) + aspectToGroup.objectForKey( anAspect ); + if ( group == null ) return false; + String key = (String) + aspectToKey.objectForKey( anAspect ); + if ( key == null ) return false; + + //FIXME: is this the right method to call? + return group.setValueForObjectAtIndex( aValue, anIndex, key ); + } + + /** + * Called by subclasses to notify that the user's input + * failed validation. Notifies the display group for + * the aspect, returning the result. + * This implementation calls + * EODisplayGroup.associationFailedToValidateValue() + * with the display group's selected object. + * @return True if the user should be allowed to continue, + * meaning that the display group with handle user notification, + * otherwise false meaning the caller should notify the user. + */ + public boolean shouldEndEditing ( + String anAspect, + String anInvalidInput, + String anErrorDescription ) + { + EODisplayGroup group = (EODisplayGroup) + aspectToGroup.objectForKey( anAspect ); + if ( group == null ) return false; + String key = (String) + aspectToKey.objectForKey( anAspect ); + if ( key == null ) return false; + return group.associationFailedToValidateValue( + this, anInvalidInput, key, + group.selectedObject(), //FIXME: is this correct? + anErrorDescription ); + } + + /** + * Called by subclasses to notify that the user's input + * failed validation. Notifies the display group for + * the aspect, returning the result. + * This implementation calls + * EODisplayGroup.associationFailedToValidateValue() + * with the object in the display group's displayed + * objects array at the specified index. + * @return True if the user should be allowed to continue, + * meaning that the display group with handle user notification, + * otherwise false meaning the caller should notify the user. + */ + public boolean shouldEndEditingAtIndex ( + String anAspect, + String anInvalidInput, + String anErrorDescription, + int anIndex ) + { + EODisplayGroup group = (EODisplayGroup) + aspectToGroup.objectForKey( anAspect ); + if ( group == null ) return false; + String key = (String) + aspectToKey.objectForKey( anAspect ); + if ( key == null ) return false; + return group.associationFailedToValidateValue( + this, anInvalidInput, key, + group.displayedObjects().objectAtIndex( anIndex ), + //FIXME: is this correct? + anErrorDescription ); + } + + /** + * Called when either the selection or the contents of + * an associated display group have changed. + * This implementation does nothing. + */ + public void subjectChanged () + { + // does nothing + } + + /** + * Returns the current value for the display group + * and key associated with the specified aspect. + */ + public Object valueForAspect ( String anAspect ) + { + EODisplayGroup group = (EODisplayGroup) + aspectToGroup.objectForKey( anAspect ); + if ( group == null ) return null; + String key = (String) + aspectToKey.objectForKey( anAspect ); + if ( key == null ) return null; + + //FIXME: is this correct? use selected object? + return group.valueForObject( group.selectedObject(), key ); + } + + /** + * Returns the current value for the display group + * and key associated with the specified aspect, + * and the specified index (assuming multiple values). + */ + public Object valueForAspectAtIndex ( + String anAspect, int anIndex ) + { + EODisplayGroup group = (EODisplayGroup) + aspectToGroup.objectForKey( anAspect ); + if ( group == null ) return null; + String key = (String) + aspectToKey.objectForKey( anAspect ); + if ( key == null ) return null; + + //FIXME: is this the right method to call? + return group.valueForObjectAtIndex( anIndex, key ); + } + +} + +/* + * $Log$ + * Revision 1.2 2006/02/18 23:14:35 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.7 2005/05/11 15:21:53 cgruber + * Change enum to enumeration, since enum is now a keyword as of Java 5.0 + * + * A few other comments in the code. + * + * Revision 1.6 2003/08/06 23:07:52 chochos + * general code cleanup (mostly, removing unused imports) + * + * Revision 1.5 2003/06/06 20:29:45 mpowers + * Now dequeuing the association when connection is broken. + * Coalesced change events had been processed even after breaking connection. + * + * Revision 1.4 2001/03/06 23:43:46 mpowers + * Implemented icon aspect for text association. + * + * Revision 1.3 2001/02/17 16:52:05 mpowers + * Changes in imports to support building with jdk1.1 collections. + * + * Revision 1.2 2001/01/25 17:46:11 mpowers + * Clarified usage and gc expectations. + * + * Revision 1.1.1.1 2000/12/21 15:48:07 mpowers + * Contributing wotonomy. + * + * Revision 1.11 2000/12/20 16:25:39 michael + * Added log to all files. + * + * + */ + -- cgit v1.2.3