An EOAssociation monitors its display object for user input or other events while also observing changes in the selection or contents of its EODisplayGroups. The basic purpose of an EOAssociation is to assure that changes at one end are reflected on the other. When the selection in a display group changes, for example, the association updates the state of its display object to reflect this new selection. The following sections describe this process in detail.
In the com.apple.yellow.eointerface package, an EOAssociation is tied to a single display object. Each EOAssociation assumes the roles defined for one or more outlets of this object. An EOControlAssociation, for example, appropriates the target and action outlets of the NSControl it is bound to. When the user activates the control or changes its value, the action is fired and the EOAssociation correspondingly updates a property of the display group's selected enterprise object. An EOControlAssociation also sets itself as the control's delegate in order to receive various editing and validation messages.
In the com.apple.yellow.eointerface package, any outlets an association claims cannot be used for other purposes. The class method objectKeysTaken returns the names of any outlets a given EOAssociation subclass appropriates, and InterfaceBuilder disables them in its Connections Inspector if the inspected object has been associated. A button acting as an EOControlAssociation's display object, for example, has its target outlet dimmed.
Although display objects are typically user-interface controls such as text fields and pop-up menus, they can be any kind of object. A notable example of this is an EOMasterDetailAssociation, where the display object is a "detail" EODisplayGroup populated with the destination enterprise objects of a relationship in the "master" display group. See the EOMasterDetailAssociation class specification for more information on master-detail configurations.
Although an EOAssociation has only one display object it may have any number of aspects. Aspects define the EODisplayGroup characteristics that the association observes. Aspects are bound to a display group by a key of the enterprise objects contained by the association. Depending upon a given EOAssociation subclass, aspects may be optional or mandatory. They might all have to be bound to a single EODisplayGroup or they may span several. Some aspects can be mutually exclusive.
On the display side, aspects are typically bound to visible facets of the EOAssociation's display object, such as the value or values it displays and any interactive state. Each aspect's value is determined by the contents of the enterprise-object property in the EODisplayGroup that the aspect is bound to. This value may be taken from all enterprise objects in the EODisplayGroup or only those in the current selection. Some aspects are "read-only" in that they merely reflect the contents of the display group, but others change enterprise-object values when the display object is manipulated.
An EOControlAssociation, for example, defines "value" and "enabled" aspects. To configure a text field to display the salary for the selected enterprise object you must create an EOControlAssociation with the text field as its display object and bind the EOControlAssociation's "value" aspect to the appropriate display group's "salary" key. You might also bind the EOControlAssociation's "enabled" aspect to some key such as "eligibleForRaise" so that the text field is made editable if this property evaluates to non-zero. When focus leaves the text field, the newly entered value is sent to the EODisplayGroup.
A multi-valued aspect can represent the destination of a to-many relationship or it can define a range of possible values for an enterprise object's property. EOComboBoxAssociation, for example, has a "titles" aspect that defines all possible values for a key, and all these values then appear in the pop-up menu. If, for example, you bind the "titles" aspect to the "name" key of an EODisplayGroup containing Departments, you get a pop-up menu containing the names of all departments. EOComboBoxAssociation also has a "selectedObject" aspect which, when bound to a relationship property of an enterprise object, determines the selection in the "titles" display group.
As EODelayedObservers, EOAssociations add themselves to the list of objects observing the display groups they are bound to. When a display group changes its selection or contents, observing EOAssociations are sent a subjectChanged message. This message does not indicate which EODisplayGroup has changed, so the receiver must query each one. When an EOAssociation wishes to modify the contents of a EODisplayGroup, it typically does so through the setValueForAspect. This process and the querying of display groups are described under "Monitoring Changes from the Display Object".
Although you normally use the Interface Builder application (and the EOPalette palette) to set up EOAssociations, you can do so programmatically as well. Because EOAssociation coordinates the actions of many objects, linking a display object to a display group is a multi-step process, as shown by the following code fragment; this fragment assumes that salaryText and employeeGroup already exist.
JTextComponent salaryText; EODisplayGroup employeeGroup; EOTextAssociation association; association = new EOTextAssociation(salaryText); association.bindAspect(EOTextAssociation.ValueAspect, employeeGroup, "salary"); association.bindAspect( EOTextAssociation.EnabledAspect, employeeGroup, "eligibleForRaise"); association.establishConnection();
Although an association is initialized with the display object
it monitors, this really represents only half of the required initialization;
the association and therefore the display object have yet to be
bound to any display group. The two invocations of bindAspect define
the specifics of the field's interaction with employeeGroup. Once
these aspects have been bound, establishConnection causes
the association to register as an observer of employeeGroup and
complete its internal initialization. Note that when using the com.apple.yellow.eointerface APIs
you can safely release a newly instantiated association once you invoke establishConnection
because
this method retains the association for the lifespan of the display object.
If none of the standard EOAssociation subclasses meets your needs, you can create a new one without much effort. To do so, you need to define four areas of functionality:
The following four sections describe how to do each of these.
If you're creating a com.apple.yellow.eointerface.EOAssociation subclass, a significant part of creating an EOAssociation subclass is defining and advertising what the subclass works with. The characteristics that your subclass should define are:
aspects
class
method that returns an NSArray of aspect names, as Strings. Some
standard aspects are:value, the value of an
attribute or relationship; enabled, whether
the control should be enabled; titles, all
existing values for an attribute; and selectedTitle,
the value of the selected attribute (bound to the same key as "titles").priority
method
appropriately. EOMasterDetailAssociation, for example, uses EODelayedObserverPrioritySecond
to catch updates before other EOAssociations based on it.EOAssociation's constructor is
public EOAssociation(Object object)
but you rarely need to write custom initialization code in this method. Instead, you override establishConnection, which is where the real initialization takes place, as described above in "Setting up an EOAssociation Programmatically".
Your subclass's implementation of establishConnection should first invoke the superclass implementation to initialize the observation of bound EODisplayGroups and then establish their notification relationship with the display object. Once the association has been bound to its display groups and appropriately attached to its display object it is ready to perform real work.
An EOAssociation is notified of changes in EODisplayGroup
selections and changes through EODelayedObserver's subjectChanged
method.
An EOAssociation sublcass, in its implementation of this method,
propagates these changes to the display object. Because subjectChanged
provides no additional information about the change that triggered
its invocation, associations must query their bound display groups
for details. The EOAssociation method displayGroupForAspect,
in conjunction with EODisplayGroup's contentsChanged and selectionChanged, faciliate efficient
aspect-by-aspect change analysis. Once you have determined the set
of affected aspects, your subclass must update its display object
to reflect their new values. How this is done is specific to the
class of display object and to the aspects your EOAssocation subclass
supports.
When an EOAssociation is notified of a change to the state of its display object, it must update the affected display groups so that they reflect the new state. Updating can involve changing a display-group value, sending messages to the display group, or sending messages to some set of the enterprise objects the display group contains. As a simple example, an association with a "value" aspect would update the value of the bound display group's selected enterprise object by invoking setValueForAspect with the display object's new contents. Complex associations might set enterprise object values more directly via EODisplayGroup's setSelectedObjectValue , setValueForObject, or setValueForObjectAtIndex in conjunction with EOAssocation"s displayGroupKeyForAspect. An association with a button as its display object might go even further, sending the message defined by its "action" aspect to the enterprise objects selected in a display group whenever the button is clicked.
For display objects that support editing, such as text fields,
an association must observe events signifying the beginning or end
of an editing operation and then inform the appropriate display
groups using EODisplayGroup's associationDidBeginEditing and associationDidEndEditing.
This operation is important because a display group requests an
end to editing when it is asked to perform tasks such as the insertion
of a new enterprise object or a save. It requests and end to editing
by sending an endEditing message to the association
it believes currently has an edit in progress. Implementations of endEditing
should
attempt to propagate the current state of the display object to
the receiver's display groups and return false if this attempt
fails, indicating that the request has been disallowed. EOAssociations
that support the display of multiple values and the notion of a
selection must also propagate changes in this selection to the appropriate
display groups using EODisplayGroup's setSelectionIndexes.
Although validation of values entered by the user can happen
in several places, EOAssociations generally concern themselves only
with data entry errors. These errors are typically caught by the display
object or an NSFormatter, and result in a message to the delegate
of the display object. For example, an NSControl sends controlIsValidObject
and controlDidFailToFormatStringErrorDescription
to
its delegate, allowing the delegate to validate values itself or
to handle errors caught by an NSFormatter. Your implementation of
a method such as controlIsValidObject
should
simply try to save the new value, using EOAssociation's setValueForAspect or setValueForAspectAtIndex,
returning true or false as that message does. For controlDidFailToFormatStringErrorDescription
,
the typical response should be to invoke shouldEndEditing or shouldEndEditingAtIndex.