/*
* 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.EOAndQualifier;
import com.webobjects.eocontrol.EOKeyValueQualifier;
import com.webobjects.eocontrol.EOKeyValueUnarchiver;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSMutableArray;
import er.extensions.eof.qualifiers.ERXPrimaryKeyListQualifier;
/**
* Very useful when you want to restrict the things a user can see during
* searches or in list pages. Set it up via a rule like:
*
* <pre>
* <code>
* entity.name = "Movie" and session.user.role <> "admin"
* =>
* extraRestrictingQualifier = {
* "studio" = "session.user.studios";
* } [er.directtoweb.ERDDelayedExtraQualifierAssignment]
* </code>
* </pre>
*
* then in your query page use sth like:
*
* <pre>
* <code>
* public EODataSource queryDataSource() {
* EODataSource ds = super.queryDataSource();
* if (ds != null && (ds instanceof EODatabaseDataSource)) {
* EOFetchSpecification fs = ((EODatabaseDataSource)ds).fetchSpecification();
* EOQualifier q = fs.qualifier();
* EOQualifier extraQualifier = (EOQualifier)d2wContext().valueForKey("extraRestrictingQualifier");
* if(q != null && extraQualifier != null) {
* q = new EOAndQualifier(new NSArray(new Object[] {q, extraQualifier}));
* } else if(extraQualifier != null) {
* q = extraQualifier;
* }
* fs.setQualifier(q);
* }
* return ds;
* }</code>
* </pre>
*
* This should guarantee that the user can only see the Movies that are made by
* studios contained in his studio relationship. If the value is null, then this
* qualifier will not be added. To search for NULL, return
* NSKeyValueCoding.NullValue.<br>
* <br>
* To use another than the default "equals" operator, specify one of the
* following abbreviations:
* <ul>
* <li>ne (not equals)
* <li>gt (greater than)
* <li>gte (greater than or equal)
* <li>lt (less than)
* <li>lte (less than or equal)
* <li>like (case-sensitive like)
* <li>ilike (case-insensitive like)
* </ul>
*
* The following example will limit results to objects that don't have the same
* id as the source object (often useful for self-referencing relationships) and
* whose startDateTime is less than the source object's startDateTime:
*
* <pre>
* {
* "id" = {
* "ne" = "object.id";
* };
* "startDateTime" = {
* "lt" = "object.startDateTime";
* };
* }
* </pre>
*
* @author ak
*/
public class ERDDelayedExtraQualifierAssignment 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;
/** logging support */
public static final Logger log = Logger.getLogger(ERDDelayedExtraQualifierAssignment.class);
/**
* 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 ERDDelayedExtraQualifierAssignment(eokeyvalueunarchiver);
}
/**
* Public constructor
* @param u key-value unarchiver used when unarchiving
* from rule files.
*/
public ERDDelayedExtraQualifierAssignment (EOKeyValueUnarchiver u) { super(u); }
/**
* Public constructor
* @param key context key
* @param value of the assignment
*/
public ERDDelayedExtraQualifierAssignment (String key, Object value) { super(key,value); }
protected EOQualifier qualifierForArray(String key, NSArray objects) {
if (objects == null)
return null;
if (objects.count() == 0)
return new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorEqual, null);
return new ERXPrimaryKeyListQualifier(key, objects);
}
protected EOQualifier qualifierForObject(String key, Object object) {
return new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorEqual, object);
}
protected EOQualifier qualifierForObject(String key, NSDictionary object) {
return new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorEqual, object);
}
protected EOQualifier qualifierForOperatorAndObject(String key,
String operatorKey,
Object value) {
if ("eq".equals(operatorKey)) {
return new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorEqual, value);
} else if ("ne".equals(operatorKey)) {
return new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorNotEqual,
value);
} else if ("gt".equals(operatorKey)) {
return new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorGreaterThan,
value);
} else if ("gte".equals(operatorKey)) {
return new EOKeyValueQualifier(key,
EOQualifier.QualifierOperatorGreaterThanOrEqualTo, value);
} else if ("lt".equals(operatorKey)) {
return new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorLessThan,
value);
} else if ("lte".equals(operatorKey)) {
return new EOKeyValueQualifier(key,
EOQualifier.QualifierOperatorLessThanOrEqualTo, value);
} else if ("like".equals(operatorKey)) {
return new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorLike, value);
} else if ("ilike".equals(operatorKey)) {
return new EOKeyValueQualifier(key,
EOQualifier.QualifierOperatorCaseInsensitiveLike, value);
}
return new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorNotEqual, value);
}
protected EOQualifier extraQualifier(D2WContext c, NSDictionary<String, Object> dict) {
NSMutableArray<EOQualifier> qualifiers = new NSMutableArray<>();
EOQualifier result = null;
for (String key : dict.allKeys()) {
Object value = null;
if (dict.objectForKey(key) instanceof NSDictionary) {
// qualifier definition with operator
NSDictionary qDict = (NSDictionary) dict.objectForKey(key);
if (qDict.size() == 1) {
String operatorKey = (String) qDict.allKeys().lastObject();
String contextKeyPath = (String) qDict.objectForKey(operatorKey);
if ("NSKeyValueCoding.NullValue".equals(contextKeyPath)) {
value = NSKeyValueCoding.NullValue;
} else {
value = c.valueForKeyPath(contextKeyPath);
}
if (value != null) {
EOQualifier q = qualifierForOperatorAndObject(key, operatorKey,
value);
qualifiers.addObject(q);
}
}
} else {
value = c.valueForKeyPath((String) dict.objectForKey(key));
if (value != null) {
EOQualifier q;
if (value instanceof NSArray) {
q = qualifierForArray(key, (NSArray) value);
} else {
if (value == NSKeyValueCoding.NullValue) {
value = null;
}
q = qualifierForObject(key, value);
}
if (q != null) {
qualifiers.addObject(q);
}
}
}
}
if (qualifiers.count() > 0)
result = new EOAndQualifier(qualifiers);
if (log.isDebugEnabled()) {
log.debug("Computed qualifier: " + result);
}
return result;
}
@Override
public Object fireNow(D2WContext c) {
Object result = null;
Object value = value();
if (value != null && value instanceof NSDictionary) {
result = extraQualifier(c, (NSDictionary) value);
}
return result;
}
}