From f11a23b57b25aa48833bc56b3c48c1dac8d4c571 Mon Sep 17 00:00:00 2001 From: Henri Sara Date: Thu, 10 Mar 2011 15:32:10 +0000 Subject: [PATCH] #6286 Container filtering improvements: logical And and Or filters svn changeset:17714/svn branch:6.6 --- .../util/filter/AbstractJunctionFilter.java | 72 ++++++ src/com/vaadin/data/util/filter/And.java | 39 +++ src/com/vaadin/data/util/filter/Or.java | 58 +++++ .../container/filter/AbstractFilterTest.java | 42 ++++ .../container/filter/AndOrFilterTest.java | 234 ++++++++++++++++++ 5 files changed, 445 insertions(+) create mode 100644 src/com/vaadin/data/util/filter/AbstractJunctionFilter.java create mode 100644 src/com/vaadin/data/util/filter/And.java create mode 100644 src/com/vaadin/data/util/filter/Or.java create mode 100644 tests/src/com/vaadin/tests/server/container/filter/AndOrFilterTest.java diff --git a/src/com/vaadin/data/util/filter/AbstractJunctionFilter.java b/src/com/vaadin/data/util/filter/AbstractJunctionFilter.java new file mode 100644 index 0000000000..f6464763c4 --- /dev/null +++ b/src/com/vaadin/data/util/filter/AbstractJunctionFilter.java @@ -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 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 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 index 0000000000..ec1a2bf8c6 --- /dev/null +++ b/src/com/vaadin/data/util/filter/And.java @@ -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 index 0000000000..c4a3684e48 --- /dev/null +++ b/src/com/vaadin/data/util/filter/Or.java @@ -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); + } + } + +} diff --git a/tests/src/com/vaadin/tests/server/container/filter/AbstractFilterTest.java b/tests/src/com/vaadin/tests/server/container/filter/AbstractFilterTest.java index 0438ee4dde..4075b87975 100644 --- a/tests/src/com/vaadin/tests/server/container/filter/AbstractFilterTest.java +++ b/tests/src/com/vaadin/tests/server/container/filter/AbstractFilterTest.java @@ -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 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 index 0000000000..94c8862dbb --- /dev/null +++ b/tests/src/com/vaadin/tests/server/container/filter/AndOrFilterTest.java @@ -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(1); + protected Item item2 = new BeanItem(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")); + } + +} -- 2.39.5