aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2014-03-24 02:39:58 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2014-03-24 02:39:58 +0000
commit393f14089d9f9e95a93352398d183d3f59ba9440 (patch)
treece54106207d6756045efcbed215195966aa6cc33 /src/main/java
parent23b2a93764a458bc86ca6121b2b4e81b2f283e32 (diff)
downloadjackcess-393f14089d9f9e95a93352398d183d3f59ba9440.tar.gz
jackcess-393f14089d9f9e95a93352398d183d3f59ba9440.zip
add column validator unit tests, work out some wrinkles
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@851 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Column.java18
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Database.java4
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java24
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java117
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/ColumnValidatorFactory.java4
5 files changed, 121 insertions, 46 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/Column.java b/src/main/java/com/healthmarketscience/jackcess/Column.java
index ee4c770..383f605 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Column.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Column.java
@@ -25,6 +25,7 @@ import java.util.Map;
import com.healthmarketscience.jackcess.complex.ComplexColumnInfo;
import com.healthmarketscience.jackcess.complex.ComplexValue;
+import com.healthmarketscience.jackcess.util.ColumnValidator;
/**
* Access database column definition. A {@link Table} has a list of Column
@@ -151,6 +152,23 @@ public interface Column
*/
public Column getVersionHistoryColumn();
+ /**
+ * Gets currently configured ColumnValidator (always non-{@code null}).
+ * @usage _intermediate_method_
+ */
+ public ColumnValidator getColumnValidator();
+
+ /**
+ * Sets a new ColumnValidator. If {@code null}, resets to the value
+ * returned from the Database's ColumnValidatorFactory (if the factory
+ * returns {@code null}, then the default is used). Autonumber columns
+ * cannot have a validator instance other than the default.
+ * @throws IllegalArgumentException if an attempt is made to set a
+ * non-{@code null} ColumnValidator instance on an autonumber column
+ * @usage _intermediate_method_
+ */
+ public void setColumnValidator(ColumnValidator newValidator);
+
public Object setRowValue(Object[] rowArray, Object value);
public Object setRowValue(Map<String,Object> rowMap, Object value);
diff --git a/src/main/java/com/healthmarketscience/jackcess/Database.java b/src/main/java/com/healthmarketscience/jackcess/Database.java
index 4e9d136..24d291d 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Database.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Database.java
@@ -387,7 +387,9 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
/**
* Sets a new ColumnValidatorFactory. If {@code null}, resets to the
- * default value.
+ * default value. The configured ColumnValidatorFactory will be used to
+ * create ColumnValidator instances on any <i>user</i> tables loaded from
+ * this point onward (this will not be used for system tables).
* @usage _intermediate_method_
*/
public void setColumnValidatorFactory(ColumnValidatorFactory newFactory);
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
index b2a1ffc..6cac9fb 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
@@ -519,9 +519,24 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
}
public void setColumnValidator(ColumnValidator newValidator) {
+
+ if(isAutoNumber()) {
+ // cannot set autonumber validator (autonumber values are controlled
+ // internally)
+ if(newValidator != null) {
+ throw new IllegalArgumentException(
+ "Cannot set ColumnValidator for autonumber columns");
+ }
+ // just leave default validator instance alone
+ return;
+ }
+
if(newValidator == null) {
newValidator = getDatabase().getColumnValidatorFactory()
.createValidator(this);
+ if(newValidator == null) {
+ newValidator = SimpleColumnValidator.INSTANCE;
+ }
}
_validator = newValidator;
}
@@ -1938,6 +1953,15 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
}
/**
+ * Returns {@code true} if the value is immutable, {@code false} otherwise.
+ * This only handles values that are returned from the {@link #read} method.
+ */
+ static boolean isImmutableValue(Object value) {
+ // for now, the only mutable value this class returns is byte[]
+ return !(value instanceof byte[]);
+ }
+
+ /**
* Date subclass which stashes the original date bits, in case we attempt to
* re-write the value (will not lose precision).
*/
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
index f52bb1d..4107abb 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
@@ -304,9 +304,12 @@ 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);
+ if(!isSystem()) {
+ // after fully constructed, allow column validator to be configured (but
+ // only for user tables)
+ for(ColumnImpl col : _columns) {
+ col.setColumnValidator(null);
+ }
}
}
@@ -578,7 +581,7 @@ public class TableImpl implements Table
}
// use any read rowValues to help update the indexes
- rowValues = rowState.getRowValues();
+ rowValues = rowState.getRowCacheValues();
// check foreign keys before proceeding w/ deletion
_fkEnforcer.deleteRow(rowValues);
@@ -692,13 +695,19 @@ public class TableImpl implements Table
if(column.getType() == DataType.BOOLEAN) {
// Boolean values are stored in the null mask. see note about
// caching below
- return rowState.setRowValue(column.getColumnIndex(),
- Boolean.valueOf(!isNull));
+ return rowState.setRowCacheValue(column.getColumnIndex(),
+ Boolean.valueOf(!isNull));
} else if(isNull) {
// well, that's easy! (no need to update cache w/ null)
return null;
}
+ Object cachedValue = rowState.getRowCacheValue(column.getColumnIndex());
+ if(cachedValue != null) {
+ // we already have it, use it
+ return cachedValue;
+ }
+
// reset position to row start
rowBuffer.reset();
@@ -754,14 +763,14 @@ public class TableImpl implements Table
// to update the index on row deletion. note, most of the returned
// values are immutable, except for binary data (returned as byte[]),
// but binary data shouldn't be indexed anyway.
- return rowState.setRowValue(column.getColumnIndex(),
- column.read(columnData));
+ return rowState.setRowCacheValue(column.getColumnIndex(),
+ column.read(columnData));
} catch(Exception e) {
// cache "raw" row value. see note about caching above
- rowState.setRowValue(column.getColumnIndex(),
- ColumnImpl.rawDataWrapper(columnData));
+ rowState.setRowCacheValue(column.getColumnIndex(),
+ ColumnImpl.rawDataWrapper(columnData));
return rowState.handleRowError(column, columnData, e);
}
@@ -1538,34 +1547,15 @@ public class TableImpl implements Table
}
// 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 {
-
+ if(!column.isAutoNumber()) {
// pass input value through column validator
- rowValue = column.validate(column.getRowValue(row));
+ column.setRowValue(row, column.validate(column.getRowValue(row)));
}
-
- column.setRowValue(row, rowValue);
}
-
+
+ // fill in autonumbers
+ handleAutoNumbersForAdd(row);
++autoNumAssignCount;
// write the row of data to a temporary buffer
@@ -1779,7 +1769,7 @@ public class TableImpl implements Table
rowValue = getRowColumn(getFormat(), rowBuffer, column, rowState, null);
} else {
-
+
rowValue = column.getRowValue(row);
if(rowValue == Column.KEEP_VALUE) {
@@ -1788,14 +1778,21 @@ public class TableImpl implements Table
keepRawVarValues);
} else {
-
+
+ // set oldValue to something that could not possibly be a real value
+ Object oldValue = Column.KEEP_VALUE;
if(_indexColumns.contains(column)) {
// read (old) row value to help update indexes
- getRowColumn(getFormat(), rowBuffer, column, rowState, null);
+ oldValue = getRowColumn(getFormat(), rowBuffer, column, rowState, null);
+ } else {
+ oldValue = rowState.getRowCacheValue(column.getColumnIndex());
}
-
- // pass input value through column validator
- rowValue = column.validate(rowValue);
+
+ // if the old value was passed back in, we don't need to validate
+ if(oldValue != rowValue) {
+ // pass input value through column validator
+ rowValue = column.validate(rowValue);
+ }
}
}
@@ -1817,7 +1814,7 @@ public class TableImpl implements Table
IndexData.PendingChange idxChange = null;
try {
- Object[] oldRowValues = rowState.getRowValues();
+ Object[] oldRowValues = rowState.getRowCacheValues();
// check foreign keys before actually updating
_fkEnforcer.updateRow(oldRowValues, row);
@@ -2184,6 +2181,33 @@ 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)
@@ -2607,13 +2631,20 @@ public class TableImpl implements Table
return(_status.ordinal() >= RowStateStatus.AT_FINAL.ordinal());
}
- private Object setRowValue(int idx, Object value) {
+ private Object setRowCacheValue(int idx, Object value) {
_haveRowValues = true;
_rowValues[idx] = value;
return value;
}
+
+ private Object getRowCacheValue(int idx) {
+ Object value = _rowValues[idx];
+ // only return immutable values. mutable values could have been
+ // modified externally and therefore could return an incorrect value
+ return(ColumnImpl.isImmutableValue(value) ? value : null);
+ }
- public Object[] getRowValues() {
+ public Object[] getRowCacheValues() {
return dupeRow(_rowValues, _rowValues.length);
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidatorFactory.java b/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidatorFactory.java
index 3a8a323..2f5bcd7 100644
--- a/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidatorFactory.java
+++ b/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidatorFactory.java
@@ -31,8 +31,8 @@ import com.healthmarketscience.jackcess.Column;
public interface ColumnValidatorFactory
{
/**
- * Returns a ColumnValidator instance for the given column, must be
- * non-{@code null}.
+ * Returns a ColumnValidator instance for the given column, or {@code null}
+ * if the default should be used.
*/
public ColumnValidator createValidator(Column col);
}