aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/changes/changes.xml6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Database.java16
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Table.java6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java24
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java15
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java113
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/ColumnValidator.java41
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/ColumnValidatorFactory.java38
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnValidator.java40
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnValidatorFactory.java39
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/TableTest.java35
11 files changed, 312 insertions, 61 deletions
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 87e24c9..42ee0f1 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -4,6 +4,12 @@
<author email="javajedi@users.sf.net">Tim McCune</author>
</properties>
<body>
+ <release version="2.0.4" date="TBD">
+ <action dev="jahlborn" type="add">
+ Add ColumnValidator interface which allows column values to be easily
+ manipulated/validated as they are written into the database.
+ </action>
+ </release>
<release version="2.0.3" date="2014-01-30">
<action dev="jahlborn" type="fix" system="SourceForge2" issue="100">
Fix NullPointerException in RowImpl.toString() when value is null.
diff --git a/src/main/java/com/healthmarketscience/jackcess/Database.java b/src/main/java/com/healthmarketscience/jackcess/Database.java
index ab4b7ca..4e9d136 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Database.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Database.java
@@ -33,6 +33,7 @@ import java.util.TimeZone;
import com.healthmarketscience.jackcess.query.Query;
import com.healthmarketscience.jackcess.impl.DatabaseImpl;
+import com.healthmarketscience.jackcess.util.ColumnValidatorFactory;
import com.healthmarketscience.jackcess.util.ErrorHandler;
import com.healthmarketscience.jackcess.util.LinkResolver;
@@ -366,7 +367,7 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
public void setColumnOrder(Table.ColumnOrder newColumnOrder);
/**
- * Gets currently foreign-key enforcement policy.
+ * Gets current foreign-key enforcement policy.
* @usage _intermediate_method_
*/
public boolean isEnforceForeignKeys();
@@ -379,6 +380,19 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
public void setEnforceForeignKeys(Boolean newEnforceForeignKeys);
/**
+ * Gets currently configured ColumnValidatorFactory (always non-{@code null}).
+ * @usage _intermediate_method_
+ */
+ public ColumnValidatorFactory getColumnValidatorFactory();
+
+ /**
+ * Sets a new ColumnValidatorFactory. If {@code null}, resets to the
+ * default value.
+ * @usage _intermediate_method_
+ */
+ public void setColumnValidatorFactory(ColumnValidatorFactory newFactory);
+
+ /**
* Returns the FileFormat of this database (which may involve inspecting the
* database itself).
* @throws IllegalStateException if the file format cannot be determined
diff --git a/src/main/java/com/healthmarketscience/jackcess/Table.java b/src/main/java/com/healthmarketscience/jackcess/Table.java
index 0765e1f..16b559f 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Table.java
@@ -71,6 +71,12 @@ public interface Table extends Iterable<Row>
public boolean isHidden();
/**
+ * Whether or not this table is a system (internal) table.
+ * @usage _general_method_
+ */
+ public boolean isSystem();
+
+ /**
* @usage _general_method_
*/
public int getColumnCount();
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
index ae68a17..b2a1ffc 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
@@ -65,6 +65,8 @@ import com.healthmarketscience.jackcess.impl.scsu.Compress;
import com.healthmarketscience.jackcess.impl.scsu.EndOfInputException;
import com.healthmarketscience.jackcess.impl.scsu.Expand;
import com.healthmarketscience.jackcess.impl.scsu.IllegalInputException;
+import com.healthmarketscience.jackcess.util.ColumnValidator;
+import com.healthmarketscience.jackcess.util.SimpleColumnValidator;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -221,6 +223,8 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
private PropertyMap _props;
/** Holds additional info for writing long values */
private LongValueBufferHolder _lvalBufferH;
+ /** Validator for writing new values */
+ private ColumnValidator _validator = SimpleColumnValidator.INSTANCE;
/**
* @usage _advanced_method_
@@ -509,6 +513,18 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
public ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
return _complexInfo;
}
+
+ public ColumnValidator getColumnValidator() {
+ return _validator;
+ }
+
+ public void setColumnValidator(ColumnValidator newValidator) {
+ if(newValidator == null) {
+ newValidator = getDatabase().getColumnValidatorFactory()
+ .createValidator(this);
+ }
+ _validator = newValidator;
+ }
private void setUnknownDataType(byte type) {
// slight hack, stash the original type in the _scale
@@ -1198,6 +1214,14 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
}
/**
+ * Passes the given obj through the currently configured validator for this
+ * column and returns the result.
+ */
+ public Object validate(Object obj) throws IOException {
+ return _validator.validate(this, obj);
+ }
+
+ /**
* Serialize an Object into a raw byte value for this column in little
* endian order
* @param obj Object to serialize
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
index 810868c..8ddc4a0 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
@@ -72,8 +72,10 @@ import com.healthmarketscience.jackcess.Table;
import com.healthmarketscience.jackcess.impl.query.QueryImpl;
import com.healthmarketscience.jackcess.query.Query;
import com.healthmarketscience.jackcess.util.CaseInsensitiveColumnMatcher;
+import com.healthmarketscience.jackcess.util.ColumnValidatorFactory;
import com.healthmarketscience.jackcess.util.ErrorHandler;
import com.healthmarketscience.jackcess.util.LinkResolver;
+import com.healthmarketscience.jackcess.util.SimpleColumnValidatorFactory;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -303,6 +305,8 @@ public class DatabaseImpl implements Database
private Table.ColumnOrder _columnOrder;
/** whether or not enforcement of foreign-keys is enabled */
private boolean _enforceForeignKeys;
+ /** factory for ColumnValidators */
+ private ColumnValidatorFactory _validatorFactory = SimpleColumnValidatorFactory.INSTANCE;
/** cache of in-use tables */
private final TableCache _tableCache = new TableCache();
/** handler for reading/writing properteies */
@@ -638,6 +642,17 @@ public class DatabaseImpl implements Database
_enforceForeignKeys = newEnforceForeignKeys;
}
+ public ColumnValidatorFactory getColumnValidatorFactory() {
+ return _validatorFactory;
+ }
+
+ public void setColumnValidatorFactory(ColumnValidatorFactory newFactory) {
+ if(newFactory == null) {
+ newFactory = SimpleColumnValidatorFactory.INSTANCE;
+ }
+ _validatorFactory = newFactory;
+ }
+
/**
* @usage _advanced_method_
*/
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
index 15dfaec..f52bb1d 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
@@ -303,6 +303,11 @@ public class TableImpl implements Table
}
_fkEnforcer = new FKEnforcer(this);
+
+ // after fully constructed, allow column validator to be configured
+ for(ColumnImpl col : _columns) {
+ col.setColumnValidator(null);
+ }
}
public String getName() {
@@ -313,6 +318,10 @@ public class TableImpl implements Table
return((_flags & DatabaseImpl.HIDDEN_OBJECT_FLAG) != 0);
}
+ public boolean isSystem() {
+ return(_tableType != TYPE_USER);
+ }
+
/**
* @usage _advanced_method_
*/
@@ -1528,8 +1537,35 @@ public class TableImpl implements Table
dupeRows.set(i, row);
}
- // fill in autonumbers
- handleAutoNumbersForAdd(row);
+ // handle various value massaging activities
+ Object complexAutoNumber = null;
+ for(ColumnImpl column : _columns) {
+
+ Object rowValue = null;
+ if(column.isAutoNumber()) {
+
+ // fill in autonumbers, ignore given row value, use next
+ // autonumber
+ ColumnImpl.AutoNumberGenerator autoNumGen =
+ column.getAutoNumberGenerator();
+ if(autoNumGen.getType() != DataType.COMPLEX_TYPE) {
+ rowValue = autoNumGen.getNext(null);
+ } else {
+ // complex type auto numbers are shared across all complex
+ // columns in the row
+ complexAutoNumber = autoNumGen.getNext(complexAutoNumber);
+ rowValue = complexAutoNumber;
+ }
+
+ } else {
+
+ // pass input value through column validator
+ rowValue = column.validate(column.getRowValue(row));
+ }
+
+ column.setRowValue(row, rowValue);
+ }
+
++autoNumAssignCount;
// write the row of data to a temporary buffer
@@ -1732,20 +1768,38 @@ public class TableImpl implements Table
Map<ColumnImpl,byte[]> keepRawVarValues =
(!_varColumns.isEmpty() ? new HashMap<ColumnImpl,byte[]>() : null);
+ // handle various value massaging activities
for(ColumnImpl column : _columns) {
- if(_autoNumColumns.contains(column)) {
+
+ Object rowValue = null;
+ if(column.isAutoNumber()) {
+
// fill in any auto-numbers (we don't allow autonumber values to be
// modified)
- column.setRowValue(row, getRowColumn(getFormat(), rowBuffer, column,
- rowState, null));
- } else if(column.getRowValue(row) == Column.KEEP_VALUE) {
- // fill in any "keep value" fields
- column.setRowValue(row, getRowColumn(getFormat(), rowBuffer, column,
- rowState, keepRawVarValues));
- } else if(_indexColumns.contains(column)) {
- // read row value to help update indexes
- getRowColumn(getFormat(), rowBuffer, column, rowState, null);
+ rowValue = getRowColumn(getFormat(), rowBuffer, column, rowState, null);
+
+ } else {
+
+ rowValue = column.getRowValue(row);
+ if(rowValue == Column.KEEP_VALUE) {
+
+ // fill in any "keep value" fields (restore old value)
+ rowValue = getRowColumn(getFormat(), rowBuffer, column, rowState,
+ keepRawVarValues);
+
+ } else {
+
+ if(_indexColumns.contains(column)) {
+ // read (old) row value to help update indexes
+ getRowColumn(getFormat(), rowBuffer, column, rowState, null);
+ }
+
+ // pass input value through column validator
+ rowValue = column.validate(rowValue);
+ }
}
+
+ column.setRowValue(row, rowValue);
}
// generate new row bytes
@@ -1969,10 +2023,7 @@ public class TableImpl implements Table
return dataPage;
}
- /**
- * @usage _advanced_method_
- */
- public ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer)
+ protected ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer)
throws IOException
{
return createRow(rowArray, buffer, 0,
@@ -2133,33 +2184,6 @@ public class TableImpl implements Table
}
/**
- * Fill in all autonumber column values.
- */
- private void handleAutoNumbersForAdd(Object[] row)
- throws IOException
- {
- if(_autoNumColumns.isEmpty()) {
- return;
- }
-
- Object complexAutoNumber = null;
- for(ColumnImpl col : _autoNumColumns) {
- // ignore given row value, use next autonumber
- ColumnImpl.AutoNumberGenerator autoNumGen = col.getAutoNumberGenerator();
- Object rowValue = null;
- if(autoNumGen.getType() != DataType.COMPLEX_TYPE) {
- rowValue = autoNumGen.getNext(null);
- } else {
- // complex type auto numbers are shared across all complex columns
- // in the row
- complexAutoNumber = autoNumGen.getNext(complexAutoNumber);
- rowValue = complexAutoNumber;
- }
- col.setRowValue(row, rowValue);
- }
- }
-
- /**
* Restores all autonumber column values from a failed add row.
*/
private void restoreAutoNumbersFromAdd(Object[] row)
@@ -2224,8 +2248,7 @@ public class TableImpl implements Table
@Override
public String toString() {
return CustomToStringStyle.builder(this)
- .append("type", (_tableType +
- ((_tableType == TYPE_USER) ? " (USER)" : " (SYSTEM)")))
+ .append("type", (_tableType + (!isSystem() ? " (USER)" : " (SYSTEM)")))
.append("name", _name)
.append("rowCount", _rowCount)
.append("columnCount", _columns.size())
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidator.java b/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidator.java
new file mode 100644
index 0000000..50c8916
--- /dev/null
+++ b/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidator.java
@@ -0,0 +1,41 @@
+/*
+Copyright (c) 2014 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.util;
+
+import java.io.IOException;
+
+import com.healthmarketscience.jackcess.Column;
+
+/**
+ * Interface which allows for data manipulation/validation as values are being
+ * inserted into a database.
+ *
+ * @author James Ahlborn
+ */
+public interface ColumnValidator
+{
+ /**
+ * Validates and/or manipulates the given potential new value for the given
+ * column. This method may return an entirely different value or throw an
+ * exception if the input value is not valid.
+ */
+ public Object validate(Column col, Object val) throws IOException;
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidatorFactory.java b/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidatorFactory.java
new file mode 100644
index 0000000..3a8a323
--- /dev/null
+++ b/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidatorFactory.java
@@ -0,0 +1,38 @@
+/*
+Copyright (c) 2014 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.util;
+
+import com.healthmarketscience.jackcess.Column;
+
+/**
+ * Factory which generates appropriate ColumnValidators when Column instances
+ * are created.
+ *
+ * @author James Ahlborn
+ */
+public interface ColumnValidatorFactory
+{
+ /**
+ * Returns a ColumnValidator instance for the given column, must be
+ * non-{@code null}.
+ */
+ public ColumnValidator createValidator(Column col);
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnValidator.java b/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnValidator.java
new file mode 100644
index 0000000..7dd8817
--- /dev/null
+++ b/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnValidator.java
@@ -0,0 +1,40 @@
+/*
+Copyright (c) 2014 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.util;
+
+import java.io.IOException;
+
+import com.healthmarketscience.jackcess.Column;
+
+/**
+ * Simple concrete implementation of ColumnValidator which simply returns the
+ * given value.
+ *
+ * @author James Ahlborn
+ */
+public class SimpleColumnValidator implements ColumnValidator
+{
+ public static final SimpleColumnValidator INSTANCE = new SimpleColumnValidator();
+
+ public Object validate(Column col, Object val) throws IOException {
+ return val;
+ }
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnValidatorFactory.java b/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnValidatorFactory.java
new file mode 100644
index 0000000..367f8c7
--- /dev/null
+++ b/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnValidatorFactory.java
@@ -0,0 +1,39 @@
+/*
+Copyright (c) 2014 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.util;
+
+import com.healthmarketscience.jackcess.Column;
+
+/**
+ * Simple concrete implementation of ColumnValidatorFactory which returns
+ * {@link SimpleColumnValidator.INSTANCE} for all columns.
+ *
+ * @author James Ahlborn
+ */
+public class SimpleColumnValidatorFactory implements ColumnValidatorFactory
+{
+ public static final SimpleColumnValidatorFactory INSTANCE =
+ new SimpleColumnValidatorFactory();
+
+ public ColumnValidator createValidator(Column col) {
+ return SimpleColumnValidator.INSTANCE;
+ }
+}
diff --git a/src/test/java/com/healthmarketscience/jackcess/TableTest.java b/src/test/java/com/healthmarketscience/jackcess/TableTest.java
index b70c045..29408ef 100644
--- a/src/test/java/com/healthmarketscience/jackcess/TableTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/TableTest.java
@@ -34,7 +34,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
-import java.util.TimeZone;
import com.healthmarketscience.jackcess.impl.ColumnImpl;
import com.healthmarketscience.jackcess.impl.JetFormat;
@@ -49,7 +48,7 @@ public class TableTest extends TestCase {
private final PageChannel _pageChannel = new PageChannel(true) {};
private List<ColumnImpl> _columns = new ArrayList<ColumnImpl>();
- private TableImpl _testTable;
+ private TestTable _testTable;
private int _varLenIdx;
private int _fixedOffset;
@@ -126,8 +125,7 @@ public class TableTest extends TestCase {
private ByteBuffer createRow(Object... row)
throws IOException
{
- return _testTable.createRow(
- row, _testTable.getPageChannel().createPageBuffer());
+ return _testTable.createRow(row);
}
private ByteBuffer[] encodeColumns(Object... row)
@@ -162,16 +160,7 @@ public class TableTest extends TestCase {
private TableImpl newTestTable()
throws Exception
{
- _testTable = new TableImpl(true, _columns) {
- @Override
- public PageChannel getPageChannel() {
- return _pageChannel;
- }
- @Override
- public JetFormat getFormat() {
- return JetFormat.VERSION_4;
- }
- };
+ _testTable = new TestTable();
return _testTable;
}
@@ -217,5 +206,21 @@ public class TableTest extends TestCase {
_columns.add(col);
}
-
+
+ private class TestTable extends TableImpl {
+ private TestTable() throws IOException {
+ super(true, _columns);
+ }
+ public ByteBuffer createRow(Object... row) throws IOException {
+ return super.createRow(row, getPageChannel().createPageBuffer());
+ }
+ @Override
+ public PageChannel getPageChannel() {
+ return _pageChannel;
+ }
+ @Override
+ public JetFormat getFormat() {
+ return JetFormat.VERSION_4;
+ }
+ }
}