diff options
author | James Ahlborn <jtahlborn@yahoo.com> | 2014-03-24 02:39:58 +0000 |
---|---|---|
committer | James Ahlborn <jtahlborn@yahoo.com> | 2014-03-24 02:39:58 +0000 |
commit | 393f14089d9f9e95a93352398d183d3f59ba9440 (patch) | |
tree | ce54106207d6756045efcbed215195966aa6cc33 /src/main/java | |
parent | 23b2a93764a458bc86ca6121b2b4e81b2f283e32 (diff) | |
download | jackcess-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')
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); } |