--- /dev/null
+package com.vaadin.data.util.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+
+/**
+ * Simple container filter comparing an item property value against a given
+ * constant value. Use the nested classes {@link Equal}, {@link Greater},
+ * {@link Less}, {@link GreaterOrEqual} and {@link LessOrEqual} instead of this
+ * class directly.
+ *
+ * This filter also directly supports in-memory filtering.
+ *
+ * The reference and actual values must implement {@link Comparable} and the
+ * class of the actual property value must be assignable from the class of the
+ * reference value.
+ *
+ * @since 6.6
+ */
+public abstract class Compare implements Filter {
+
+ public enum Operation {
+ EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL
+ };
+
+ private final Object propertyId;
+ private final Operation operation;
+ private final Object value;
+
+ /**
+ * A {@link Compare} filter that accepts items for which the identified
+ * property value is equal to <code>value</code>.
+ *
+ * For in-memory filters, equals() is used for the comparison. For other
+ * containers, the comparison implementation is container dependent and may
+ * use e.g. database comparison operations.
+ *
+ * @since 6.6
+ */
+ public static final class Equal extends Compare {
+ /**
+ * Construct a filter that accepts items for which the identified
+ * property value is equal to <code>value</code>.
+ *
+ * For in-memory filters, equals() is used for the comparison. For other
+ * containers, the comparison implementation is container dependent and
+ * may use e.g. database comparison operations.
+ *
+ * @param propertyId
+ * the identifier of the property whose value to compare
+ * against value
+ * @param value
+ * the value to compare against
+ */
+ public Equal(Object propertyId, Object value) {
+ super(propertyId, value, Operation.EQUAL);
+ }
+ }
+
+ /**
+ * A {@link Compare} filter that accepts items for which the identified
+ * property value is greater than <code>value</code>.
+ *
+ * For in-memory filters, the values must implement {@link Comparable} and
+ * {@link Comparable#compareTo(Object)} is used for the comparison. For
+ * other containers, the comparison implementation is container dependent
+ * and may use e.g. database comparison operations.
+ *
+ * @since 6.6
+ */
+ public static final class Greater extends Compare {
+ /**
+ * Construct a filter that accepts items for which the identified
+ * property value is greater than <code>value</code>.
+ *
+ * For in-memory filters, the values must implement {@link Comparable}
+ * and {@link Comparable#compareTo(Object)} is used for the comparison.
+ * For other containers, the comparison implementation is container
+ * dependent and may use e.g. database comparison operations.
+ *
+ * @param propertyId
+ * the identifier of the property whose value to compare
+ * against value
+ * @param value
+ * the value to compare against
+ */
+ public Greater(Object propertyId, Object value) {
+ super(propertyId, value, Operation.GREATER);
+ }
+ }
+
+ /**
+ * A {@link Compare} filter that accepts items for which the identified
+ * property value is less than <code>value</code>.
+ *
+ * For in-memory filters, the values must implement {@link Comparable} and
+ * {@link Comparable#compareTo(Object)} is used for the comparison. For
+ * other containers, the comparison implementation is container dependent
+ * and may use e.g. database comparison operations.
+ *
+ * @since 6.6
+ */
+ public static final class Less extends Compare {
+ /**
+ * Construct a filter that accepts items for which the identified
+ * property value is less than <code>value</code>.
+ *
+ * For in-memory filters, the values must implement {@link Comparable}
+ * and {@link Comparable#compareTo(Object)} is used for the comparison.
+ * For other containers, the comparison implementation is container
+ * dependent and may use e.g. database comparison operations.
+ *
+ * @param propertyId
+ * the identifier of the property whose value to compare
+ * against value
+ * @param value
+ * the value to compare against
+ */
+ public Less(Object propertyId, Object value) {
+ super(propertyId, value, Operation.LESS);
+ }
+ }
+
+ /**
+ * A {@link Compare} filter that accepts items for which the identified
+ * property value is greater than or equal to <code>value</code>.
+ *
+ * For in-memory filters, the values must implement {@link Comparable} and
+ * {@link Comparable#compareTo(Object)} is used for the comparison. For
+ * other containers, the comparison implementation is container dependent
+ * and may use e.g. database comparison operations.
+ *
+ * @since 6.6
+ */
+ public static final class GreaterOrEqual extends Compare {
+ /**
+ * Construct a filter that accepts items for which the identified
+ * property value is greater than or equal to <code>value</code>.
+ *
+ * For in-memory filters, the values must implement {@link Comparable}
+ * and {@link Comparable#compareTo(Object)} is used for the comparison.
+ * For other containers, the comparison implementation is container
+ * dependent and may use e.g. database comparison operations.
+ *
+ * @param propertyId
+ * the identifier of the property whose value to compare
+ * against value
+ * @param value
+ * the value to compare against
+ */
+ public GreaterOrEqual(Object propertyId, Object value) {
+ super(propertyId, value, Operation.GREATER_OR_EQUAL);
+ }
+ }
+
+ /**
+ * A {@link Compare} filter that accepts items for which the identified
+ * property value is less than or equal to <code>value</code>.
+ *
+ * For in-memory filters, the values must implement {@link Comparable} and
+ * {@link Comparable#compareTo(Object)} is used for the comparison. For
+ * other containers, the comparison implementation is container dependent
+ * and may use e.g. database comparison operations.
+ *
+ * @since 6.6
+ */
+ public static final class LessOrEqual extends Compare {
+ /**
+ * Construct a filter that accepts items for which the identified
+ * property value is less than or equal to <code>value</code>.
+ *
+ * For in-memory filters, the values must implement {@link Comparable}
+ * and {@link Comparable#compareTo(Object)} is used for the comparison.
+ * For other containers, the comparison implementation is container
+ * dependent and may use e.g. database comparison operations.
+ *
+ * @param propertyId
+ * the identifier of the property whose value to compare
+ * against value
+ * @param value
+ * the value to compare against
+ */
+ public LessOrEqual(Object propertyId, Object value) {
+ super(propertyId, value, Operation.LESS_OR_EQUAL);
+ }
+ }
+
+ /**
+ * Constructor for a {@link Compare} filter that compares the value of an
+ * item property with the given constant <code>value</code>.
+ *
+ * This constructor is intended to be used by the nested static classes only
+ * ({@link Equal}, {@link Greater}, {@link Less}, {@link GreaterOrEqual},
+ * {@link LessOrEqual}).
+ *
+ * For in-memory filtering, comparisons except EQUAL require that the values
+ * implement {@link Comparable} and {@link Comparable#compareTo(Object)} is
+ * used for the comparison. The equality comparison is performed using
+ * {@link Object#equals(Object)}.
+ *
+ * For other containers, the comparison implementation is container
+ * dependent and may use e.g. database comparison operations. Therefore, the
+ * behavior of comparisons might differ in some cases between in-memory and
+ * other containers.
+ *
+ * @param propertyId
+ * the identifier of the property whose value to compare against
+ * value
+ * @param value
+ * the value to compare against
+ * @param operation
+ * the comparison {@link Operation} to use
+ */
+ Compare(Object propertyId, Object value, Operation operation) {
+ this.propertyId = propertyId;
+ this.value = value;
+ this.operation = operation;
+ }
+
+ public boolean passesFilter(Object itemId, Item item) {
+ final Property p = item.getItemProperty(getPropertyId());
+ if (null == p) {
+ return false;
+ }
+ Object value = p.getValue();
+ switch (getOperation()) {
+ case EQUAL:
+ return (null == this.value) ? (null == value) : this.value
+ .equals(value);
+ case GREATER:
+ return compareValue(value) > 0;
+ case LESS:
+ return compareValue(value) < 0;
+ case GREATER_OR_EQUAL:
+ return compareValue(value) >= 0;
+ case LESS_OR_EQUAL:
+ return compareValue(value) <= 0;
+ }
+ // all cases should have been processed above
+ return false;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected int compareValue(Object value1) {
+ if (null == value) {
+ return null == value1 ? 0 : -1;
+ } else if (null == value1) {
+ return 1;
+ } else if (getValue() instanceof Comparable
+ && value1.getClass().isAssignableFrom(getValue().getClass())) {
+ return -((Comparable) getValue()).compareTo(value1);
+ }
+ throw new IllegalArgumentException("Could not compare the arguments: "
+ + value1 + ", " + getValue());
+ }
+
+ public boolean appliesToProperty(Object propertyId) {
+ return getPropertyId().equals(propertyId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+
+ // Only objects of the same class can be equal
+ if (!getClass().equals(obj.getClass())) {
+ return false;
+ }
+ final Compare o = (Compare) obj;
+
+ // Checks the properties one by one
+ if (getPropertyId() != o.getPropertyId() && null != o.getPropertyId()
+ && !o.getPropertyId().equals(getPropertyId())) {
+ return false;
+ }
+ if (getOperation() != o.getOperation()) {
+ return false;
+ }
+ return (null == getValue()) ? null == o.getValue() : getValue().equals(
+ o.getValue());
+ }
+
+ @Override
+ public int hashCode() {
+ return (null != getPropertyId() ? getPropertyId().hashCode() : 0)
+ ^ (null != getValue() ? getValue().hashCode() : 0);
+ }
+
+ /**
+ * Returns the property id of the property to compare against the fixed
+ * value.
+ *
+ * @return property id
+ */
+ public Object getPropertyId() {
+ return propertyId;
+ }
+
+ /**
+ * Returns the comparison operation.
+ *
+ * @return {@link Operation}
+ */
+ public Operation getOperation() {
+ return operation;
+ }
+
+ /**
+ * Returns the value to compare the property against.
+ *
+ * @return comparison reference value
+ */
+ public Object getValue() {
+ return value;
+ }
+}
--- /dev/null
+package com.vaadin.tests.server.container.filter;
+
+import junit.framework.Assert;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.Item;
+import com.vaadin.data.util.ObjectProperty;
+import com.vaadin.data.util.PropertysetItem;
+import com.vaadin.data.util.filter.Compare.Equal;
+import com.vaadin.data.util.filter.Compare.Greater;
+import com.vaadin.data.util.filter.Compare.GreaterOrEqual;
+import com.vaadin.data.util.filter.Compare.Less;
+import com.vaadin.data.util.filter.Compare.LessOrEqual;
+
+public class CompareFilterTest extends AbstractFilterTest {
+
+ protected Item itemNull;
+ protected Item itemEmpty;
+ protected Item itemA;
+ protected Item itemB;
+ protected Item itemC;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ itemNull = new PropertysetItem();
+ itemNull.addItemProperty(PROPERTY1, new ObjectProperty<String>(null,
+ String.class));
+ itemEmpty = new PropertysetItem();
+ itemEmpty.addItemProperty(PROPERTY1, new ObjectProperty<String>("",
+ String.class));
+ itemA = new PropertysetItem();
+ itemA.addItemProperty(PROPERTY1, new ObjectProperty<String>("a",
+ String.class));
+ itemB = new PropertysetItem();
+ itemB.addItemProperty(PROPERTY1, new ObjectProperty<String>("b",
+ String.class));
+ itemC = new PropertysetItem();
+ itemC.addItemProperty(PROPERTY1, new ObjectProperty<String>("c",
+ String.class));
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ itemNull = null;
+ itemEmpty = null;
+ itemA = null;
+ itemB = null;
+ }
+
+ public void testCompareString() {
+ Filter equalB = new Equal(PROPERTY1, "b");
+ Filter greaterB = new Greater(PROPERTY1, "b");
+ Filter lessB = new Less(PROPERTY1, "b");
+ Filter greaterEqualB = new GreaterOrEqual(PROPERTY1, "b");
+ Filter lessEqualB = new LessOrEqual(PROPERTY1, "b");
+
+ Assert.assertFalse(equalB.passesFilter(null, itemA));
+ Assert.assertTrue(equalB.passesFilter(null, itemB));
+ Assert.assertFalse(equalB.passesFilter(null, itemC));
+
+ Assert.assertFalse(greaterB.passesFilter(null, itemA));
+ Assert.assertFalse(greaterB.passesFilter(null, itemB));
+ Assert.assertTrue(greaterB.passesFilter(null, itemC));
+
+ Assert.assertTrue(lessB.passesFilter(null, itemA));
+ Assert.assertFalse(lessB.passesFilter(null, itemB));
+ Assert.assertFalse(lessB.passesFilter(null, itemC));
+
+ Assert.assertFalse(greaterEqualB.passesFilter(null, itemA));
+ Assert.assertTrue(greaterEqualB.passesFilter(null, itemB));
+ Assert.assertTrue(greaterEqualB.passesFilter(null, itemC));
+
+ Assert.assertTrue(lessEqualB.passesFilter(null, itemA));
+ Assert.assertTrue(lessEqualB.passesFilter(null, itemB));
+ Assert.assertFalse(lessEqualB.passesFilter(null, itemC));
+ }
+
+ // TODO more tests: null comparisons, different datatypes...
+
+ public void testCompareEqualsHashCode() {
+ // most checks with Equal filter, then only some with others
+ Filter equalNull = new Equal(PROPERTY1, null);
+ Filter equalNull2 = new Equal(PROPERTY1, null);
+ Filter equalNullProperty2 = new Equal(PROPERTY2, null);
+ Filter equalEmpty = new Equal(PROPERTY1, "");
+ Filter equalEmpty2 = new Equal(PROPERTY1, "");
+ Filter equalEmptyProperty2 = new Equal(PROPERTY2, "");
+ Filter equalA = new Equal(PROPERTY1, "a");
+ Filter equalA2 = new Equal(PROPERTY1, "a");
+ Filter equalAProperty2 = new Equal(PROPERTY2, "a");
+ Filter equalB = new Equal(PROPERTY1, "b");
+
+ Filter greaterNull = new Greater(PROPERTY1, null);
+ Filter greaterEmpty = new Greater(PROPERTY1, "");
+
+ Filter greaterA = new Greater(PROPERTY1, "a");
+ Filter lessA = new Less(PROPERTY1, "a");
+ Filter greaterEqualA = new GreaterOrEqual(PROPERTY1, "a");
+ Filter lessEqualA = new LessOrEqual(PROPERTY1, "a");
+
+ // equals()
+ Assert.assertEquals(equalNull, equalNull);
+ Assert.assertEquals(equalNull, equalNull2);
+ Assert.assertFalse(equalNull.equals(equalNullProperty2));
+ Assert.assertFalse(equalNull.equals(equalEmpty));
+ Assert.assertFalse(equalNull.equals(equalA));
+
+ Assert.assertEquals(equalEmpty, equalEmpty);
+ Assert.assertFalse(equalEmpty.equals(equalNull));
+ Assert.assertEquals(equalEmpty, equalEmpty2);
+ Assert.assertFalse(equalEmpty.equals(equalEmptyProperty2));
+ Assert.assertFalse(equalEmpty.equals(equalA));
+
+ Assert.assertEquals(equalA, equalA);
+ Assert.assertFalse(equalA.equals(equalNull));
+ Assert.assertFalse(equalA.equals(equalEmpty));
+ Assert.assertEquals(equalA, equalA2);
+ Assert.assertFalse(equalA.equals(equalAProperty2));
+ Assert.assertFalse(equalA.equals(equalB));
+
+ Assert.assertEquals(greaterA, greaterA);
+ Assert.assertFalse(greaterA.equals(lessA));
+ Assert.assertFalse(greaterA.equals(greaterEqualA));
+ Assert.assertFalse(greaterA.equals(lessEqualA));
+
+ Assert.assertFalse(greaterNull.equals(greaterEmpty));
+ Assert.assertFalse(greaterNull.equals(greaterA));
+ Assert.assertFalse(greaterEmpty.equals(greaterNull));
+ Assert.assertFalse(greaterEmpty.equals(greaterA));
+ Assert.assertFalse(greaterA.equals(greaterNull));
+ Assert.assertFalse(greaterA.equals(greaterEmpty));
+
+ // hashCode()
+ Assert.assertEquals(equalNull.hashCode(), equalNull2.hashCode());
+ Assert.assertEquals(equalEmpty.hashCode(), equalEmpty2.hashCode());
+ Assert.assertEquals(equalA.hashCode(), equalA2.hashCode());
+ }
+}