package com.openbase.webobjects.qualifiers;
//
// InQualifier.java
//
// Much thanks to the Wonder guys and Pierre Bernard for their helpful examples
//
// The in qualifier allows qualification on an attribute that is contained in a list of values. This qualifier
// supports both in-memory and sql based qualification.
//
// The SQL generated is of the form: "VALUE IN (XX, YY, ZZ)"
//
// Created by Alex Cone on 12/16/05.
// Copyright (c) 2005 __MyCompanyName__. All rights reserved.
//
import java.util.Enumeration;
import java.util.StringTokenizer;
import com.webobjects.eoaccess.EOAttribute;
import com.webobjects.eoaccess.EOEntity;
import com.webobjects.eoaccess.EOJoin;
import com.webobjects.eoaccess.EOQualifierSQLGeneration;
import com.webobjects.eoaccess.EORelationship;
import com.webobjects.eoaccess.EOSQLExpression;
import com.webobjects.eocontrol.EOClassDescription;
import com.webobjects.eocontrol.EOEnterpriseObject;
import com.webobjects.eocontrol.EOKeyValueQualifier;
import com.webobjects.eocontrol.EOObjectStoreCoordinator;
import com.webobjects.eocontrol.EOOrQualifier;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.eocontrol.EOQualifierEvaluation;
import com.webobjects.eocontrol.EOQualifierVariable;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSKeyValueCodingAdditions;
import com.webobjects.foundation.NSMutableSet;
import com.webobjects.foundation.NSSet;
public class InQualifier extends EOKeyValueQualifier implements EOQualifierEvaluation, Cloneable {
private static final String InKeyword = " IN ";
private String _key = null;
private NSArray _values = null;
/** register SQL generation support for the qualifier */
static {
EOQualifierSQLGeneration.Support.setSupportForClass(new InQualifierSQLGenerationSupport(), InQualifier.class);
}
public static String allButLastPathComponent(String path) {
int i = path.lastIndexOf(NSKeyValueCodingAdditions.KeyPathSeparator);
return (i < 0) ? null : path.substring(0, i);
}
public static String lastPathComponent(String path) {
int i = path.lastIndexOf(NSKeyValueCodingAdditions.KeyPathSeparator);
return (i < 0) ? path : path.substring(i + 1);
}
public InQualifier(String aKey, NSArray values) {
// Just to make EOKeyValueQualifier happy
super(aKey, EOQualifier.QualifierOperatorEqual, values);
this.setKey(aKey);
this.setValues(values);
}
public String key() {
return _key;
}
public void setKey(String aValue) {
_key = aValue;
}
public NSArray values() {
return _values;
}
public void setValues(NSArray anArray) {
_values = anArray;
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("(");
buffer.append(key());
buffer.append(InKeyword);
buffer.append("(");
Enumeration e = values().objectEnumerator();
while (e.hasMoreElements()) {
Object object = e.nextElement();
if (object == NSKeyValueCoding.NullValue) {
buffer.append("null");
} else if (object instanceof Number) {
buffer.append(object);
} else if (object instanceof EOQualifierVariable) {
buffer.append("$");
buffer.append(((EOQualifierVariable) object).key());
} else {
buffer.append("'");
buffer.append(object);
buffer.append("'");
}
if (e.hasMoreElements()) {
buffer.append(", ");
}
}
buffer.append("))");
return buffer.toString();
}
public void addQualifierKeysToSet(NSMutableSet aSet) {
if (aSet != null) {
String aKey = this.key();
if (aKey != null) {
aSet.addObject(aKey);
}
}
}
public boolean evaluateWithObject(Object object) {
Object value = NSKeyValueCodingAdditions.Utility.valueForKeyPath(object, key());
if (value == null) {
value = NSKeyValueCoding.NullValue;
}
return values().containsObject(value);
}
// we don't do bindings
public EOQualifier qualifierWithBindings(NSDictionary someBindings, boolean requiresAll) {
return (EOQualifier) this.clone();
}
// we don't do validation
public void validateKeysWithRootClassDescription(EOClassDescription aClassDescription) {
super.validateKeysWithRootClassDescription(aClassDescription);
}
public static class InQualifierSQLGenerationSupport extends EOQualifierSQLGeneration.Support {
public InQualifierSQLGenerationSupport() {
super();
}
public String sqlStringForSQLExpression(EOQualifier eoqualifier, EOSQLExpression aSQLExpression) {
String sqlString = null;
if ((aSQLExpression != null) && (aSQLExpression.entity() != null)) {
InQualifier inQualifier = (InQualifier)eoqualifier;
EOEntity anEntity = aSQLExpression.entity();
String aKey = inQualifier.key();
NSArray aValuesArray = inQualifier.values();
if ((aKey != null) && (aValuesArray != null) && (aValuesArray.count() > 0)) {
StringBuffer sb = new StringBuffer();
EOAttribute keyAttr = attributeForPath(anEntity, aKey);
String attributeString = aSQLExpression.sqlStringForAttribute(keyAttr);
sb.append(aSQLExpression.formatSQLString(attributeString, keyAttr.readFormat()));
sb.append(InQualifier.InKeyword);
sb.append("(");
for (int i = 0; i < aValuesArray.count(); i++ ) {
EOKeyValueQualifier containsQualifier = new EOKeyValueQualifier(aKey, EOQualifier.QualifierOperatorContains,
aValuesArray.objectAtIndex(i));
containsQualifier = (EOKeyValueQualifier) anEntity.schemaBasedQualifier(containsQualifier);
if ( i > 0 ) {
sb.append(", ");
}
sb.append(aSQLExpression.sqlStringForValue(containsQualifier.value(), containsQualifier.key()));
}
sb.append(")");
sqlString = sb.toString();
}
}
return sqlString;
}
public EOQualifier schemaBasedQualifierWithRootEntity(EOQualifier qualifier, EOEntity entity) {
InQualifier inQualifier = (InQualifier) qualifier;
String keyPath = inQualifier.key();
EORelationship relationship = relationshipForPath(entity, keyPath);
if (relationship != null) {
if (relationship.isFlattened()) {
relationship = (EORelationship) relationship.componentRelationships().lastObject();
}
// just handle single key joins for now
EOJoin join = (EOJoin)relationship.joins().objectAtIndex(0);
String destAttributeName = join.destinationAttribute().name();
String optimizedPath = optimizeQualifierKeyPath(entity, keyPath, destAttributeName);
NSMutableSet newValues = new NSMutableSet(inQualifier.values().count());
Enumeration values = inQualifier.values().objectEnumerator();
while (values.hasMoreElements()) {
Object value = values.nextElement();
if (value == NSKeyValueCoding.NullValue || (value instanceof EOQualifierVariable)) {
newValues.addObject(value);
} else {
EOEnterpriseObject enterpriseObject = (EOEnterpriseObject) value;
EOObjectStoreCoordinator objectStoreCoordinator =
(EOObjectStoreCoordinator) enterpriseObject.editingContext().rootObjectStore();
NSDictionary destValues =
objectStoreCoordinator.valuesForKeys(new NSArray(destAttributeName), enterpriseObject);
Object destVal = destValues.objectForKey(destAttributeName);
newValues.addObject((destVal != null ? destVal: NSKeyValueCoding.NullValue));
}
}
return nullValueAwareQualifier(new InQualifier(optimizedPath, newValues.allObjects()));
} else {
return nullValueAwareQualifier(inQualifier);
}
}
public EOQualifier qualifierMigratedFromEntityRelationshipPath(EOQualifier eoqualifier, EOEntity eoentity, String s) {
// the key migration is the same as for EOKeyValueQualifier
InQualifier inQualifier=(InQualifier)eoqualifier;
return new InQualifier(_translateKeyAcrossRelationshipPath(inQualifier.key(), s, eoentity), inQualifier.values());
}
// Protected instance methods
protected EOQualifier nullValueAwareQualifier(InQualifier inQualifier) {
if (inQualifier.values().containsObject(NSKeyValueCoding.NullValue)) {
EOQualifier nullQual = new EOKeyValueQualifier(inQualifier.key(), EOQualifier.QualifierOperatorEqual, NSKeyValueCoding.NullValue);
NSSet aSet = new NSSet(inQualifier.values());
NSSet noNullSet = aSet.setBySubtractingSet(new NSSet(NSKeyValueCoding.NullValue));
EOQualifier noNullQual = new InQualifier(inQualifier.key(), noNullSet.allObjects());
return new EOOrQualifier(new NSArray(new Object[] { nullQual, noNullQual }));
} else {
return inQualifier;
}
}
protected EOAttribute attributeForPath(EOEntity entity, String keyPath) {
if (keyPath != null) {
StringTokenizer tokenizer = new StringTokenizer(keyPath, NSKeyValueCodingAdditions.KeyPathSeparator);
EORelationship relationship = null;
while (tokenizer.hasMoreElements()) {
String key = tokenizer.nextToken();
if (tokenizer.hasMoreElements()) {
relationship = entity.anyRelationshipNamed(key);
} else {
EOAttribute attribute = entity.anyAttributeNamed(key);
if (attribute == null) {
relationship = entity.anyRelationshipNamed(key);
if (relationship != null) {
if (relationship.isFlattened()) {
relationship = (EORelationship) relationship.componentRelationships().lastObject();
}
// just handle single key joins for now
EOJoin join = (EOJoin)relationship.joins().objectAtIndex(0);
return join.sourceAttribute();
}
}
}
if (relationship != null) {
entity = relationship.destinationEntity();
} else {
return null;
}
}
return null;
}
return null;
}
protected EORelationship relationshipForPath(EOEntity entity, String keyPath) {
if (keyPath != null) {
StringTokenizer tokenizer = new StringTokenizer(keyPath, NSKeyValueCodingAdditions.KeyPathSeparator);
EORelationship relationship = null;
while (tokenizer.hasMoreElements()) {
String key = tokenizer.nextToken();
relationship = entity.anyRelationshipNamed(key);
if (relationship != null) {
entity = relationship.destinationEntity();
} else {
return null;
}
}
return relationship;
}
return null;
}
protected String optimizeQualifierKeyPath(EOEntity entity, String keyPath, String attributeName) {
if ((keyPath == null) || (keyPath.length() == 0)) {
return attributeName;
} else {
EORelationship relationship = (entity == null) ? null : relationshipForPath(entity, keyPath);
if (relationship != null) {
NSArray joins = relationship.joins();
int joinCount = (joins == null) ? 0 : joins.count();
for (int i = joinCount - 1; i >= 0; i--) {
EOJoin join = (EOJoin) joins.objectAtIndex(i);
if (join.destinationAttribute().name().equals(attributeName)) {
String newPath = allButLastPathComponent(keyPath);
String newAttributeName = join.sourceAttribute().name();
return optimizeQualifierKeyPath(entity, newPath, newAttributeName);
}
}
}
return (keyPath + NSKeyValueCodingAdditions.KeyPathSeparator + attributeName);
}
}
}
// cloning support
public Object clone() {
return new InQualifier(this.key(), this.values());
}
}