summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EOAssociation.java
diff options
context:
space:
mode:
authorBenjamin Culkin <scorpress@gmail.com>2024-05-19 17:56:33 -0400
committerBenjamin Culkin <scorpress@gmail.com>2024-05-19 17:56:33 -0400
commitaedc34d55462a75e329bbf342251ff6504cd117e (patch)
treebcc8f1f2352582717b484df302aeea6696b8f000 /projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EOAssociation.java
Initial import from SVN
Diffstat (limited to 'projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EOAssociation.java')
-rw-r--r--projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EOAssociation.java565
1 files changed, 565 insertions, 0 deletions
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. <br><br>
+*
+* 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. <br><br>
+*
+* 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. <br><br>
+*
+* (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.) <br><br>
+*
+* @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. <ul>
+ * <li>"A" attribute: the aspect can be bound to
+ * an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a
+ * property that returns a single object.</li>
+ * <li>"M" to-one: the aspect can be bound to a
+ * property that returns multiple objects.</li>
+ * </ul>
+ * 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.
+ *
+ *
+ */
+