Browse Source

start enforcing the required and no-zero len column properties

git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/exprs@1147 f203690c-595d-4dc9-a70b-905162fa7fd2
tags/jackcess-2.2.0
James Ahlborn 6 years ago
parent
commit
5a39a80966

+ 32
- 0
src/main/java/com/healthmarketscience/jackcess/InvalidValueException.java View File

@@ -0,0 +1,32 @@
/*
Copyright (c) 2018 James Ahlborn

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package com.healthmarketscience.jackcess;

/**
* JackcessException which indicates that an invalid column value was provided
* in a database update.
*
* @author James Ahlborn
*/
public class InvalidValueException extends JackcessException
{
private static final long serialVersionUID = 20180428L;

public InvalidValueException(String msg) {
super(msg);
}
}

+ 18
- 16
src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java View File

@@ -21,6 +21,8 @@ import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import com.healthmarketscience.jackcess.InvalidValueException;


/**
* Utility code for dealing with calculated columns.
@@ -30,7 +32,7 @@ import java.nio.ByteOrder;
*
* @author James Ahlborn
*/
class CalculatedColumnUtil
class CalculatedColumnUtil
{
// offset to the int which specifies the length of the actual data
private static final int CALC_DATA_LEN_OFFSET = 16;
@@ -51,12 +53,12 @@ class CalculatedColumnUtil
/**
* Creates the appropriate ColumnImpl class for a calculated column and
* reads a column definition in from a buffer
*
*
* @param args column construction info
* @usage _advanced_method_
*/
static ColumnImpl create(ColumnImpl.InitArgs args) throws IOException
{
{
switch(args.type) {
case BOOLEAN:
return new CalcBooleanColImpl(args);
@@ -71,7 +73,7 @@ class CalculatedColumnUtil
if(args.type.getHasScalePrecision()) {
return new CalcNumericColImpl(args);
}
return new CalcColImpl(args);
}

@@ -82,7 +84,7 @@ class CalculatedColumnUtil
if(data.length < CALC_DATA_OFFSET) {
return data;
}
ByteBuffer buffer = PageChannel.wrap(data);
buffer.position(CALC_DATA_LEN_OFFSET);
int dataLen = buffer.getInt();
@@ -109,7 +111,7 @@ class CalculatedColumnUtil
*/
private static byte[] wrapCalculatedValue(byte[] data) {
int dataLen = data.length;
data = ByteUtil.copyOf(data, 0, dataLen + CALC_EXTRA_DATA_LEN,
data = ByteUtil.copyOf(data, 0, dataLen + CALC_EXTRA_DATA_LEN,
CALC_DATA_OFFSET);
PageChannel.wrap(data).putInt(CALC_DATA_LEN_OFFSET, dataLen);
return data;
@@ -126,7 +128,7 @@ class CalculatedColumnUtil
buffer.position(CALC_DATA_OFFSET);
return buffer;
}

/**
* General calculated column implementation.
@@ -148,7 +150,7 @@ class CalculatedColumnUtil
}

@Override
protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
ByteOrder order)
throws IOException
{
@@ -185,7 +187,7 @@ class CalculatedColumnUtil
}

@Override
protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
ByteOrder order)
throws IOException
{
@@ -216,7 +218,7 @@ class CalculatedColumnUtil
}

@Override
protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
ByteOrder order)
throws IOException
{
@@ -249,12 +251,12 @@ class CalculatedColumnUtil
}

@Override
protected ByteBuffer writeLongValue(byte[] value, int remainingRowLength)
protected ByteBuffer writeLongValue(byte[] value, int remainingRowLength)
throws IOException
{
return super.writeLongValue(
wrapCalculatedValue(value), remainingRowLength);
}
}
}

/**
@@ -282,7 +284,7 @@ class CalculatedColumnUtil
}

@Override
protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
ByteOrder order)
throws IOException
{
@@ -337,14 +339,14 @@ class CalculatedColumnUtil
decVal = decVal.setScale(maxScale);
}
int scale = decVal.scale();
// check precision
if(decVal.precision() > getType().getMaxPrecision()) {
throw new IOException(withErrorContext(
throw new InvalidValueException(withErrorContext(
"Numeric value is too big for specified precision "
+ getType().getMaxPrecision() + ": " + decVal));
}
// convert to unscaled BigInteger, big-endian bytes
byte[] intValBytes = toUnscaledByteArray(decVal, dataLen - 4);


+ 230
- 135
src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
File diff suppressed because it is too large
View File


+ 160
- 159
src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
File diff suppressed because it is too large
View File


+ 62
- 0
src/main/java/com/healthmarketscience/jackcess/impl/InternalColumnValidator.java View File

@@ -0,0 +1,62 @@
/*
Copyright (c) 2018 James Ahlborn

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package com.healthmarketscience.jackcess.impl;

import java.io.IOException;

import com.healthmarketscience.jackcess.Column;
import com.healthmarketscience.jackcess.util.ColumnValidator;

/**
* Base class for ColumnValidator instances handling "internal" validation
* functionality, which are wrappers around any "external" behavior.
*
* @author James Ahlborn
*/
abstract class InternalColumnValidator implements ColumnValidator
{
private ColumnValidator _delegate;

protected InternalColumnValidator(ColumnValidator delegate)
{
_delegate = delegate;
}

ColumnValidator getExternal() {
ColumnValidator extValidator = _delegate;
while(extValidator instanceof InternalColumnValidator) {
extValidator = ((InternalColumnValidator)extValidator)._delegate;
}
return extValidator;
}

void setExternal(ColumnValidator extValidator) {
InternalColumnValidator intValidator = this;
while(intValidator._delegate instanceof InternalColumnValidator) {
intValidator = (InternalColumnValidator)intValidator._delegate;
}
intValidator._delegate = extValidator;
}

public final Object validate(Column col, Object val) throws IOException {
val = _delegate.validate(col, val);
return internalValidate(col, val);
}

protected abstract Object internalValidate(Column col, Object val)
throws IOException;
}

+ 32
- 32
src/main/java/com/healthmarketscience/jackcess/impl/LongValueColumnImpl.java View File

@@ -17,19 +17,19 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collection;

import com.healthmarketscience.jackcess.InvalidValueException;

/**
* ColumnImpl subclass which is used for long value data types.
*
*
* @author James Ahlborn
* @usage _advanced_class_
*/
class LongValueColumnImpl extends ColumnImpl
class LongValueColumnImpl extends ColumnImpl
{
/**
* Long value (LVAL) type that indicates that the value is stored on the
@@ -60,12 +60,12 @@ class LongValueColumnImpl extends ColumnImpl
{
super(args);
}
@Override
public int getOwnedPageCount() {
return ((_lvalBufferH == null) ? 0 : _lvalBufferH.getOwnedPageCount());
}
@Override
void setUsageMaps(UsageMap ownedPages, UsageMap freeSpacePages) {
_lvalBufferH = new UmapLongValueBufferHolder(ownedPages, freeSpacePages);
@@ -75,7 +75,7 @@ class LongValueColumnImpl extends ColumnImpl
void collectUsageMapPages(Collection<Integer> pages) {
_lvalBufferH.collectUsageMapPages(pages);
}
@Override
void postTableLoadInit() throws IOException {
if(_lvalBufferH == null) {
@@ -104,7 +104,7 @@ class LongValueColumnImpl extends ColumnImpl
default:
throw new RuntimeException(withErrorContext(
"unexpected var length, long value type: " + getType()));
}
}
}

@Override
@@ -122,12 +122,12 @@ class LongValueColumnImpl extends ColumnImpl
default:
throw new RuntimeException(withErrorContext(
"unexpected var length, long value type: " + getType()));
}
}

// create long value buffer
return writeLongValue(toByteArray(obj), remainingRowLength);
}
}
/**
* @param lvalDefinition Column value that points to an LVAL record
* @return The LVAL data
@@ -152,7 +152,7 @@ class LongValueColumnImpl extends ColumnImpl
if(rowLen < length) {
// warn the caller, but return whatever we can
LOG.warn(withErrorContext(
"Value may be truncated: expected length " +
"Value may be truncated: expected length " +
length + " found " + rowLen));
rtn = new byte[rowLen];
}
@@ -172,7 +172,7 @@ class LongValueColumnImpl extends ColumnImpl
int rowNum = ByteUtil.getUnsignedByte(def);
int pageNum = ByteUtil.get3ByteInt(def, def.position());
ByteBuffer lvalPage = getPageChannel().createPageBuffer();
switch (type) {
case LONG_VALUE_TYPE_OTHER_PAGE:
{
@@ -185,16 +185,16 @@ class LongValueColumnImpl extends ColumnImpl
if(rowLen < length) {
// warn the caller, but return whatever we can
LOG.warn(withErrorContext(
"Value may be truncated: expected length " +
"Value may be truncated: expected length " +
length + " found " + rowLen));
rtn = new byte[rowLen];
}
lvalPage.position(rowStart);
lvalPage.get(rtn);
}
break;
case LONG_VALUE_TYPE_OTHER_PAGES:

ByteBuffer rtnBuf = ByteBuffer.wrap(rtn);
@@ -205,7 +205,7 @@ class LongValueColumnImpl extends ColumnImpl

short rowStart = TableImpl.findRowStart(lvalPage, rowNum, getFormat());
short rowEnd = TableImpl.findRowEnd(lvalPage, rowNum, getFormat());
// read next page information
lvalPage.position(rowStart);
rowNum = ByteUtil.getUnsignedByte(lvalPage);
@@ -218,22 +218,22 @@ class LongValueColumnImpl extends ColumnImpl
chunkLength = remainingLen;
}
remainingLen -= chunkLength;
lvalPage.limit(rowEnd);
rtnBuf.put(lvalPage);
}
break;
default:
throw new IOException(withErrorContext(
"Unrecognized long value type: " + type));
}
}
return rtn;
}
/**
* @param lvalDefinition Column value that points to an LVAL record
* @return The LVAL data
@@ -259,11 +259,11 @@ class LongValueColumnImpl extends ColumnImpl
* value (unless written to other pages)
* @usage _advanced_method_
*/
protected ByteBuffer writeLongValue(byte[] value, int remainingRowLength)
protected ByteBuffer writeLongValue(byte[] value, int remainingRowLength)
throws IOException
{
if(value.length > getType().getMaxSize()) {
throw new IOException(withErrorContext(
throw new InvalidValueException(withErrorContext(
"value too big for column, max " +
getType().getMaxSize() + ", got " + value.length));
}
@@ -292,11 +292,11 @@ class LongValueColumnImpl extends ColumnImpl
def.putInt(0); //Unknown
def.put(value);
} else {
ByteBuffer lvalPage = null;
int firstLvalPageNum = PageChannel.INVALID_PAGE_NUMBER;
byte firstLvalRow = 0;
// write other page(s)
switch(type) {
case LONG_VALUE_TYPE_OTHER_PAGE:
@@ -335,7 +335,7 @@ class LongValueColumnImpl extends ColumnImpl
nextLvalPage = _lvalBufferH.getLongValuePage(
(remainingLen - chunkLength) + 4);
nextLvalPageNum = _lvalBufferH.getPageNumber();
nextLvalRowNum = TableImpl.getRowsOnDataPage(nextLvalPage,
nextLvalRowNum = TableImpl.getRowsOnDataPage(nextLvalPage,
getFormat());
} else {
nextLvalPage = null;
@@ -345,7 +345,7 @@ class LongValueColumnImpl extends ColumnImpl

// add row to this page
TableImpl.addDataPageRow(lvalPage, chunkLength + 4, getFormat(), 0);
// write next page info
lvalPage.put((byte)nextLvalRowNum); // row number
ByteUtil.put3ByteInt(lvalPage, nextLvalPageNum); // page number
@@ -373,9 +373,9 @@ class LongValueColumnImpl extends ColumnImpl
def.put(firstLvalRow);
ByteUtil.put3ByteInt(def, firstLvalPageNum);
def.putInt(0); //Unknown
}
def.flip();
return def;
}
@@ -499,10 +499,10 @@ class LongValueColumnImpl extends ColumnImpl
@Override
protected ByteBuffer findNewPage(int dataLength) throws IOException {

// grab last owned page and check for free space.
ByteBuffer newPage = TableImpl.findFreeRowSpace(
// grab last owned page and check for free space.
ByteBuffer newPage = TableImpl.findFreeRowSpace(
_ownedPages, _freeSpacePages, _longValueBufferH);
if(newPage != null) {
if(TableImpl.rowFitsOnDataPage(dataLength, newPage, getFormat())) {
return newPage;

+ 41
- 24
src/main/java/com/healthmarketscience/jackcess/impl/PropertyMaps.java View File

@@ -46,16 +46,19 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>

/** maps the PropertyMap name (case-insensitive) to the PropertyMap
instance */
private final Map<String,PropertyMapImpl> _maps =
private final Map<String,PropertyMapImpl> _maps =
new LinkedHashMap<String,PropertyMapImpl>();
private final int _objectId;
private final RowIdImpl _rowId;
private final Handler _handler;
private final Owner _owner;

public PropertyMaps(int objectId, RowIdImpl rowId, Handler handler) {
public PropertyMaps(int objectId, RowIdImpl rowId, Handler handler,
Owner owner) {
_objectId = objectId;
_rowId = rowId;
_handler = handler;
_owner = owner;
}

public int getObjectId() {
@@ -110,6 +113,9 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>

public void save() throws IOException {
_handler.save(this);
if(_owner != null) {
_owner.propertiesUpdated();
}
}

@Override
@@ -129,7 +135,7 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
/** the system table "property" column */
private final ColumnImpl _propCol;
/** cache of PropColumns used to read/write property values */
private final Map<DataType,PropColumn> _columns =
private final Map<DataType,PropColumn> _columns =
new HashMap<DataType,PropColumn>();

Handler(DatabaseImpl database) {
@@ -142,11 +148,11 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
* @return a PropertyMaps instance decoded from the given bytes (always
* returns non-{@code null} result).
*/
public PropertyMaps read(byte[] propBytes, int objectId,
RowIdImpl rowId)
throws IOException
public PropertyMaps read(byte[] propBytes, int objectId,
RowIdImpl rowId, Owner owner)
throws IOException
{
PropertyMaps maps = new PropertyMaps(objectId, rowId, this);
PropertyMaps maps = new PropertyMaps(objectId, rowId, this, owner);
if((propBytes == null) || (propBytes.length == 0)) {
return maps;
}
@@ -176,7 +182,7 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
short type = bb.getShort();
int endPos = bb.position() + len - 6;

ByteBuffer bbBlock = PageChannel.narrowBuffer(bb, bb.position(),
ByteBuffer bbBlock = PageChannel.narrowBuffer(bb, bb.position(),
endPos);

if(type == PROPERTY_NAME_LIST) {
@@ -226,7 +232,7 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
writeBlock(propMap, propNames, propMap.getType(), bab);
}
}
return bab.toArray();
}

@@ -260,12 +266,12 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
writePropertyNames(propNames, bab);
} else {
writePropertyValues(propMap, propNames, bab);
}
}

int len = bab.position() - blockStartPos;
bab.putInt(blockStartPos, len);
}
/**
* @return the property names parsed from the given data chunk
*/
@@ -281,7 +287,7 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
ByteArrayBuilder bab) {
for(String propName : propNames) {
writePropName(propName, bab);
}
}
}

/**
@@ -290,7 +296,7 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
*/
private PropertyMapImpl readPropertyValues(
ByteBuffer bbBlock, List<String> propNames, short blockType,
PropertyMaps maps)
PropertyMaps maps)
throws IOException
{
String mapName = DEFAULT_NAME;
@@ -305,13 +311,13 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
}
bbBlock.position(endPos);
}
PropertyMapImpl map = maps.get(mapName, blockType);

// read the values
while(bbBlock.hasRemaining()) {

int valLen = bbBlock.getShort();
int valLen = bbBlock.getShort();
int endPos = bbBlock.position() + valLen - 2;
boolean isDdl = (bbBlock.get() != 0);
DataType dataType = DataType.fromByte(bbBlock.get());
@@ -333,9 +339,9 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
}

private void writePropertyValues(
PropertyMapImpl propMap, Set<String> propNames, ByteArrayBuilder bab)
PropertyMapImpl propMap, Set<String> propNames, ByteArrayBuilder bab)
throws IOException
{
{
// write the map name, if any
String mapName = propMap.getName();
int blockStartPos = bab.position();
@@ -384,7 +390,7 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
/**
* Reads a property name from the given data block
*/
private String readPropName(ByteBuffer buffer) {
private String readPropName(ByteBuffer buffer) {
int nameLength = buffer.getShort();
byte[] nameBytes = ByteUtil.getBytes(buffer, nameLength);
return ColumnImpl.decodeUncompressedText(nameBytes, _database.getCharset());
@@ -404,8 +410,8 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
* Gets a PropColumn capable of reading/writing a property of the given
* DataType
*/
private PropColumn getColumn(DataType dataType, String propName,
int dataSize, Object value)
private PropColumn getColumn(DataType dataType, String propName,
int dataSize, Object value)
throws IOException
{

@@ -426,7 +432,7 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
}

// create column with ability to read/write the given data type
col = ((colType == DataType.BOOLEAN) ?
col = ((colType == DataType.BOOLEAN) ?
new BooleanPropColumn() : new PropColumn(colType));

_columns.put(dataType, col);
@@ -436,11 +442,11 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
}

private static boolean isPseudoGuidColumn(
DataType dataType, String propName, int dataSize, Object value)
DataType dataType, String propName, int dataSize, Object value)
throws IOException
{
// guids seem to be marked as "binary" fields
return((dataType == DataType.BINARY) &&
return((dataType == DataType.BINARY) &&
((dataSize == DataType.GUID.getFixedSize()) ||
((dataSize == -1) && ColumnImpl.isGUIDValue(value))) &&
PropertyMap.GUID_PROP.equalsIgnoreCase(propName));
@@ -454,7 +460,7 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
private PropColumn(DataType type) {
super(null, null, type, 0, 0, 0);
}
@Override
public DatabaseImpl getDatabase() {
return _database;
@@ -487,4 +493,15 @@ public class PropertyMaps implements Iterable<PropertyMapImpl>
}
}
}

/**
* Utility interface for the object which owns the PropertyMaps
*/
static interface Owner {

/**
* Invoked when new properties are saved.
*/
public void propertiesUpdated() throws IOException;
}
}

+ 247
- 240
src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
File diff suppressed because it is too large
View File


+ 140
- 27
src/test/java/com/healthmarketscience/jackcess/PropertiesTest.java View File

@@ -21,9 +21,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static com.healthmarketscience.jackcess.Database.*;
import com.healthmarketscience.jackcess.InvalidValueException;
import com.healthmarketscience.jackcess.impl.DatabaseImpl;
import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
import com.healthmarketscience.jackcess.impl.PropertyMapImpl;
@@ -44,7 +46,7 @@ public class PropertiesTest extends TestCase

public void testPropertyMaps() throws Exception
{
PropertyMaps maps = new PropertyMaps(10, null, null);
PropertyMaps maps = new PropertyMaps(10, null, null, null);
assertTrue(maps.isEmpty());
assertEquals(0, maps.getSize());
assertFalse(maps.iterator().hasNext());
@@ -59,10 +61,10 @@ public class PropertiesTest extends TestCase
assertTrue(colMap.isEmpty());
assertEquals(0, colMap.getSize());
assertFalse(colMap.iterator().hasNext());
assertFalse(maps.isEmpty());
assertEquals(2, maps.getSize());
assertSame(defMap, maps.get(PropertyMaps.DEFAULT_NAME));
assertEquals(PropertyMaps.DEFAULT_NAME, defMap.getName());
assertSame(colMap, maps.get("TESTCOL"));
@@ -97,25 +99,25 @@ public class PropertiesTest extends TestCase
}
}

assertEquals(Arrays.asList(defMap.get("foo"), defMap.get("baz"),
assertEquals(Arrays.asList(defMap.get("foo"), defMap.get("baz"),
colMap.get("buzz")), props);
}

public void testInferTypes() throws Exception
{
PropertyMaps maps = new PropertyMaps(10, null, null);
PropertyMaps maps = new PropertyMaps(10, null, null, null);
PropertyMap defMap = maps.getDefault();

assertEquals(DataType.TEXT,
assertEquals(DataType.TEXT,
defMap.put(PropertyMap.FORMAT_PROP, null).getType());
assertEquals(DataType.BOOLEAN,
assertEquals(DataType.BOOLEAN,
defMap.put(PropertyMap.REQUIRED_PROP, null).getType());

assertEquals(DataType.TEXT,
assertEquals(DataType.TEXT,
defMap.put("strprop", "this is a string").getType());
assertEquals(DataType.BOOLEAN,
assertEquals(DataType.BOOLEAN,
defMap.put("boolprop", true).getType());
assertEquals(DataType.LONG,
assertEquals(DataType.LONG,
defMap.put("intprop", 37).getType());
}

@@ -127,22 +129,22 @@ public class PropertiesTest extends TestCase
Database db = open(testDb);

TableImpl t = (TableImpl)db.getTable("Table1");
assertEquals(t.getTableDefPageNumber(),
assertEquals(t.getTableDefPageNumber(),
t.getPropertyMaps().getObjectId());
PropertyMap tProps = t.getProperties();
assertEquals(PropertyMaps.DEFAULT_NAME, tProps.getName());
int expectedNumProps = 3;
if(db.getFileFormat() != Database.FileFormat.V1997) {
assertEquals("{5A29A676-1145-4D1A-AE47-9F5415CDF2F1}",
assertEquals("{5A29A676-1145-4D1A-AE47-9F5415CDF2F1}",
tProps.getValue(PropertyMap.GUID_PROP));
if(nameMapBytes == null) {
nameMapBytes = (byte[])tProps.getValue("NameMap");
} else {
assertTrue(Arrays.equals(nameMapBytes,
assertTrue(Arrays.equals(nameMapBytes,
(byte[])tProps.getValue("NameMap")));
}
expectedNumProps += 2;
}
}
assertEquals(expectedNumProps, tProps.getSize());
assertEquals((byte)0, tProps.getValue("Orientation"));
assertEquals(Boolean.FALSE, tProps.getValue("OrderByOn"));
@@ -152,7 +154,7 @@ public class PropertiesTest extends TestCase
assertEquals("A", colProps.getName());
expectedNumProps = 9;
if(db.getFileFormat() != Database.FileFormat.V1997) {
assertEquals("{E9EDD90C-CE55-4151-ABE1-A1ACE1007515}",
assertEquals("{E9EDD90C-CE55-4151-ABE1-A1ACE1007515}",
colProps.getValue(PropertyMap.GUID_PROP));
++expectedNumProps;
}
@@ -160,9 +162,9 @@ public class PropertiesTest extends TestCase
assertEquals((short)-1, colProps.getValue("ColumnWidth"));
assertEquals((short)0, colProps.getValue("ColumnOrder"));
assertEquals(Boolean.FALSE, colProps.getValue("ColumnHidden"));
assertEquals(Boolean.FALSE,
assertEquals(Boolean.FALSE,
colProps.getValue(PropertyMap.REQUIRED_PROP));
assertEquals(Boolean.FALSE,
assertEquals(Boolean.FALSE,
colProps.getValue(PropertyMap.ALLOW_ZERO_LEN_PROP));
assertEquals((short)109, colProps.getValue("DisplayControl"));
assertEquals(Boolean.TRUE, colProps.getValue("UnicodeCompression"));
@@ -210,7 +212,8 @@ public class PropertiesTest extends TestCase
for(Row row : ((DatabaseImpl)db).getSystemCatalog()) {
int id = row.getInt("Id");
byte[] propBytes = row.getBytes("LvProp");
PropertyMaps propMaps = ((DatabaseImpl)db).getPropertiesForObject(id);
PropertyMaps propMaps = ((DatabaseImpl)db).getPropertiesForObject(
id, null);
int byteLen = ((propBytes != null) ? propBytes.length : 0);
if(byteLen == 0) {
assertTrue(propMaps.isEmpty());
@@ -251,12 +254,12 @@ public class PropertiesTest extends TestCase

checkProperties(propMap, propMap2);
}
assertFalse(iter.hasNext());
assertFalse(iter2.hasNext());

db.close();
}
}
}

public void testModifyProperties() throws Exception
@@ -333,7 +336,7 @@ public class PropertiesTest extends TestCase

assertTrue((Boolean)cProps.getValue(PropertyMap.REQUIRED_PROP));
assertEquals("42", fProps.getValue(PropertyMap.DEFAULT_VALUE_PROP));
assertNull(dProps.getValue("DisplayControl"));
assertNull(dProps.getValue("DisplayControl"));

cProps.put(PropertyMap.REQUIRED_PROP, DataType.BOOLEAN, false);
fProps.get(PropertyMap.DEFAULT_VALUE_PROP).setValue("0");
@@ -355,7 +358,7 @@ public class PropertiesTest extends TestCase
// weirdo format, no properties
continue;
}
File file = TestUtil.createTempFile(false);
Database db = new DatabaseBuilder(file)
.setFileFormat(ff)
@@ -380,16 +383,16 @@ public class PropertiesTest extends TestCase
db.close();

db = new DatabaseBuilder(file).open();
assertEquals("123", db.getUserDefinedProperties().getValue("testing"));

t = db.getTable("Test");

assertEquals(Boolean.TRUE,
assertEquals(Boolean.TRUE,
t.getProperties().getValue("awesome_table"));

Column c = t.getColumn("id");
assertEquals(Boolean.TRUE,
assertEquals(Boolean.TRUE,
c.getProperties().getValue(PropertyMap.REQUIRED_PROP));
assertEquals("{" + u1.toString().toUpperCase() + "}",
c.getProperties().getValue(PropertyMap.GUID_PROP));
@@ -397,13 +400,123 @@ public class PropertiesTest extends TestCase
c = t.getColumn("data");
assertEquals(Boolean.FALSE,
c.getProperties().getValue(PropertyMap.ALLOW_ZERO_LEN_PROP));
assertEquals("{" + u2.toString().toUpperCase() + "}",
assertEquals("{" + u2.toString().toUpperCase() + "}",
c.getProperties().getValue(PropertyMap.GUID_PROP));

}
}

private static void checkProperties(PropertyMap propMap1,
public void testEnforceProperties() throws Exception
{
for(final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
Database db = create(fileFormat);

Table t = new TableBuilder("testReq")
.addColumn(new ColumnBuilder("id", DataType.LONG)
.setAutoNumber(true)
.putProperty(PropertyMap.REQUIRED_PROP, true))
.addColumn(new ColumnBuilder("value", DataType.TEXT)
.putProperty(PropertyMap.REQUIRED_PROP, true))
.toTable(db);

t.addRow(Column.AUTO_NUMBER, "v1");

try {
t.addRow(Column.AUTO_NUMBER, null);
fail("InvalidValueException should have been thrown");
} catch(InvalidValueException expected) {
// success
}

t.addRow(Column.AUTO_NUMBER, "");

List<? extends Map<String, Object>> expectedRows =
createExpectedTable(
createExpectedRow(
"id", 1,
"value", "v1"),
createExpectedRow(
"id", 2,
"value", ""));
assertTable(expectedRows, t);


t = new TableBuilder("testNz")
.addColumn(new ColumnBuilder("id", DataType.LONG)
.setAutoNumber(true)
.putProperty(PropertyMap.REQUIRED_PROP, true))
.addColumn(new ColumnBuilder("value", DataType.TEXT)
.putProperty(PropertyMap.ALLOW_ZERO_LEN_PROP, false))
.toTable(db);

t.addRow(Column.AUTO_NUMBER, "v1");

try {
t.addRow(Column.AUTO_NUMBER, "");
fail("InvalidValueException should have been thrown");
} catch(InvalidValueException expected) {
// success
}

t.addRow(Column.AUTO_NUMBER, null);

expectedRows =
createExpectedTable(
createExpectedRow(
"id", 1,
"value", "v1"),
createExpectedRow(
"id", 2,
"value", null));
assertTable(expectedRows, t);


t = new TableBuilder("testReqNz")
.addColumn(new ColumnBuilder("id", DataType.LONG)
.setAutoNumber(true)
.putProperty(PropertyMap.REQUIRED_PROP, true))
.addColumn(new ColumnBuilder("value", DataType.TEXT))
.toTable(db);

Column col = t.getColumn("value");
PropertyMap props = col.getProperties();
props.put(PropertyMap.REQUIRED_PROP, true);
props.put(PropertyMap.ALLOW_ZERO_LEN_PROP, false);
props.save();

t.addRow(Column.AUTO_NUMBER, "v1");

try {
t.addRow(Column.AUTO_NUMBER, "");
fail("InvalidValueException should have been thrown");
} catch(InvalidValueException expected) {
// success
}

try {
t.addRow(Column.AUTO_NUMBER, null);
fail("InvalidValueException should have been thrown");
} catch(InvalidValueException expected) {
// success
}

t.addRow(Column.AUTO_NUMBER, "v2");

expectedRows =
createExpectedTable(
createExpectedRow(
"id", 1,
"value", "v1"),
createExpectedRow(
"id", 2,
"value", "v2"));
assertTable(expectedRows, t);

db.close();
}
}

private static void checkProperties(PropertyMap propMap1,
PropertyMap propMap2)
{
assertEquals(propMap1.getSize(), propMap2.getSize());

Loading…
Cancel
Save