-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ERMDAjaxNotificationCenter component that allows updating of depe…
…ndent property keys.
- Loading branch information
Showing
4 changed files
with
248 additions
and
0 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
...mponents/Nonlocalized.lproj/ERMDAjaxNotificationCenter.wo/ERMDAjaxNotificationCenter.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<!-- | ||
The spaces between the elements are intentional. If there are no spaces, and no triggers, | ||
the browser will receive no response and think there is an error. | ||
--> | ||
<webobject name = "AjaxUpdateContainer"> <webobject name = "AjaxUpdateTrigger"/> </webobject> |
8 changes: 8 additions & 0 deletions
8
...omponents/Nonlocalized.lproj/ERMDAjaxNotificationCenter.wo/ERMDAjaxNotificationCenter.wod
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
AjaxUpdateContainer: AjaxUpdateContainer { | ||
id = id; | ||
} | ||
|
||
AjaxUpdateTrigger: AjaxUpdateTrigger { | ||
updateContainerIDs = updateContainerIDs; | ||
resetAfterUpdate = true; | ||
} |
4 changes: 4 additions & 0 deletions
4
...omponents/Nonlocalized.lproj/ERMDAjaxNotificationCenter.wo/ERMDAjaxNotificationCenter.woo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"WebObjects Release" = "WebObjects 5.0"; | ||
encoding = "UTF-8"; | ||
} |
231 changes: 231 additions & 0 deletions
231
...odernDirectToWeb/Sources/er/modern/directtoweb/components/ERMDAjaxNotificationCenter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
package er.modern.directtoweb.components; | ||
|
||
import org.apache.log4j.Logger; | ||
|
||
import com.webobjects.appserver.WOContext; | ||
import com.webobjects.directtoweb.D2WContext; | ||
import com.webobjects.foundation.NSArray; | ||
import com.webobjects.foundation.NSDictionary; | ||
import com.webobjects.foundation.NSMutableArray; | ||
import com.webobjects.foundation.NSMutableDictionary; | ||
import com.webobjects.foundation.NSNotification; | ||
import com.webobjects.foundation.NSNotificationCenter; | ||
import com.webobjects.foundation.NSSelector; | ||
|
||
import er.ajax.AjaxUpdateContainer; | ||
import er.directtoweb.components.ERDCustomComponent; | ||
import er.extensions.appserver.ERXWOContext; | ||
import er.extensions.eof.ERXConstant; | ||
import er.extensions.eof.ERXKey; | ||
import er.extensions.foundation.ERXArrayUtilities; | ||
import er.extensions.foundation.ERXStringUtilities; | ||
import er.modern.directtoweb.components.buttons.ERMDDeleteButton; | ||
|
||
/** | ||
* ERMDAjaxNotificationCenter makes it easy to observe properties for changes | ||
* and update dependent property keys. You just specify a dependency structure | ||
* via the propertyDependencies D2W key. It takes a dictionary with property | ||
* keys to be observed as keys and an array of the dependents to be updated as | ||
* the value. Example: | ||
* | ||
* <pre> | ||
* 100 : ((task = 'create' or task = 'edit') and entity.name = 'Person') => propertyDependencies = {"isFemale" = ("salutation"); "dateOfBirth" = ("discount", "parentEmail"); } [com.webobjects.directtoweb.Assignment] | ||
* </pre> | ||
* | ||
* This will observe the property keys "isFemale" and "dateOfBirth". If | ||
* "isFemale" changes, the "salutation" property will be updated. If | ||
* "dateOfBirth" changes, the "discount" and "parentEmail" properties will be | ||
* updated. You can then hide and show properties on the fly by using the | ||
* displayVariant key: | ||
* | ||
* <pre> | ||
* 100 : ((task = 'create' or task = 'edit') and entity.name = 'Person' and propertyKey = 'parentEmail' and object.isAdult = '1') => displayVariant = "omit" [com.webobjects.directtoweb.Assignment] | ||
* </pre> | ||
* | ||
* | ||
* By default, ERMDAjaxNotificationCenter will be included in the | ||
* aboveDisplayPropertyKeys repetition when propertyDependencies is not null. If | ||
* you set aboveDisplayPropertyKeys yourself, you have to include the | ||
* "ajaxNotificationCenter" property key. | ||
* | ||
* Unlike the original version by Ramsey, this implementation depends on | ||
* ERMDInspectPageRepetition to insert AjaxObserveField and AjaxUpdateContainer | ||
* components where required. | ||
* | ||
* @d2wKey ajaxNotificationCenter | ||
* @d2wKey propertyDependencies | ||
* | ||
* @author rgurley@mac.com | ||
* @author fpeters | ||
* | ||
*/ | ||
public class ERMDAjaxNotificationCenter extends ERDCustomComponent { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
public static final ERXKey<String> AJAX_NOTIFICATION_CENTER_ID = new ERXKey<String>( | ||
"ajaxNotificationCenterID"); | ||
|
||
public static final ERXKey<String> PROPERTY_OBSERVER_ID = new ERXKey<String>( | ||
"propertyObserverID"); | ||
|
||
public static final ERXKey<String> PROPERTY_KEY = new ERXKey<String>("propertyKey"); | ||
|
||
public static final ERXKey<NSDictionary<String, NSArray<String>>> PROPERTY_DEPENDENCIES = new ERXKey<NSDictionary<String, NSArray<String>>>( | ||
"propertyDependencies"); | ||
|
||
public static final String PropertyChangedNotification = "PropertyChangedNotification"; | ||
|
||
public static final String RegisterPropertyObserverIDNotification = "RegisterPropertyObserverIDNotification"; | ||
|
||
@SuppressWarnings("rawtypes") | ||
private NSSelector propertyChanged = new NSSelector("propertyChanged", | ||
ERXConstant.NotificationClassArray); | ||
|
||
private String id; | ||
|
||
private NSMutableArray<String> updateContainerIDs = new NSMutableArray<String>(); | ||
|
||
private static final Logger log = Logger.getLogger(ERMDAjaxNotificationCenter.class); | ||
|
||
public String id() { | ||
if (id == null) { | ||
id = ERXWOContext.safeIdentifierName(context(), true); | ||
AJAX_NOTIFICATION_CENTER_ID.takeValueInObject(id, d2wContext()); | ||
} | ||
return id; | ||
} | ||
|
||
public ERMDAjaxNotificationCenter(WOContext context) { | ||
super(context); | ||
} | ||
|
||
public void setD2wContext(D2WContext context) { | ||
if (context != null && !context.equals(d2wContext())) { | ||
log.debug("Removing observers for old context"); | ||
NSNotificationCenter.defaultCenter().removeObserver(this, | ||
PropertyChangedNotification, null); | ||
} | ||
NSNotificationCenter.defaultCenter().addObserver(this, propertyChanged, | ||
PropertyChangedNotification, context); | ||
log.debug("Notifications registered for context: " + context); | ||
super.setD2wContext(context); | ||
} | ||
|
||
public NSMutableArray<String> updateContainerIDs() { | ||
log.debug("Updating container IDs: " | ||
+ updateContainerIDs.componentsJoinedByString(", ")); | ||
return updateContainerIDs; | ||
} | ||
|
||
public void propertyChanged(NSNotification n) { | ||
log.debug("Property changed for property key: " | ||
+ PROPERTY_KEY.valueInObject(n.object())); | ||
|
||
NSArray<String> updateProps = propertyChanged((D2WContext) n.object()); | ||
if (updateProps != null && updateProps.count() > 0) { | ||
|
||
refreshRelationships(updateProps); | ||
|
||
// collect the corresponding update container IDs | ||
NSMutableArray<String> attributeLineUCs = new NSMutableArray<String>(); | ||
D2WContext c = (D2WContext) n.object(); | ||
String pageConfiguration = (String) c.valueForKey("pageConfiguration"); | ||
for (String aPropertyName : updateProps) { | ||
String lineUC = pageConfiguration; | ||
lineUC = lineUC.concat(ERXStringUtilities.capitalize(aPropertyName)); | ||
lineUC = lineUC.concat("LineUC"); | ||
attributeLineUCs.addObject(lineUC); | ||
} | ||
|
||
ERXArrayUtilities.addObjectsFromArrayWithoutDuplicates(updateContainerIDs, | ||
attributeLineUCs); | ||
|
||
// force update of notification center UC | ||
AjaxUpdateContainer.safeUpdateContainerWithID(id, context()); | ||
log.debug("Container ids to be updated: " | ||
+ updateContainerIDs.componentsJoinedByString(", ")); | ||
} | ||
} | ||
|
||
public NSDictionary<String, NSArray<String>> propertyDependencies(D2WContext context) { | ||
NSDictionary<String, NSArray<String>> propertyDependencies = PROPERTY_DEPENDENCIES | ||
.valueInObject(context); | ||
return propertyDependencies; | ||
} | ||
|
||
/** | ||
* @param context | ||
* The d2wContext of the changed property level component | ||
* @return a list of property keys to be updated | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
public NSArray<String> propertyChanged(D2WContext context) { | ||
String prop = context.propertyKey(); | ||
NSArray<String> dependants = NSArray.EmptyArray; | ||
NSDictionary<String, NSArray<String>> propertyDependencies = PROPERTY_DEPENDENCIES | ||
.valueInObject(context); | ||
if (propertyDependencies.containsKey(prop)) { | ||
dependants = (NSArray<String>) propertyDependencies.valueForKey(prop); | ||
} | ||
return dependants; | ||
} | ||
|
||
/** | ||
* Sends out a notification to instances of | ||
* {@link ERMODEditRelationshipPage} for any relationships that have to be | ||
* updated, causing them to be refetched. | ||
* | ||
* @param updateProps | ||
*/ | ||
private void refreshRelationships(NSArray<String> updateProps) { | ||
for (String aPropertyKey : updateProps) { | ||
// TODO handle key paths to different entities | ||
if (d2wContext().entity().relationshipNamed(aPropertyKey) != null) { | ||
// this is a relationship, so we'll send out a notification | ||
Object obj = context().page(); | ||
String OBJECT_KEY = "object"; | ||
NSMutableDictionary<String, Object> userInfo = new NSMutableDictionary<String, Object>( | ||
obj, OBJECT_KEY); | ||
userInfo.setObjectForKey(d2wContext().valueForKey("object"), OBJECT_KEY); | ||
userInfo.setObjectForKey(aPropertyKey, "propertyKey"); | ||
userInfo.setObjectForKey(id, "ajaxNotificationCenterId"); | ||
// HACK: the delete action notification is the only way to | ||
// trigger a relationship component update for now | ||
NSNotificationCenter.defaultCenter().postNotification( | ||
ERMDDeleteButton.BUTTON_PERFORMED_DELETE_ACTION, obj, userInfo); | ||
log.debug("Sent update notification for relationship: " + aPropertyKey); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Since this component uses synchronization to update observers when the | ||
* d2wContext changes, it cannot be non-synchronizing. However, if we want | ||
* to be able to drop this component anywhere, it needs to be able to accept | ||
* any binding value. So this method simply returns value for key from the | ||
* dynamicBindings dictionary. | ||
*/ | ||
public Object handleQueryWithUnboundKey(String key) { | ||
if (log.isDebugEnabled()) { | ||
log.debug("Handling unbound key: " + key); | ||
} | ||
return dynamicBindings().objectForKey(key); | ||
} | ||
|
||
/** | ||
* Since this component uses synchronization to update observers when the | ||
* d2wContext changes, it cannot be non-synchronizing. However, if we want | ||
* to be able to drop this component anywhere, it needs to be able to accept | ||
* any binding value. So this method simply adds value for key to the | ||
* dynamicBindings dictionary. | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
public void handleTakeValueForUnboundKey(Object value, String key) { | ||
if (log.isDebugEnabled()) { | ||
log.debug("Take value: " + value + " for unbound key: " + key); | ||
} | ||
dynamicBindings().setObjectForKey(value, key); | ||
} | ||
|
||
} |