package er.ajax.look.components;
import org.apache.log4j.Logger;
import com.webobjects.appserver.WOContext;
import com.webobjects.directtoweb.D2WContext;
import com.webobjects.eocontrol.EOKeyValueCodingAdditions;
import com.webobjects.foundation.NSArray;
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.look.interfaces.PropertyChangedDelegate;
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;
public class AjaxNotificationCenter extends ERDCustomComponent {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
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<PropertyChangedDelegate> PROPERTY_CHANGED_DELEGATE = new ERXKey<PropertyChangedDelegate>("propertyChangedDelegate");
public static final String PropertyChangedNotification = "PropertyChangedNotification";
public static final String RegisterPropertyObserverIDNotification = "RegisterPropertyObserverIDNotification";
@SuppressWarnings("rawtypes")
private static final NSSelector propertyChanged = new NSSelector("propertyChanged", ERXConstant.NotificationClassArray);
@SuppressWarnings("rawtypes")
private static final NSSelector registerPropertyObserverID = new NSSelector("registerPropertyObserverID", ERXConstant.NotificationClassArray);
private String id;
private NSMutableDictionary<String, String> propertyObserverIDs = new NSMutableDictionary<String, String>();
private NSMutableArray<String> updateContainerIDs = new NSMutableArray<String>();
private static final Logger log = Logger.getLogger(AjaxNotificationCenter.class);
public String id() {
if(id == null) {
id = ERXWOContext.safeIdentifierName(context(), true);
AJAX_NOTIFICATION_CENTER_ID.takeValueInObject(id, d2wContext());
}
return id;
}
public AjaxNotificationCenter(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().removeObserver(this, RegisterPropertyObserverIDNotification, null);
}
NSNotificationCenter.defaultCenter().addObserver(this, propertyChanged, PropertyChangedNotification, context);
NSNotificationCenter.defaultCenter().addObserver(this, registerPropertyObserverID, RegisterPropertyObserverIDNotification, context);
log.debug("Notifications registered for context: " + context);
super.setD2wContext(context);
}
public NSMutableArray<String> updateContainerIDs() {
log.debug("Updating container IDs: " + updateContainerIDs.componentsJoinedByString(", "));
return updateContainerIDs;
}
@SuppressWarnings("unchecked")
public void propertyChanged(NSNotification n) {
log.debug("Property changed for property key: " + PROPERTY_KEY.valueInObject(n.object()));
PropertyChangedDelegate delegate = PROPERTY_CHANGED_DELEGATE.valueInObject(n.object());
if(delegate != null) {
log.debug("Updating container id list with propertyChangedDelegate");
NSArray<String> updateProps = delegate.propertyChanged((D2WContext)n.object());
NSArray updateIDs = EOKeyValueCodingAdditions.Utility.valuesForKeys(propertyObserverIDs, updateProps).allValues();
updateIDs = ERXArrayUtilities.removeNullValues(updateIDs);
updateContainerIDs.addObjectsFromArray(updateIDs);
log.debug("Container ids to be updated: " + updateContainerIDs.componentsJoinedByString(", "));
}
}
public void registerPropertyObserverID(NSNotification n) {
String propKey = PROPERTY_KEY.valueInObject(n.object());
String propID = PROPERTY_OBSERVER_ID.valueInObject(n.object());
if(!ERXStringUtilities.stringIsNullOrEmpty(propKey) && !ERXStringUtilities.stringIsNullOrEmpty(propID)) {
propertyObserverIDs.setObjectForKey(propID, propKey);
log.debug("ID registered for property: (" + propKey + ", " + propID + ")" );
}
}
/**
* 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);
}
}