// // =========================================================================== // // Title: ERXBetweenQualifier.java // Description: [Description] // Author: Petite Abeille // Creation Date: Mon 20-Aug-2001 // Legal: Copyright (C) 2001 Petite Abeille. All Rights Reserved. // This class is hereby released for all uses. // No warranties whatsoever. // Motto: "Victory belongs to those who believe in it the longest" // // --------------------------------------------------------------------------- // package er.extensions.eof.qualifiers; import java.math.BigDecimal; import com.webobjects.eoaccess.EOEntity; import com.webobjects.eoaccess.EOQualifierSQLGeneration; import com.webobjects.eoaccess.EOSQLExpression; import com.webobjects.eocontrol.EOClassDescription; import com.webobjects.eocontrol.EOKeyValueArchiver; import com.webobjects.eocontrol.EOKeyValueQualifier; import com.webobjects.eocontrol.EOKeyValueUnarchiver; import com.webobjects.eocontrol.EOQualifier; import com.webobjects.eocontrol.EOQualifierEvaluation; import com.webobjects.eocontrol.EOQualifierVariable; import com.webobjects.foundation.NSCoder; import com.webobjects.foundation.NSComparator; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSMutableSet; import com.webobjects.foundation.NSTimestamp; import com.webobjects.foundation._NSStringUtilities; import er.extensions.qualifiers.ERXKeyValueQualifier; /** * The between qualifier allows qualification on an * attribute that is between two values. This qualifier * supports both in-memory and sql based qualification. * * The SQL generated is of the form: * "FOO BETWEEN 1 AND 3" * * Note this qualifier supports qualifing against String, Number * and NSTimestamp values. */ public class ERXBetweenQualifier extends ERXKeyValueQualifier implements EOQualifierEvaluation, Cloneable { /** * 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; /** register SQL generation support for the qualifier */ static { EOQualifierSQLGeneration.Support.setSupportForClass(new BetweenQualifierSQLGenerationSupport(), ERXBetweenQualifier.class); } // =========================================================================== // Constant(s) // --------------------------------------------------------------------------- /** holds the between sql string */ private static final String BetweenStatement = " BETWEEN "; /** holds the and sql string */ private static final String Separator = " AND "; // =========================================================================== // Class variable(s) // --------------------------------------------------------------------------- // =========================================================================== // Instance variable(s) // --------------------------------------------------------------------------- /** holds the key used to compare against */ private String _key = null; /** holds the minimun value */ private Object _minimumValue = null; /** holds the maximum value */ private Object _maximumValue = null; // =========================================================================== // Constructor method(s) // --------------------------------------------------------------------------- /** * Creates a qualifier for a given key with a * min and max value specified. * @param aKey key to qualify against * @param aMinimumValue minimum value of the key * @param aMaximumValue maximum value of the key */ public ERXBetweenQualifier(String aKey, Object aMinimumValue, Object aMaximumValue) { // Just to make EOKeyValueQualifier happy super(aKey, EOQualifier.QualifierOperatorEqual, aMinimumValue); setKey( aKey ); setMinimumValue( aMinimumValue ); setMaximumValue( aMaximumValue ); } // =========================================================================== // Class method(s) // --------------------------------------------------------------------------- // =========================================================================== // Instance method(s) // --------------------------------------------------------------------------- /** * Gets the key to qualify against. * @return qualifier key */ @Override public String key() { return _key; } /** * Sets the qualification key. * @param aValue for the qualification key. */ public void setKey(String aValue) { _key = aValue; } /** * Gets the minimum value. * @return minimum value. */ public Object minimumValue() { return _minimumValue; } /** * Sets the minimum value. * @param aValue new minimum value */ public void setMinimumValue(Object aValue) { _minimumValue = aValue; } /** * Gets the maximum value. * @return maximum value. */ public Object maximumValue() { return _maximumValue; } /** * Sets the maximum value. * @param aValue new maximum value */ public void setMaximumValue(Object aValue) { _maximumValue = aValue; } // =========================================================================== // EOQualifier method(s) // --------------------------------------------------------------------------- /** * Adds the qualification key of the qualifier to * the given set. * @param aSet to add the qualification key to. */ @Override public void addQualifierKeysToSet(NSMutableSet aSet) { if ( aSet != null ) { String aKey = key(); if ( aKey != null ) { aSet.addObject( aKey ); } } } /** * Creates another qualifier after replacing the values of the bindings. * Since this qualifier does not support qualifier binding keys a clone * of the qualifier is returned. * @param someBindings some bindings * @param requiresAll tells if the qualifier requires all bindings * @return clone of the current qualifier. */ @Override public EOQualifier qualifierWithBindings(NSDictionary someBindings, boolean requiresAll) { return (EOQualifier) clone(); } /** * This qualifier does not perform validation. This * is a no-op method. * @param aClassDescription to validation the qualifier keys * against. */ // FIXME: Should do something here... @Override public void validateKeysWithRootClassDescription(EOClassDescription aClassDescription) { } // =========================================================================== // EOQualifierSQLGeneration method(s) // --------------------------------------------------------------------------- public static class BetweenQualifierSQLGenerationSupport extends EOQualifierSQLGeneration.Support { /** * Public constructor */ public BetweenQualifierSQLGenerationSupport() { super(); } /** * Constructs the BETWEEN sql string for sql qualification. * @param aSQLExpression to contruct the qualifier for. * @return BETWEEN sql string for the qualifier. */ @Override public String sqlStringForSQLExpression(EOQualifier eoqualifier, EOSQLExpression aSQLExpression) { if ( ( aSQLExpression != null ) && ( aSQLExpression.entity() != null ) ) { ERXBetweenQualifier betweenQualifier = (ERXBetweenQualifier)eoqualifier; EOEntity anEntity = aSQLExpression.entity(); String aKey = betweenQualifier.key(); Object aMinimumValue = betweenQualifier.minimumValue(); Object aMaximumValue = betweenQualifier.maximumValue(); if ( ( aKey != null ) && ( aMinimumValue != null ) && ( aMaximumValue != null ) ) { StringBuilder aBuffer = new StringBuilder(); EOKeyValueQualifier aMinimumQualifier = new EOKeyValueQualifier( aKey, EOQualifier.QualifierOperatorEqual, aMinimumValue ); EOKeyValueQualifier aMaximumQualifier = new EOKeyValueQualifier( aKey, EOQualifier.QualifierOperatorEqual, aMaximumValue ); aMinimumQualifier = (EOKeyValueQualifier) anEntity.schemaBasedQualifier( aMinimumQualifier ); aMaximumQualifier = (EOKeyValueQualifier) anEntity.schemaBasedQualifier( aMaximumQualifier ); aBuffer.append( aSQLExpression.sqlStringForAttributeNamed( aMinimumQualifier.key() ) ); aBuffer.append( ERXBetweenQualifier.BetweenStatement ); aBuffer.append( aSQLExpression.sqlStringForValue( aMinimumQualifier.value(), aMinimumQualifier.key() ) ); aBuffer.append( ERXBetweenQualifier.Separator ); aBuffer.append( aSQLExpression.sqlStringForValue( aMaximumQualifier.value(), aMaximumQualifier.key() ) ); return aBuffer.toString(); } } return null; } // ENHANCEME: This should support restrictive qualifiers on the root entity @Override public EOQualifier schemaBasedQualifierWithRootEntity(EOQualifier eoqualifier, EOEntity eoentity) { return (EOQualifier)eoqualifier.clone(); } @Override public EOQualifier qualifierMigratedFromEntityRelationshipPath(EOQualifier eoqualifier, EOEntity eoentity, String s) { // the key migration is the same as for EOKeyValueQualifier ERXBetweenQualifier betweenQualifier=(ERXBetweenQualifier)eoqualifier; return new ERXBetweenQualifier(_translateKeyAcrossRelationshipPath(betweenQualifier.key(), s, eoentity), betweenQualifier.minimumValue(), betweenQualifier.maximumValue()); } } // =========================================================================== // EOQualifierEvaluation method(s) // --------------------------------------------------------------------------- /** * Determines the comparator to use for a given object based * on the object's class. * @param anObject to find the comparator for * @return comparator to use when comparing objects of a given * class. */ // ENHANCEME: Should have a way to extend this. protected NSComparator comparatorForObject(Object anObject) { if ( anObject != null ) { Class anObjectClass = anObject.getClass(); Class[] someClasses = { String.class, Number.class, NSTimestamp.class }; NSComparator[] someComparators = { NSComparator.AscendingStringComparator, NSComparator.AscendingNumberComparator, NSComparator.AscendingTimestampComparator }; int count = someClasses.length; for ( int index = 0; index < count; index++ ) { Class aClass = someClasses[ index ]; if ( aClass.isAssignableFrom( anObjectClass ) ) { return someComparators[ index ]; } } } return null; } /** * Compares an object to determine if it is within the * between qualification of the current qualifier. This * method is only used for in-memory qualification. * @return if the given object is within the boundries of * the qualifier. */ @Override public boolean evaluateWithObject(Object anObject) { if ( ( anObject != null ) && ( anObject instanceof NSKeyValueCoding ) ) { String aKey = key(); Object aMinimumValue = minimumValue(); Object aMaximumValue = maximumValue(); if ( ( aKey != null ) && ( aMinimumValue != null ) && ( aMaximumValue != null ) ) { Object aValue = ( (NSKeyValueCoding) anObject ).valueForKey( aKey ); if ( aValue != null ) { NSComparator aComparator = comparatorForObject( aValue ); if ( aComparator != null ) { boolean containsObject = false; try { int anOrder = aComparator.compare( aMinimumValue, aValue ); if ( ( anOrder == NSComparator.OrderedSame ) || ( anOrder == NSComparator.OrderedAscending ) ) { anOrder = aComparator.compare( aMaximumValue, aValue ); if ( ( anOrder == NSComparator.OrderedSame ) || ( anOrder == NSComparator.OrderedDescending ) ) { containsObject = true; } } } catch(NSComparator.ComparisonException anException) { } return containsObject; } } } } return false; } // =========================================================================== // Cloneable method(s) // --------------------------------------------------------------------------- /** * Implementation of the Clonable interface. * @return clone of the qualifier */ @Override public Object clone() { return new ERXBetweenQualifier(key(), minimumValue(), maximumValue()); } @Override public String toString() { return "(" + _key + " between " + valueStringForValue(_minimumValue) + " and " + valueStringForValue(_maximumValue) + ")"; } private String valueStringForValue(Object aValue) { String valueString; if(aValue == NSKeyValueCoding.NullValue) valueString = "null"; else if(aValue instanceof String) valueString = "'" + (String)aValue + "'"; else if((aValue instanceof Number) && !(aValue instanceof BigDecimal)) valueString = aValue.toString(); else if(aValue instanceof EOQualifierVariable) valueString = "$" + ((EOQualifierVariable)aValue).key(); else valueString = "(" + (aValue == null ? "null" : aValue.getClass().getName()) + ")" + _NSStringUtilities.quotedStringWithQuote(aValue == null ? "null" : aValue.toString(), '\''); return valueString; } @Override public Class classForCoder() { return getClass(); } public static Object decodeObject(NSCoder coder) { String key = (String)coder.decodeObject(); Object minimumValue = coder.decodeObject(); Object maximumValue = coder.decodeObject(); return new ERXBetweenQualifier(key, minimumValue, maximumValue); } @Override public void encodeWithCoder(NSCoder coder) { coder.encodeObject(key()); coder.encodeObject(minimumValue()); coder.encodeObject(maximumValue()); } @Override public void encodeWithKeyValueArchiver(EOKeyValueArchiver archiver) { archiver.encodeObject(key(), "key"); archiver.encodeObject(minimumValue(), "minimumValue"); archiver.encodeObject(maximumValue(), "maximumValue"); } public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver unarchiver) { return new ERXBetweenQualifier( (String)unarchiver.decodeObjectForKey("key"), unarchiver.decodeObjectForKey("minimumValue"), unarchiver.decodeObjectForKey("maximumValue")); } }