From a71be1bae382c212666a2c0f037d1444c1fb0e9f Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Tue, 21 Oct 2008 01:19:46 +0000 Subject: [PATCH] Add RowFilter contributed by Patricia Donaldson git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@378 f203690c-595d-4dc9-a70b-905162fa7fd2 --- CREDITS.txt | 1 + src/changes/changes.xml | 5 + .../jackcess/RowFilter.java | 203 ++++++++++++++++++ .../jackcess/BigIndexTest.java | 4 - .../jackcess/IndexTest.java | 4 - .../jackcess/RowFilterTest.java | 112 ++++++++++ 6 files changed, 321 insertions(+), 8 deletions(-) create mode 100644 src/java/com/healthmarketscience/jackcess/RowFilter.java create mode 100644 test/src/java/com/healthmarketscience/jackcess/RowFilterTest.java diff --git a/CREDITS.txt b/CREDITS.txt index c77b80e..78b627b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -4,3 +4,4 @@ Mitchell J. Friedman - Added support for additional JDBC data types James Ahlborn - Added support for NUMERIC data type Jon Iles - Added support for reading table definitions that span multiple pages James Schopp - added support for reading currency columns +Patricia Donaldson - contributed RowFilter class diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 332abbc..8b9ed1f 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -5,6 +5,11 @@ James Ahlborn + + + Add RowFilter contributed by Patricia Donaldson. + + Fix simple index handling of tail index pages. diff --git a/src/java/com/healthmarketscience/jackcess/RowFilter.java b/src/java/com/healthmarketscience/jackcess/RowFilter.java new file mode 100644 index 0000000..c498273 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/RowFilter.java @@ -0,0 +1,203 @@ +/* +Copyright (c) 2007 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess; + +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.lang.ObjectUtils; + + +/** + * The RowFilter class encapsulates a filter test for a table row. This can + * be used by the {@link #apply(Iterable)} method to create an Iterable over a + * table which returns only rows matching some criteria. + * + * @author Patricia Donaldson, Xerox Corporation + */ +public abstract class RowFilter +{ + + /** + * Returns {@code true} if the given table row matches the Filter criteria, + * {@code false} otherwise. + * @param row current row to test for inclusion in the filter + */ + public abstract boolean matches(Map row); + + /** + * Returns an iterable which filters the given iterable based on this + * filter. + * + * @param iterable row iterable to filter + * + * @return a filtering iterable + */ + public Iterable> apply( + Iterable> iterable) + { + return new FilterIterable(iterable); + } + + + /** + * Creates a filter based on a row pattern. + * + * @param rowPattern Map from column names to the values to be matched. + * A table row will match the target if + * {@code ObjectUtil.equals(rowPattern.get(s), row.get(s))} + * for all column names in the pattern map. + * @return a filter which matches table rows which match the values in the + * row pattern + */ + public static RowFilter matchPattern(final Map rowPattern) + { + return new RowFilter() { + @Override + public boolean matches(Map row) + { + for(Map.Entry e : rowPattern.entrySet()) { + if(!ObjectUtils.equals(e.getValue(), row.get(e.getKey()))) { + return false; + } + } + return true; + } + }; + } + + /** + * Creates a filter based on a single value row pattern. + * + * @param columnPattern column to be matched + * @param valuePattern value to be matched. + * A table row will match the target if + * {@code ObjectUtil.equals(valuePattern, row.get(columnPattern.getName()))}. + * @return a filter which matches table rows which match the value in the + * row pattern + */ + public static RowFilter matchPattern(final Column columnPattern, final Object valuePattern) + { + return new RowFilter() { + @Override + public boolean matches(Map row) + { + return ObjectUtils.equals(valuePattern, row.get(columnPattern.getName())); + } + }; + } + + /** + * Creates a filter which inverts the sense of the given filter (rows which + * are matched by the given filter will not be matched by the returned + * filter, and vice versa). + * + * @param filter filter which to invert + * + * @return a RowFilter which matches rows not matched by the given filter + */ + public static RowFilter invert(final RowFilter filter) + { + return new RowFilter() { + @Override + public boolean matches(Map row) + { + return !filter.matches(row); + } + }; + } + + + /** + * Returns an iterable which filters the given iterable based on the given + * rowFilter. + * + * @param rowFilter the filter criteria, may be {@code null} + * @param iterable row iterable to filter + * + * @return a filtering iterable (or the given iterable if a {@code null} + * filter was given) + */ + public static Iterable> apply( + RowFilter rowFilter, + Iterable> iterable) + { + return((rowFilter != null) ? rowFilter.apply(iterable) : iterable); + } + + + /** + * Iterable which creates a filtered view of a another row iterable. + */ + private class FilterIterable implements Iterable> + { + private final Iterable> _iterable; + + private FilterIterable(Iterable> iterable) + { + _iterable = iterable; + } + + + /** + * Returns an iterator which iterates through the rows of the underlying + * iterable, returning only rows for which the {@link RowFilter#matches} + * method returns {@code true} + */ + public Iterator> iterator() + { + return new Iterator>() { + private final Iterator> _iter = + _iterable.iterator(); + private Map _next; + + public boolean hasNext() { + while(_iter.hasNext()) { + _next = _iter.next(); + if(RowFilter.this.matches(_next)) { + return true; + } + } + _next = null; + return false; + } + + public Map next() { + return _next; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + }; + } + + } + +} diff --git a/test/src/java/com/healthmarketscience/jackcess/BigIndexTest.java b/test/src/java/com/healthmarketscience/jackcess/BigIndexTest.java index 628fc05..212bae8 100644 --- a/test/src/java/com/healthmarketscience/jackcess/BigIndexTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/BigIndexTest.java @@ -45,10 +45,6 @@ public class BigIndexTest extends TestCase { private String _oldBigIndexValue = null; - /** - * Creates a new IndexTest instance. - * - */ public BigIndexTest(String name) { super(name); } diff --git a/test/src/java/com/healthmarketscience/jackcess/IndexTest.java b/test/src/java/com/healthmarketscience/jackcess/IndexTest.java index 351e2d4..d746844 100644 --- a/test/src/java/com/healthmarketscience/jackcess/IndexTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/IndexTest.java @@ -46,10 +46,6 @@ import static com.healthmarketscience.jackcess.DatabaseTest.*; */ public class IndexTest extends TestCase { - /** - * Creates a new IndexTest instance. - * - */ public IndexTest(String name) { super(name); } diff --git a/test/src/java/com/healthmarketscience/jackcess/RowFilterTest.java b/test/src/java/com/healthmarketscience/jackcess/RowFilterTest.java new file mode 100644 index 0000000..8d9b510 --- /dev/null +++ b/test/src/java/com/healthmarketscience/jackcess/RowFilterTest.java @@ -0,0 +1,112 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import static com.healthmarketscience.jackcess.DatabaseTest.*; + +/** + * @author James Ahlborn + */ +public class RowFilterTest extends TestCase +{ + private static final String ID_COL = "id"; + private static final String COL1 = "col1"; + private static final String COL2 = "col2"; + private static final String COL3 = "col3"; + + + public RowFilterTest(String name) { + super(name); + } + + @SuppressWarnings("unchecked") + public void testFilter() throws Exception + { + Map row0 = createExpectedRow(ID_COL, 0, COL1, "foo", COL2, 13, COL3, "bar"); + Map row1 = createExpectedRow(ID_COL, 1, COL1, "bar", COL2, 42, COL3, null); + Map row2 = createExpectedRow(ID_COL, 2, COL1, "foo", COL2, 55, COL3, "bar"); + Map row3 = createExpectedRow(ID_COL, 3, COL1, "baz", COL2, 42, COL3, "bar"); + Map row4 = createExpectedRow(ID_COL, 4, COL1, "foo", COL2, 13, COL3, null); + Map row5 = createExpectedRow(ID_COL, 5, COL1, "bla", COL2, 13, COL3, "bar"); + + + List> rows = Arrays.asList(row0, row1, row2, row3, row4, row5); + + assertEquals(Arrays.asList(row0, row2, row4), + toList(RowFilter.matchPattern( + new ColumnBuilder(COL1, DataType.TEXT).toColumn(), + "foo").apply(rows))); + assertEquals(Arrays.asList(row1, row3, row5), + toList(RowFilter.invert( + RowFilter.matchPattern( + new ColumnBuilder(COL1, DataType.TEXT).toColumn(), + "foo")).apply(rows))); + + assertEquals(Arrays.asList(row0, row2, row4), + toList(RowFilter.matchPattern( + createExpectedRow(COL1, "foo")) + .apply(rows))); + assertEquals(Arrays.asList(row0, row2), + toList(RowFilter.matchPattern( + createExpectedRow(COL1, "foo", COL3, "bar")) + .apply(rows))); + assertEquals(Arrays.asList(row4), + toList(RowFilter.matchPattern( + createExpectedRow(COL1, "foo", COL3, null)) + .apply(rows))); + assertEquals(Arrays.asList(row0, row4, row5), + toList(RowFilter.matchPattern( + createExpectedRow(COL2, 13)) + .apply(rows))); + assertEquals(Arrays.asList(row1), + toList(RowFilter.matchPattern(row1) + .apply(rows))); + + assertEquals(rows, toList(RowFilter.apply(null, rows))); + assertEquals(Arrays.asList(row1), + toList(RowFilter.apply(RowFilter.matchPattern(row1), + rows))); + } + + private List> toList(Iterable> rows) + { + List> rowList = new ArrayList>(); + for(Map row : rows) { + rowList.add(row); + } + return rowList; + } + +} -- 2.39.5