package er.extensions.eof.qualifiers;
import com.webobjects.eoaccess.EOQualifierSQLGeneration;
import com.webobjects.eoaccess.EOQualifierSQLGeneration._OrQualifierSupport;
import com.webobjects.eoaccess.EOSQLExpression;
import com.webobjects.eocontrol.EOAndQualifier;
import com.webobjects.eocontrol.EOKeyComparisonQualifier;
import com.webobjects.eocontrol.EOKeyValueQualifier;
import com.webobjects.eocontrol.EONotQualifier;
import com.webobjects.eocontrol.EOOrQualifier;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.eocontrol.EOQualifierVisitor;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSMutableArray;
import er.extensions.eof.ERXConstant;
import er.extensions.jdbc.ERXSQLHelper;
import er.extensions.qualifiers.ERXKeyValueQualifier;
/**
* ERXInOrQualifierSupport replaces the stock _OrQualifierSupport and turns qualifying EOOrQualifiers into IN-set SQL
* statements instead of enormous strings of OR's.
* <p>
* To register this as the generation support for EOOrQualifiers, register with:
* <p>
* EOQualifierSQLGeneration.Support.setSupportForClass(new ERXInOrQualifierSupport(), EOOrQualifier._CLASS);
*
* @author mschrag
*/
public class ERXInOrQualifierSupport extends _OrQualifierSupport {
@Override
public String sqlStringForSQLExpression(EOQualifier qualifier, EOSQLExpression sqlExpression) {
OrIsInVisitor visitor = new OrIsInVisitor();
qualifier._accept(visitor, false);
String sqlString;
NSArray values = visitor.values();
if (visitor.canBeRepresentedAsInSet() && values.count() > 0) {
if (values.count() == 1) {
EOKeyValueQualifier singleValueQualifier = new EOKeyValueQualifier(visitor.key(), EOQualifier.QualifierOperatorEqual, visitor.values().objectAtIndex(0));
sqlString = EOQualifierSQLGeneration.Support.supportForClass(EOKeyValueQualifier.class).sqlStringForSQLExpression(singleValueQualifier, sqlExpression);
}
else {
sqlString = ERXSQLHelper.newSQLHelper(sqlExpression).sqlWhereClauseStringForKey(sqlExpression, visitor.key(), visitor.values());
}
}
else {
sqlString = super.sqlStringForSQLExpression(qualifier, sqlExpression);
}
return sqlString;
}
protected static class OrIsInVisitor implements EOQualifierVisitor {
private boolean _canBeRepresentedAsInSet;
private String _key;
private NSMutableArray<Object> _values;
public OrIsInVisitor() {
_canBeRepresentedAsInSet = true;
_values = new NSMutableArray<>();
}
public boolean canBeRepresentedAsInSet() {
return _canBeRepresentedAsInSet;
}
public String key() {
return _key;
}
public NSMutableArray<Object> values() {
return _values;
}
public void visitKeyValueQualifier(EOKeyValueQualifier qualifier) {
if (_canBeRepresentedAsInSet) {
Class qualifierClass = qualifier.getClass();
// MS: You might end up with subclasses of EOKeyValueQualifier, which could
// cause terrible problems, so here we want to check for specific classes that we support
// and only convert them to in-sets if it's in the list.
if (qualifierClass == EOKeyValueQualifier.class || qualifierClass == ERXKeyValueQualifier.class) {
if (qualifier.selector() == EOQualifier.QualifierOperatorEqual) {
String key = qualifier.key();
Object value = qualifier.value();
// ak: this ends up in value.toString() (we should really use bind vars for the IN qualifier)
// so we need to exclude the obvious cases where the value produces garbage
if ((_key != null && !_key.equals(key)) || (value != null && (
(value instanceof ERXConstant.NumberConstant) ||
(value instanceof Number && !value.getClass().getName().startsWith("java.")) ||
(value == NSKeyValueCoding.NullValue)
))) {
_canBeRepresentedAsInSet = false;
}
else {
_key = key;
_values.addObject(qualifier.value());
}
}
else {
_canBeRepresentedAsInSet = false;
}
}
else {
_canBeRepresentedAsInSet = false;
}
}
}
public void visitAndQualifier(EOAndQualifier qualifier) {
_canBeRepresentedAsInSet = false;
}
public void visitKeyComparisonQualifier(EOKeyComparisonQualifier qualifier) {
_canBeRepresentedAsInSet = false;
}
public void visitNotQualifier(EONotQualifier qualifier) {
_canBeRepresentedAsInSet = false;
}
public void visitOrQualifier(EOOrQualifier qualifier) {
// MS: nested or statements are ok as long as it meets all
// the same criteria, so:
// (a = 5 or (a = 6 or a = 7) or a = 8) is a in (5,6,7,8)
}
public void visitUnknownQualifier(EOQualifier qualifier) {
_canBeRepresentedAsInSet = false;
}
}
}