package er.directtoweb;
import com.webobjects.directtoweb.Assignment;
import com.webobjects.directtoweb.Rule;
import com.webobjects.eocontrol.EOAndQualifier;
import com.webobjects.eocontrol.EOKeyValueArchiver;
import com.webobjects.eocontrol.EOKeyValueUnarchiver;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSMutableDictionary;
import er.extensions.foundation.ERXProperties;
/**
* Rule class that works around two problems:
* <ul>
* <li>when you have an assignment class that is not present in the classpath
* then the model will not load, making for very strange errors. We replace the
* missing class with the normal assignment class and log the error.
* <li>when evaluating rule priorities, the default is to place rules containing <code>pageConfiguration</code>
* keys so high up that they will get preferred over rules without such a condition, but with a higher author setting.
* This is pretty ridiculous and leads to having to set <code>... AND (pageConfiguration like '*')</code>
* in all the conditions.<br>
* We place rules with a <code>pageConfiguration</code> so high that they will be higher than rules with the same author setting
* but lower than a rule with a higher setting.
* </ul>
* In order to be usable with the D2WClient and Rule editor, we also patch the encoded
* dictionary so these tools find no trace of the patched rules.
* @author ak
*/
public class ERD2WRule extends Rule {
/**
* 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 int _priority = -1;
private String _assignmentClassName;
private boolean patchRulePriority = ERXProperties.booleanForKeyWithDefault("er.directtoweb.ERD2WRule.patchRulePriority", true);
public ERD2WRule() {
super();
}
public ERD2WRule(EOKeyValueUnarchiver eokeyvalueunarchiver) {
super(eokeyvalueunarchiver.decodeIntForKey("author"),
((EOQualifier) eokeyvalueunarchiver.decodeObjectForKey("lhs")),
((Assignment) eokeyvalueunarchiver.decodeObjectForKey("rhs")));
_assignmentClassName = (String)eokeyvalueunarchiver.decodeObjectForKey("assignmentClassName");
}
public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver eokeyvalueunarchiver) {
ERD2WRule rule = null;
try {
rule = new ERD2WRule(eokeyvalueunarchiver);
} catch(Throwable t) {
// AK: this occurs mostly when we want to load a rule that contains an assigment class which can't be found
//HACK cheesy way to get at the encoded rule dictionary
NSMutableDictionary dict = (NSMutableDictionary)NSKeyValueCoding.Utility.valueForKey(eokeyvalueunarchiver,"propertyList");
String ruleString = dict.toString();
// now store the old assignment class
dict.takeValueForKeyPath(dict.valueForKeyPath("rhs.class"), "assignmentClassName");
// and push in the default class
dict.takeValueForKeyPath(Assignment.class.getName(), "rhs.class");
// try again
try {
rule = new ERD2WRule(eokeyvalueunarchiver);
ruleString = rule.toString();
} finally {
ERD2WModel.log.error("Problems with this rule: \n" + t + "\n" + ruleString, t);
}
}
return rule;
}
/**
* Overridden to patch the normal rule class name into the generated dictionary.
* @see com.webobjects.eocontrol.EOKeyValueArchiving#encodeWithKeyValueArchiver(com.webobjects.eocontrol.EOKeyValueArchiver)
*/
@Override
public void encodeWithKeyValueArchiver (EOKeyValueArchiver eokeyvaluearchiver) {
super.encodeWithKeyValueArchiver(eokeyvaluearchiver);
((NSMutableDictionary)eokeyvaluearchiver.dictionary()).setObjectForKey(Rule.class.getName(), "class");
}
/**
* Overridden to work around
* @see com.webobjects.directtoweb.Rule#priority()
*/
@Override
public int priority() {
if (!patchRulePriority) {
// TC - Added for MZ because we have thousands of rules and don't want to comb through all of them to change their priorities.
return super.priority();
}
if(_priority == -1) {
EOQualifier lhs = lhs();
String lhsString = "";
_priority = 1000 * author();
if(lhs != null) {
lhsString = lhs.toString();
if(lhsString.indexOf("dummyTrue") == -1) {
if(lhsString.indexOf("pageConfiguration") != -1) {
_priority += 500;
}
if(lhs() instanceof EOAndQualifier) {
_priority += ((EOAndQualifier)lhs()).qualifiers().count();
} else {
_priority ++;
}
}
}
}
return _priority;
}
public String assignmentClassName() {
if(_assignmentClassName == null) {
_assignmentClassName = rhs().getClass().getName();
}
return _assignmentClassName;
}
public ERD2WRule cloneRule() {
EOKeyValueArchiver archiver = new EOKeyValueArchiver();
encodeWithKeyValueArchiver(archiver);
EOKeyValueUnarchiver unarchiver = new EOKeyValueUnarchiver(archiver.dictionary());
return new ERD2WRule(unarchiver);
}
/**
* Builds a string like:<br>
* <pre><code> 100: ((entity.name = 'Bug') and (task = 'edit')) => isInspectable = true [com.directtowen.BooleanAssignment]</code></pre>
* @return a nice description of the rule
*/
@Override
public String toString() {
String prefix = " ";
String rhsClass = assignmentClassName();
return (
prefix.substring(0, prefix.length() - ("" + author()).length()) + author() + " : " +
(lhs() == null ? "*true*" : lhs().toString()) +
" => " +
(rhs() == null ? "<NULL>" : rhs().keyPath() + " = " + rhs().value() +
( rhsClass.equals(Assignment.class.getName()) ? "" : " [" + rhsClass + "]")
) +
" (" + priority() + ")");
}
}