/*
* Copyright (C) NetStruxr, Inc. All rights reserved.
*
* This software is published under the terms of the NetStruxr
* Public Software License version 0.5, a copy of which has been
* included with this distribution in the LICENSE.NPL file. */
package er.directtoweb.assignments.delayed;
import org.apache.log4j.Logger;
import com.webobjects.directtoweb.D2WContext;
import com.webobjects.eocontrol.EOKeyValueUnarchiver;
import com.webobjects.foundation.NSArray;
import er.extensions.eof.ERXKey;
import er.extensions.foundation.ERXValueUtilities;
import er.extensions.localization.ERXLocalizer;
/**
* The delayed non-null conditional is a way to provide a branching result from
* a rule resolution. The value of this assignment must be a dictionary that has
* the following keys:
* <dl>
* <dt>dictionary key "nonNullKeyPath":</dt>
* <dd>key path to be tested for nullability off of the current D2W context.</dd>
* <dt>dictionary key "trueValue":</dt>
* <dd>value to be returned if the key path is not null.</dd>
* <dt>dictionary key "falseValue":</dt>
* <dd>value to be returned if the key path is null.</dd>
* <dt>dictionary key "negate":</dt>
* <dd>if true, returns trueValue when null and falseValue when not null.</dd>
* <dt>dictionary key "localize":</dt>
* <dd>if true, localizes the result before returning it</dd>
* </dl>
* <p>
* Because this assignment is a delayed assignment the above condition will be
* evaluated every time that the D2W fired rule cache resolves to a rule of this
* class.
* <p>
* Example usage. Let's imagine that a User has a relationship called
* <code>toOwnedHouse</code> this relationship is only set if the User is a home
* owner. Now let's imagine that we have a page configuration for displaying
* information about a User. One of the propertyKeys for this page configuration
* is 'residence' which is not an attribute or relationship off of the User
* object. Imagine that we have already built a custom component for displaying
* the address of either an owned house or a rented house for a given User.
* However we want the displayed name to be either "Rented House" or
* "Owned House" depending on if the User is a home owner. The usual approach
* would be to create two page configurations and set the displayNameForProperty
* to be different for each of these page configurations. However by using a
* DelayedNonNullConditionalAssignment we will only have to use a single page
* configuration. Using this rule:
* <pre><code>
* pageConfguration = 'InspectUser' AND propertyKey = 'residence' =>
* displayNameForProperty =
* {
* nonNullKeyPath = "object.toOwnedHouse";
* trueValue = "Owned House";
* falseValue = "Rented House";
* }</code></pre>
*/
public class ERDDelayedNonNullConditionalAssignment extends ERDDelayedAssignment {
/**
* 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;
private static final ERXKey<String> NON_NULL_KEY_PATH = new ERXKey<>("nonNullKeyPath");
private static final ERXKey<String> TRUE_VALUE = new ERXKey<>("trueValue");
private static final ERXKey<String> FALSE_VALUE = new ERXKey<>("falseValue");
private static final ERXKey<String> NEGATE = new ERXKey<>("negate");
private static final ERXKey<String> LOCALIZE = new ERXKey<>("localize");
/** logging support */
public final static Logger log = Logger.getLogger("er.directtoweb.rules.DelayedNonNullConditionalAssigment");
/**
* Static constructor required by the EOKeyValueUnarchiver interface. If
* this isn't implemented then the default behavior is to construct the
* first super class that does implement this method. Very lame.
*
* @param eokeyvalueunarchiver
* to be unarchived
* @return decoded assignment of this class
*/
public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver eokeyvalueunarchiver) {
return new ERDDelayedNonNullConditionalAssignment(eokeyvalueunarchiver);
}
/**
* Public constructor
*
* @param u
* key-value unarchiver used when unarchiving from rule files.
*/
public ERDDelayedNonNullConditionalAssignment(EOKeyValueUnarchiver u) {
super(u);
}
/**
* Public constructor
*
* @param key
* context key
* @param value
* of the assignment
*/
public ERDDelayedNonNullConditionalAssignment(String key, Object value) {
super(key, value);
}
/**
* Implementation of the
* {@link er.directtoweb.assignments.ERDComputingAssignmentInterface}. This
* assignment depends upon an array composed of the single value
* "nonNullKeyPath" from the dictionary of the value of this assignment.
* This array of keys is used when constructing the significant keys for the
* passed in keyPath.
*
* @param keyPath
* to compute significant keys for.
* @return array of context keys this assignment depends upon.
*/
public NSArray<String> dependentKeys(String keyPath) {
return new NSArray<>(NON_NULL_KEY_PATH.valueInObject(value()));
}
/**
* Implementation of the abstract method from
* {@link er.directtoweb.assignments.delayed.ERDDelayedAssignment}. This
* method is called each time this Assignment is resolved from the rule
* firing cache. For the non-null conditional the dictionary key
* 'nonNullKeyPath' is checked against the current context. If the key path
* is indeed non-null then the object returned by the dictionary key
* 'trueValue' will be returned otherwise the object returned by the
* dictionary key 'falseValue' will be returned.
*
* @param c
* current D2W context
* @return Either the 'trueValue' or 'falseValue' depending on if the key
* path is non-null or null.
*/
@Override
public Object fireNow(D2WContext c) {
Object result = null;
String keyPath = NON_NULL_KEY_PATH.valueInObject(value());
boolean negate = ERXValueUtilities.booleanValue(NEGATE.valueInObject(value()));
boolean localize = ERXValueUtilities.booleanValue(LOCALIZE.valueInObject(value()));
boolean isNull = ERXValueUtilities.isNull(c.valueForKeyPath(keyPath));
ERXKey<String> resultKey = isNull != negate ? FALSE_VALUE : TRUE_VALUE;
result = resultKey.valueInObject(value());
if (localize) {
String key = (String) result;
result = ERXLocalizer.currentLocalizer().localizedTemplateStringForKeyWithObject(key, c);
}
if (log.isDebugEnabled()) {
log.debug("ResultKey: " + resultKey + " = " + result);
}
return result;
}
}