From a9607826269664210d4d5040f1b3505074837087 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Tue, 16 Sep 2014 02:34:51 +0000 Subject: [PATCH] fix creation of calculated fields git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@876 f203690c-595d-4dc9-a70b-905162fa7fd2 --- .../jackcess/ColumnBuilder.java | 5 +++ .../jackcess/impl/CalculatedColumnUtil.java | 13 +++++-- .../jackcess/impl/ColumnImpl.java | 24 +++++++++---- .../jackcess/impl/CalcFieldTest.java | 34 ++++++++++++++++--- 4 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java index 628df86..bbfca27 100644 --- a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java +++ b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java @@ -215,6 +215,11 @@ public class ColumnBuilder { return putProperty(PropertyMap.RESULT_TYPE_PROP, getType().getValue()); } + public boolean isVariableLength() { + // calculated columns are written as var len + return(getType().isVariableLength() || isCalculated()); + } + /** * Sets whether of not the new column allows unicode compression. */ diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java b/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java index e548da2..727818a 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java @@ -40,7 +40,9 @@ class CalculatedColumnUtil // offset to the actual data private static final int CALC_DATA_OFFSET = CALC_DATA_LEN_OFFSET + 4; // total amount of extra bytes added for calculated values - private static final int CALC_EXTRA_DATA_LEN = 23; + static final int CALC_EXTRA_DATA_LEN = 23; + // ms access seems to define all fixed-len calc fields as this length + static final short CALC_FIXED_FIELD_LEN = 39; // fully encode calculated BOOLEAN "true" value private static final byte[] CALC_BOOL_TRUE = wrapCalculatedValue( @@ -278,7 +280,8 @@ class CalculatedColumnUtil throws IOException { int totalDataLen = Math.min(CALC_EXTRA_DATA_LEN + 16 + 4, getLength()); - int dataLen = totalDataLen - CALC_EXTRA_DATA_LEN; + // data length must be multiple of 4 + int dataLen = toMul4(totalDataLen - CALC_EXTRA_DATA_LEN); ByteBuffer buffer = prepareWrappedCalcValue(dataLen, order); writeCalcNumericValue(buffer, obj, dataLen); @@ -293,7 +296,7 @@ class CalculatedColumnUtil // numeric bytes need to be a multiple of 4 and we currently handle at // most 16 bytes int numByteLen = ((totalLen > 0) ? totalLen : buffer.remaining()) - 2; - numByteLen = Math.min((numByteLen / 4) * 4, 16); + numByteLen = Math.min(toMul4(numByteLen), 16); byte scale = buffer.get(); boolean negate = (buffer.get() != 0); @@ -371,6 +374,10 @@ class CalculatedColumnUtil ByteUtil.swap8Bytes(bytes, pos); } } + + private static int toMul4(int val) { + return ((val / 4) * 4); + } } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java index 40c8c6c..0fbe7c2 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java @@ -1264,7 +1264,7 @@ public class ColumnImpl implements Column, Comparable { */ private static byte getColumnBitFlags(ColumnBuilder col) { byte flags = UNKNOWN_FLAG_MASK; - if(!col.getType().isVariableLength()) { + if(!col.isVariableLength()) { flags |= FIXED_LEN_FLAG_MASK; } if(col.isAutoNumber()) { @@ -1377,7 +1377,7 @@ public class ColumnImpl implements Column, Comparable { public static short countVariableLength(List columns) { short rtn = 0; for (ColumnBuilder col : columns) { - if (col.getType().isVariableLength()) { + if (col.isVariableLength()) { rtn++; } } @@ -1390,10 +1390,10 @@ public class ColumnImpl implements Column, Comparable { * found in the list * @usage _advanced_method_ */ - public static short countNonLongVariableLength(List columns) { + private static short countNonLongVariableLength(List columns) { short rtn = 0; for (ColumnBuilder col : columns) { - if (col.getType().isVariableLength() && !col.getType().isLongValue()) { + if (col.isVariableLength() && !col.getType().isLongValue()) { rtn++; } } @@ -1572,7 +1572,7 @@ public class ColumnImpl implements Column, Comparable { buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER); //constant magic number buffer.putShort(col.getColumnNumber()); //Column Number - if (col.getType().isVariableLength()) { + if(col.isVariableLength()) { if(!col.getType().isLongValue()) { buffer.putShort(variableOffset++); } else { @@ -1614,7 +1614,7 @@ public class ColumnImpl implements Column, Comparable { buffer.putInt(0); //Unknown, but always 0. //Offset for fixed length columns - if (col.getType().isVariableLength()) { + if(col.isVariableLength()) { buffer.putShort((short) 0); } else { buffer.putShort(fixedOffset); @@ -1622,7 +1622,17 @@ public class ColumnImpl implements Column, Comparable { } if(!col.getType().isLongValue()) { - buffer.putShort(col.getLength()); //Column length + short length = col.getLength(); + if(col.isCalculated()) { + // calced columns have additional value overhead + if(!col.getType().isVariableLength() || + col.getType().getHasScalePrecision()) { + length = CalculatedColumnUtil.CALC_FIXED_FIELD_LEN; + } else { + length += CalculatedColumnUtil.CALC_EXTRA_DATA_LEN; + } + } + buffer.putShort(length); //Column length } else { buffer.putShort((short)0x0000); // unused } diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/CalcFieldTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/CalcFieldTest.java index d230cba..26b2390 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/CalcFieldTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/CalcFieldTest.java @@ -20,6 +20,7 @@ USA package com.healthmarketscience.jackcess.impl; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -75,11 +76,19 @@ public class CalcFieldTest extends TestCase .addColumn(new ColumnBuilder("id", DataType.LONG) .setAutoNumber(true)) .addColumn(new ColumnBuilder("data", DataType.TEXT)) - .addColumn(new ColumnBuilder("calc_data", DataType.TEXT) + .addColumn(new ColumnBuilder("calc_text", DataType.TEXT) .setCalculatedInfo("[id] & \"_\" & [data]")) + .addColumn(new ColumnBuilder("calc_memo", DataType.MEMO) + .setCalculatedInfo("[id] & \"_\" & [data]")) + .addColumn(new ColumnBuilder("calc_bool", DataType.BOOLEAN) + .setCalculatedInfo("[id] > 0")) + .addColumn(new ColumnBuilder("calc_long", DataType.LONG) + .setCalculatedInfo("[id] + 1")) + .addColumn(new ColumnBuilder("calc_numeric", DataType.NUMERIC) + .setCalculatedInfo("[id] / 0.03")) .toTable(db); - Column col = t.getColumn("calc_data"); + Column col = t.getColumn("calc_text"); assertTrue(col.isCalculated()); assertEquals("[id] & \"_\" & [data]", col.getProperties().getValue( PropertyMap.EXPRESSION_PROP)); @@ -87,14 +96,31 @@ public class CalcFieldTest extends TestCase col.getProperties().getValue( PropertyMap.RESULT_TYPE_PROP)); - t.addRow(Column.AUTO_NUMBER, "foo", "1_foo"); + String longStr = createString(1000); + BigDecimal bd1 = new BigDecimal("-1234.5678"); + BigDecimal bd2 = new BigDecimal("0.0234"); + + t.addRow(Column.AUTO_NUMBER, "foo", "1_foo", longStr, true, 2, bd1); + t.addRow(Column.AUTO_NUMBER, "bar", "2_bar", longStr, false, -37, bd2); List> expectedRows = createExpectedTable( createExpectedRow( "id", 1, "data", "foo", - "calc_data", "1_foo")); + "calc_text", "1_foo", + "calc_memo", longStr, + "calc_bool", true, + "calc_long", 2, + "calc_numeric", bd1), + createExpectedRow( + "id", 2, + "data", "bar", + "calc_text", "2_bar", + "calc_memo", longStr, + "calc_bool", false, + "calc_long", -37, + "calc_numeric", bd2)); assertTable(expectedRows, t); -- 2.39.5