]> source.dussan.org Git - vaadin-framework.git/commitdiff
#6286 Container filtering improvements: logical And and Or filters
authorHenri Sara <henri.sara@itmill.com>
Thu, 10 Mar 2011 15:32:10 +0000 (15:32 +0000)
committerHenri Sara <henri.sara@itmill.com>
Thu, 10 Mar 2011 15:32:10 +0000 (15:32 +0000)
svn changeset:17714/svn branch:6.6

src/com/vaadin/data/util/filter/AbstractJunctionFilter.java [new file with mode: 0644]
src/com/vaadin/data/util/filter/And.java [new file with mode: 0644]
src/com/vaadin/data/util/filter/Or.java [new file with mode: 0644]
tests/src/com/vaadin/tests/server/container/filter/AbstractFilterTest.java
tests/src/com/vaadin/tests/server/container/filter/AndOrFilterTest.java [new file with mode: 0644]

diff --git a/src/com/vaadin/data/util/filter/AbstractJunctionFilter.java b/src/com/vaadin/data/util/filter/AbstractJunctionFilter.java
new file mode 100644 (file)
index 0000000..f646476
--- /dev/null
@@ -0,0 +1,72 @@
+package com.vaadin.data.util.filter;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+import com.vaadin.data.Container.Filter;
+
+/**
+ * Abstract base class for filters that are composed of multiple sub-filters.
+ * 
+ * The method {@link #appliesToProperty(Object)} is provided to help
+ * implementing {@link Filter} for in-memory filters.
+ * 
+ * @since 6.6
+ */
+public abstract class AbstractJunctionFilter implements Filter {
+
+    protected final Collection<Filter> filters;
+
+    public AbstractJunctionFilter(Filter... filters) {
+        this.filters = Collections.unmodifiableCollection(Arrays
+                .asList(filters));
+    }
+
+    /**
+     * Returns an unmodifiable collection of the sub-filters of this composite
+     * filter.
+     * 
+     * @return
+     */
+    public Collection<Filter> getFilters() {
+        return filters;
+    }
+
+    /**
+     * Returns true if a change in the named property may affect the filtering
+     * result. If some of the sub-filters are not in-memory filters, true is
+     * returned.
+     * 
+     * By default, all sub-filters are iterated to check if any of them applies.
+     * If there are no sub-filters, false is returned - override in subclasses
+     * to change this behavior.
+     */
+    public boolean appliesToProperty(Object propertyId) {
+        for (Filter filter : getFilters()) {
+            if (filter.appliesToProperty(propertyId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !getClass().equals(obj.getClass())) {
+            return false;
+        }
+        AbstractJunctionFilter other = (AbstractJunctionFilter) obj;
+        // contents comparison with equals()
+        return Arrays.equals(filters.toArray(), other.filters.toArray());
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = getFilters().size();
+        for (Filter filter : filters) {
+            hash = (hash << 1) ^ filter.hashCode();
+        }
+        return hash;
+    }
+}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/filter/And.java b/src/com/vaadin/data/util/filter/And.java
new file mode 100644 (file)
index 0000000..ec1a2bf
--- /dev/null
@@ -0,0 +1,39 @@
+package com.vaadin.data.util.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.Item;
+
+/**
+ * A compound {@link Filter} that accepts an item if all of its filters accept
+ * the item.
+ * 
+ * If no filters are given, the filter should accept all items.
+ * 
+ * This filter also directly supports in-memory filtering when all sub-filters
+ * do so.
+ * 
+ * @see Or
+ * 
+ * @since 6.6
+ */
+public class And extends AbstractJunctionFilter implements Filter {
+
+    /**
+     * 
+     * @param filters
+     *            filters of which the And filter will be composed
+     */
+    public And(Filter... filters) {
+        super(filters);
+    }
+
+    public boolean passesFilter(Item item) throws UnsupportedFilterException {
+        for (Filter filter : getFilters()) {
+            if (!filter.passesFilter(item)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/src/com/vaadin/data/util/filter/Or.java b/src/com/vaadin/data/util/filter/Or.java
new file mode 100644 (file)
index 0000000..c4a3684
--- /dev/null
@@ -0,0 +1,58 @@
+package com.vaadin.data.util.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.Item;
+
+/**
+ * A compound {@link Filter} that accepts an item if any of its filters accept
+ * the item.
+ * 
+ * If no filters are given, the filter should reject all items.
+ * 
+ * This filter also directly supports in-memory filtering when all sub-filters
+ * do so.
+ * 
+ * @see And
+ * 
+ * @since 6.6
+ */
+public class Or extends AbstractJunctionFilter implements Filter {
+
+    /**
+     * 
+     * @param filters
+     *            filters of which the Or filter will be composed
+     */
+    public Or(Filter... filters) {
+        super(filters);
+    }
+
+    public boolean passesFilter(Item item) throws UnsupportedFilterException {
+        for (Filter filter : getFilters()) {
+            if (filter.passesFilter(item)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if a change in the named property may affect the filtering
+     * result. If some of the sub-filters are not in-memory filters, true is
+     * returned.
+     * 
+     * By default, all sub-filters are iterated to check if any of them applies.
+     * If there are no sub-filters, true is returned as an empty Or rejects all
+     * items.
+     */
+    @Override
+    public boolean appliesToProperty(Object propertyId) {
+        if (getFilters().isEmpty()) {
+            // empty Or filters out everything
+            return true;
+        } else {
+            return super.appliesToProperty(propertyId);
+        }
+    }
+
+}
index 0438ee4ddee73534d7decc78f1bef805d346c783..4075b879759842cd9850018bbe9070392e1f11a2 100644 (file)
@@ -3,6 +3,7 @@ package com.vaadin.tests.server.container.filter;
 import junit.framework.TestCase;
 
 import com.vaadin.data.Container.Filter;
+import com.vaadin.data.Item;
 import com.vaadin.data.Property;
 import com.vaadin.data.util.ObjectProperty;
 import com.vaadin.data.util.PropertysetItem;
@@ -46,4 +47,45 @@ public abstract class AbstractFilterTest<FILTERTYPE extends Filter> extends
 
     }
 
+    public static class SameItemFilter implements Filter {
+
+        private final Item item;
+        private final Object propertyId;
+
+        public SameItemFilter(Item item) {
+            this(item, "");
+        }
+
+        public SameItemFilter(Item item, Object propertyId) {
+            this.item = item;
+            this.propertyId = propertyId;
+        }
+
+        public boolean passesFilter(Item item)
+                throws UnsupportedOperationException {
+            return this.item == item;
+        }
+
+        public boolean appliesToProperty(Object propertyId) {
+            return this.propertyId != null ? this.propertyId.equals(propertyId)
+                    : true;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null || !getClass().equals(obj.getClass())) {
+                return false;
+            }
+            SameItemFilter other = (SameItemFilter) obj;
+            return item == other.item
+                    && (propertyId == null ? other.propertyId == null
+                            : propertyId.equals(other.propertyId));
+        }
+
+        @Override
+        public int hashCode() {
+            return item.hashCode();
+        }
+    }
+
 }
diff --git a/tests/src/com/vaadin/tests/server/container/filter/AndOrFilterTest.java b/tests/src/com/vaadin/tests/server/container/filter/AndOrFilterTest.java
new file mode 100644 (file)
index 0000000..94c8862
--- /dev/null
@@ -0,0 +1,234 @@
+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.BeanItem;
+import com.vaadin.data.util.filter.And;
+import com.vaadin.data.util.filter.Or;
+
+public class AndOrFilterTest extends AbstractFilterTest {
+
+    protected Item item1 = new BeanItem<Integer>(1);
+    protected Item item2 = new BeanItem<Integer>(2);
+
+    public void testNoFilterAnd() {
+        Filter filter = new And();
+
+        Assert.assertTrue(filter.passesFilter(item1));
+    }
+
+    public void testSingleFilterAnd() {
+        Filter filter = new And(new SameItemFilter(item1));
+
+        Assert.assertTrue(filter.passesFilter(item1));
+        Assert.assertFalse(filter.passesFilter(item2));
+    }
+
+    public void testTwoFilterAnd() {
+        Filter filter1 = new And(new SameItemFilter(item1), new SameItemFilter(
+                item1));
+        Filter filter2 = new And(new SameItemFilter(item1), new SameItemFilter(
+                item2));
+
+        Assert.assertTrue(filter1.passesFilter(item1));
+        Assert.assertFalse(filter1.passesFilter(item2));
+
+        Assert.assertFalse(filter2.passesFilter(item1));
+        Assert.assertFalse(filter2.passesFilter(item2));
+    }
+
+    public void testThreeFilterAnd() {
+        Filter filter1 = new And(new SameItemFilter(item1), new SameItemFilter(
+                item1), new SameItemFilter(item1));
+        Filter filter2 = new And(new SameItemFilter(item1), new SameItemFilter(
+                item1), new SameItemFilter(item2));
+
+        Assert.assertTrue(filter1.passesFilter(item1));
+        Assert.assertFalse(filter1.passesFilter(item2));
+
+        Assert.assertFalse(filter2.passesFilter(item1));
+        Assert.assertFalse(filter2.passesFilter(item2));
+    }
+
+    public void testNoFilterOr() {
+        Filter filter = new Or();
+
+        Assert.assertFalse(filter.passesFilter(item1));
+    }
+
+    public void testSingleFilterOr() {
+        Filter filter = new Or(new SameItemFilter(item1));
+
+        Assert.assertTrue(filter.passesFilter(item1));
+        Assert.assertFalse(filter.passesFilter(item2));
+    }
+
+    public void testTwoFilterOr() {
+        Filter filter1 = new Or(new SameItemFilter(item1), new SameItemFilter(
+                item1));
+        Filter filter2 = new Or(new SameItemFilter(item1), new SameItemFilter(
+                item2));
+
+        Assert.assertTrue(filter1.passesFilter(item1));
+        Assert.assertFalse(filter1.passesFilter(item2));
+
+        Assert.assertTrue(filter2.passesFilter(item1));
+        Assert.assertTrue(filter2.passesFilter(item2));
+    }
+
+    public void testThreeFilterOr() {
+        Filter filter1 = new Or(new SameItemFilter(item1), new SameItemFilter(
+                item1), new SameItemFilter(item1));
+        Filter filter2 = new Or(new SameItemFilter(item1), new SameItemFilter(
+                item1), new SameItemFilter(item2));
+
+        Assert.assertTrue(filter1.passesFilter(item1));
+        Assert.assertFalse(filter1.passesFilter(item2));
+
+        Assert.assertTrue(filter2.passesFilter(item1));
+        Assert.assertTrue(filter2.passesFilter(item2));
+    }
+
+    public void testAndEqualsHashCode() {
+        Filter filter0 = new And();
+        Filter filter0b = new And();
+        Filter filter1a = new And(new SameItemFilter(item1));
+        Filter filter1a2 = new And(new SameItemFilter(item1));
+        Filter filter1b = new And(new SameItemFilter(item2));
+        Filter filter2a = new And(new SameItemFilter(item1),
+                new SameItemFilter(item1));
+        Filter filter2b = new And(new SameItemFilter(item1),
+                new SameItemFilter(item2));
+        Filter filter2b2 = new And(new SameItemFilter(item1),
+                new SameItemFilter(item2));
+        Filter other0 = new Or();
+        Filter other1 = new Or(new SameItemFilter(item1));
+
+        Assert.assertEquals(filter0, filter0);
+        Assert.assertEquals(filter0, filter0b);
+        Assert.assertFalse(filter0.equals(filter1a));
+        Assert.assertFalse(filter0.equals(other0));
+        Assert.assertFalse(filter0.equals(other1));
+
+        Assert.assertFalse(filter1a.equals(filter1b));
+        Assert.assertFalse(filter1a.equals(other1));
+
+        Assert.assertFalse(filter1a.equals(filter2a));
+        Assert.assertFalse(filter2a.equals(filter1a));
+
+        Assert.assertFalse(filter2a.equals(filter2b));
+        Assert.assertEquals(filter2b, filter2b2);
+
+        // hashCode()
+        Assert.assertEquals(filter0.hashCode(), filter0.hashCode());
+        Assert.assertEquals(filter0.hashCode(), filter0b.hashCode());
+        Assert.assertEquals(filter1a.hashCode(), filter1a.hashCode());
+        Assert.assertEquals(filter1a.hashCode(), filter1a2.hashCode());
+        Assert.assertEquals(filter2a.hashCode(), filter2a.hashCode());
+        Assert.assertEquals(filter2b.hashCode(), filter2b2.hashCode());
+    }
+
+    public void testOrEqualsHashCode() {
+        Filter filter0 = new Or();
+        Filter filter0b = new Or();
+        Filter filter1a = new Or(new SameItemFilter(item1));
+        Filter filter1a2 = new Or(new SameItemFilter(item1));
+        Filter filter1b = new Or(new SameItemFilter(item2));
+        Filter filter2a = new Or(new SameItemFilter(item1), new SameItemFilter(
+                item1));
+        Filter filter2b = new Or(new SameItemFilter(item1), new SameItemFilter(
+                item2));
+        Filter filter2b2 = new Or(new SameItemFilter(item1),
+                new SameItemFilter(item2));
+        Filter other0 = new And();
+        Filter other1 = new And(new SameItemFilter(item1));
+
+        Assert.assertEquals(filter0, filter0);
+        Assert.assertEquals(filter0, filter0b);
+        Assert.assertFalse(filter0.equals(filter1a));
+        Assert.assertFalse(filter0.equals(other0));
+        Assert.assertFalse(filter0.equals(other1));
+
+        Assert.assertFalse(filter1a.equals(filter1b));
+        Assert.assertFalse(filter1a.equals(other1));
+
+        Assert.assertFalse(filter1a.equals(filter2a));
+        Assert.assertFalse(filter2a.equals(filter1a));
+
+        Assert.assertFalse(filter2a.equals(filter2b));
+        Assert.assertEquals(filter2b, filter2b2);
+
+        // hashCode()
+        Assert.assertEquals(filter0.hashCode(), filter0.hashCode());
+        Assert.assertEquals(filter0.hashCode(), filter0b.hashCode());
+        Assert.assertEquals(filter1a.hashCode(), filter1a.hashCode());
+        Assert.assertEquals(filter1a.hashCode(), filter1a2.hashCode());
+        Assert.assertEquals(filter2a.hashCode(), filter2a.hashCode());
+        Assert.assertEquals(filter2b.hashCode(), filter2b2.hashCode());
+    }
+
+    public void testAndAppliesToProperty() {
+        Filter filter0 = new And();
+        Filter filter1a = new And(new SameItemFilter(item1, "a"));
+        Filter filter1b = new And(new SameItemFilter(item1, "b"));
+        Filter filter2aa = new And(new SameItemFilter(item1, "a"),
+                new SameItemFilter(item1, "a"));
+        Filter filter2ab = new And(new SameItemFilter(item1, "a"),
+                new SameItemFilter(item1, "b"));
+        Filter filter3abc = new And(new SameItemFilter(item1, "a"),
+                new SameItemFilter(item1, "b"), new SameItemFilter(item1, "c"));
+
+        // empty And does not filter out anything
+        Assert.assertFalse(filter0.appliesToProperty("a"));
+        Assert.assertFalse(filter0.appliesToProperty("d"));
+
+        Assert.assertTrue(filter1a.appliesToProperty("a"));
+        Assert.assertFalse(filter1a.appliesToProperty("b"));
+        Assert.assertFalse(filter1b.appliesToProperty("a"));
+        Assert.assertTrue(filter1b.appliesToProperty("b"));
+
+        Assert.assertTrue(filter2aa.appliesToProperty("a"));
+        Assert.assertFalse(filter2aa.appliesToProperty("b"));
+        Assert.assertTrue(filter2ab.appliesToProperty("a"));
+        Assert.assertTrue(filter2ab.appliesToProperty("b"));
+
+        Assert.assertTrue(filter3abc.appliesToProperty("a"));
+        Assert.assertTrue(filter3abc.appliesToProperty("b"));
+        Assert.assertTrue(filter3abc.appliesToProperty("c"));
+        Assert.assertFalse(filter3abc.appliesToProperty("d"));
+    }
+
+    public void testOrAppliesToProperty() {
+        Filter filter0 = new Or();
+        Filter filter1a = new Or(new SameItemFilter(item1, "a"));
+        Filter filter1b = new Or(new SameItemFilter(item1, "b"));
+        Filter filter2aa = new Or(new SameItemFilter(item1, "a"),
+                new SameItemFilter(item1, "a"));
+        Filter filter2ab = new Or(new SameItemFilter(item1, "a"),
+                new SameItemFilter(item1, "b"));
+        Filter filter3abc = new Or(new SameItemFilter(item1, "a"),
+                new SameItemFilter(item1, "b"), new SameItemFilter(item1, "c"));
+
+        // empty Or filters out everything
+        Assert.assertTrue(filter0.appliesToProperty("a"));
+        Assert.assertTrue(filter0.appliesToProperty("d"));
+
+        Assert.assertTrue(filter1a.appliesToProperty("a"));
+        Assert.assertFalse(filter1a.appliesToProperty("b"));
+        Assert.assertFalse(filter1b.appliesToProperty("a"));
+        Assert.assertTrue(filter1b.appliesToProperty("b"));
+
+        Assert.assertTrue(filter2aa.appliesToProperty("a"));
+        Assert.assertFalse(filter2aa.appliesToProperty("b"));
+        Assert.assertTrue(filter2ab.appliesToProperty("a"));
+        Assert.assertTrue(filter2ab.appliesToProperty("b"));
+
+        Assert.assertTrue(filter3abc.appliesToProperty("a"));
+        Assert.assertTrue(filter3abc.appliesToProperty("b"));
+        Assert.assertTrue(filter3abc.appliesToProperty("c"));
+        Assert.assertFalse(filter3abc.appliesToProperty("d"));
+    }
+
+}