<action dev="jahlborn" type="update" issue="3097387">
Allow column order in tables to be configured.
</action>
+ <action dev="jahlborn" type="update" issue="3105829">
+ Add support for custom column value matching when finding rows using a
+ Cursor.
+ </action>
</release>
<release version="1.2.1" date="2010-08-01">
<action dev="jahlborn" type="add" issue="3005272">
--- /dev/null
+/*
+Copyright (c) 2010 James Ahlborn
+
+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
+
+*/
+
+package com.healthmarketscience.jackcess;
+
+import java.io.IOException;
+
+/**
+ * Concrete implementation of ColumnMatcher which tests textual columns
+ * case-insensitively ({@link DataType#TEXT} and {@link DataType#MEMO}), and
+ * all other columns using simple equality.
+ *
+ * @author James Ahlborn
+ */
+public class CaseInsensitiveColumnMatcher implements ColumnMatcher {
+
+ public static final CaseInsensitiveColumnMatcher INSTANCE =
+ new CaseInsensitiveColumnMatcher();
+
+
+ public CaseInsensitiveColumnMatcher() {
+ }
+
+ public boolean matches(Table table, String columnName, Object value1,
+ Object value2)
+ {
+ if(!isTextual(table.getColumn(columnName))) {
+ // use simple equality
+ return SimpleColumnMatcher.INSTANCE.matches(table, columnName,
+ value1, value2);
+ }
+
+ // convert both values to Strings and compare case-insensitively
+ try {
+ CharSequence cs1 = Column.toCharSequence(value1);
+ CharSequence cs2 = Column.toCharSequence(value2);
+
+ return((cs1 == cs2) ||
+ ((cs1 != null) && (cs2 != null) &&
+ cs1.toString().equalsIgnoreCase(cs2.toString())));
+ } catch(IOException e) {
+ throw new IllegalStateException("Could not read column " + columnName
+ + " value", e);
+ }
+ }
+
+ private static boolean isTextual(Column col)
+ {
+ DataType type = col.getType();
+ return((type == DataType.TEXT) || (type == DataType.MEMO));
+ }
+
+}
--- /dev/null
+/*
+Copyright (c) 2010 James Ahlborn
+
+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
+
+*/
+
+package com.healthmarketscience.jackcess;
+
+/**
+ * Interface for handling comparisons between column values.
+ *
+ * @author James Ahlborn
+ */
+public interface ColumnMatcher
+{
+
+ /**
+ * Returns {@code true} if the given value1 should be considered a match for
+ * the given value2 for the given column in the given table, {@code false}
+ * otherwise.
+ *
+ * @param table the relevant table
+ * @param columnName the name of the relevant column within the table
+ * @param value1 the first value to match (may be {@code null})
+ * @param value2 the second value to match (may be {@code null})
+ */
+ public boolean matches(Table table, String columnName, Object value1,
+ Object value2);
+}
private Position _prevPos;
/** the current row */
private Position _curPos;
-
+ /** ColumnMatcher to be used when matching column values */
+ private ColumnMatcher _columnMatcher = SimpleColumnMatcher.INSTANCE;
protected Cursor(Id id, Table table, Position firstPos, Position lastPos) {
_id = id;
_rowState.setErrorHandler(newErrorHandler);
}
+ /**
+ * Returns the currently configured ColumnMatcher, always non-{@code null}.
+ */
+ public ColumnMatcher getColumnMatcher() {
+ return _columnMatcher;
+ }
+
+ /**
+ * Sets a new ColumnMatcher. If {@code null}, resets to using the
+ * default matcher, {@link SimpleColumnMatcher#INSTANCE}.
+ */
+ public void setColumnMatcher(ColumnMatcher columnMatcher) {
+ if(columnMatcher == null) {
+ columnMatcher = SimpleColumnMatcher.INSTANCE;
+ }
+ _columnMatcher = columnMatcher;
+ }
+
/**
* Returns the current state of the cursor which can be restored at a future
* point in time by a call to {@link #restoreSavepoint}.
public boolean currentRowMatches(Column columnPattern, Object valuePattern)
throws IOException
{
- return ObjectUtils.equals(valuePattern, getCurrentRowValue(columnPattern));
+ return _columnMatcher.matches(getTable(), columnPattern.getName(),
+ valuePattern,
+ getCurrentRowValue(columnPattern));
}
/**
public boolean currentRowMatches(Map<String,Object> rowPattern)
throws IOException
{
- return ObjectUtils.equals(rowPattern, getCurrentRow(rowPattern.keySet()));
+ Map<String,Object> row = getCurrentRow(rowPattern.keySet());
+
+ if(rowPattern.size() != row.size()) {
+ return false;
+ }
+
+ for(Map.Entry<String,Object> e : row.entrySet()) {
+ String columnName = e.getKey();
+ if(!_columnMatcher.matches(getTable(), columnName,
+ rowPattern.get(columnName), e.getValue())) {
+ return false;
+ }
+ }
+
+ return true;
}
/**
private boolean _beforeFirst = true;
/** optional save point to restore to the cursor */
private Cursor.Savepoint _savepoint;
+ /** ColumnMatcher to be used when matching column values */
+ private ColumnMatcher _columnMatcher;
public CursorBuilder(Table table) {
_table = table;
return this;
}
+ /**
+ * Sets the ColumnMatcher to use for matching row patterns.
+ */
+ public CursorBuilder setColumnMatcher(ColumnMatcher columnMatcher) {
+ _columnMatcher = columnMatcher;
+ return this;
+ }
+
/**
* Returns a new cursor for the table, constructed to the given
* specifications.
_startRow, _startRowInclusive,
_endRow, _endRowInclusive);
}
+ cursor.setColumnMatcher(_columnMatcher);
if(_savepoint == null) {
if(!_beforeFirst) {
cursor.afterLast();
--- /dev/null
+/*
+Copyright (c) 2010 James Ahlborn
+
+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
+
+*/
+
+package com.healthmarketscience.jackcess;
+
+import org.apache.commons.lang.ObjectUtils;
+
+/**
+ * Simple concrete implementation of ColumnMatcher which test for equality.
+ *
+ * @author James Ahlborn
+ */
+public class SimpleColumnMatcher implements ColumnMatcher {
+
+ public static final SimpleColumnMatcher INSTANCE = new SimpleColumnMatcher();
+
+ public SimpleColumnMatcher() {
+ }
+
+ public boolean matches(Table table, String columnName, Object value1,
+ Object value2)
+ {
+ return ObjectUtils.equals(value1, value2);
+ }
+}
return expectedRows;
}
- private static Database createTestTable(final FileFormat fileFormat) throws Exception {
+ private static Database createTestTable(final FileFormat fileFormat)
+ throws Exception
+ {
Database db = create(fileFormat);
Table table = new TableBuilder("test")
}
}
+ public void testColmnMatcher() throws Exception {
+
+
+ for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
+ Database db = createTestTable(fileFormat);
+
+ Table table = db.getTable("test");
+
+ doTestMatchers(table, SimpleColumnMatcher.INSTANCE, false);
+ doTestMatchers(table, CaseInsensitiveColumnMatcher.INSTANCE, true);
+
+ Cursor cursor = Cursor.createCursor(table);
+ doTestMatcher(table, cursor, SimpleColumnMatcher.INSTANCE, false);
+ doTestMatcher(table, cursor, CaseInsensitiveColumnMatcher.INSTANCE,
+ true);
+ db.close();
+ }
+ }
+
+ private void doTestMatchers(Table table, ColumnMatcher columnMatcher,
+ boolean caseInsensitive)
+ throws Exception
+ {
+ assertTrue(columnMatcher.matches(table, "value", null, null));
+ assertFalse(columnMatcher.matches(table, "value", "foo", null));
+ assertFalse(columnMatcher.matches(table, "value", null, "foo"));
+ assertTrue(columnMatcher.matches(table, "value", "foo", "foo"));
+ assertTrue(columnMatcher.matches(table, "value", "foo", "Foo")
+ == caseInsensitive);
+
+ assertFalse(columnMatcher.matches(table, "value", 13, null));
+ assertFalse(columnMatcher.matches(table, "value", null, 13));
+ assertTrue(columnMatcher.matches(table, "value", 13, 13));
+ }
+
+ private void doTestMatcher(Table table, Cursor cursor,
+ ColumnMatcher columnMatcher,
+ boolean caseInsensitive)
+ throws Exception
+ {
+ cursor.setColumnMatcher(columnMatcher);
+
+ assertTrue(cursor.findRow(table.getColumn("id"), 3));
+ assertEquals(createExpectedRow("id", 3,
+ "value", "data" + 3),
+ cursor.getCurrentRow());
+
+ assertTrue(cursor.findRow(createExpectedRow(
+ "id", 6,
+ "value", "data" + 6)));
+ assertEquals(createExpectedRow("id", 6,
+ "value", "data" + 6),
+ cursor.getCurrentRow());
+
+ assertTrue(cursor.findRow(createExpectedRow(
+ "id", 6,
+ "value", "Data" + 6)) == caseInsensitive);
+ if(caseInsensitive) {
+ assertEquals(createExpectedRow("id", 6,
+ "value", "data" + 6),
+ cursor.getCurrentRow());
+ }
+
+ assertFalse(cursor.findRow(createExpectedRow(
+ "id", 8,
+ "value", "data" + 13)));
+ assertFalse(cursor.findRow(table.getColumn("id"), 13));
+ assertEquals(createExpectedRow("id", 6,
+ "value", "data" + 6),
+ cursor.getCurrentRow());
+
+ assertTrue(cursor.findRow(createExpectedRow(
+ "value", "data" + 7)));
+ assertEquals(createExpectedRow("id", 7,
+ "value", "data" + 7),
+ cursor.getCurrentRow());
+
+ assertTrue(cursor.findRow(createExpectedRow(
+ "value", "Data" + 7)) == caseInsensitive);
+ if(caseInsensitive) {
+ assertEquals(createExpectedRow("id", 7,
+ "value", "data" + 7),
+ cursor.getCurrentRow());
+ }
+
+ assertTrue(cursor.findRow(table.getColumn("value"), "data" + 4));
+ assertEquals(createExpectedRow("id", 4,
+ "value", "data" + 4),
+ cursor.getCurrentRow());
+
+ assertTrue(cursor.findRow(table.getColumn("value"), "Data" + 4)
+ == caseInsensitive);
+ if(caseInsensitive) {
+ assertEquals(createExpectedRow("id", 4,
+ "value", "data" + 4),
+ cursor.getCurrentRow());
+ }
+ }
+
}