summaryrefslogtreecommitdiffstats
path: root/src/java/com/healthmarketscience
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2008-03-08 05:00:43 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2008-03-08 05:00:43 +0000
commitacc18a5b2de91ee966e3a288d0b1914bd7cb324a (patch)
treee18d42079a729f96edecb38c5b31fcefb5559a6f /src/java/com/healthmarketscience
parent44b2ba7fdbfffe928b5d26baa0733ff019e6740c (diff)
downloadjackcess-acc18a5b2de91ee966e3a288d0b1914bd7cb324a.tar.gz
jackcess-acc18a5b2de91ee966e3a288d0b1914bd7cb324a.zip
more fixes for index writing
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@253 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src/java/com/healthmarketscience')
-rw-r--r--src/java/com/healthmarketscience/jackcess/Column.java54
-rw-r--r--src/java/com/healthmarketscience/jackcess/Index.java178
2 files changed, 165 insertions, 67 deletions
diff --git a/src/java/com/healthmarketscience/jackcess/Column.java b/src/java/com/healthmarketscience/jackcess/Column.java
index 1912787..c262b84 100644
--- a/src/java/com/healthmarketscience/jackcess/Column.java
+++ b/src/java/com/healthmarketscience/jackcess/Column.java
@@ -28,6 +28,7 @@ King of Prussia, PA 19406
package com.healthmarketscience.jackcess;
import java.io.IOException;
+import java.io.ObjectStreamException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
@@ -613,12 +614,14 @@ public class Column implements Comparable<Column> {
// convert to unscaled BigInteger, big-endian bytes
byte[] intValBytes = decVal.unscaledValue().toByteArray();
- if(intValBytes.length > 16) {
+ int maxByteLen = getType().getFixedSize() - 1;
+ if(intValBytes.length > maxByteLen) {
throw new IOException("Too many bytes for valid BigInteger?");
}
- if(intValBytes.length < 16) {
- byte[] tmpBytes = new byte[16];
- System.arraycopy(intValBytes, 0, tmpBytes, (16 - intValBytes.length),
+ if(intValBytes.length < maxByteLen) {
+ byte[] tmpBytes = new byte[maxByteLen];
+ System.arraycopy(intValBytes, 0, tmpBytes,
+ (maxByteLen - intValBytes.length),
intValBytes.length);
intValBytes = tmpBytes;
}
@@ -639,10 +642,12 @@ public class Column implements Comparable<Column> {
{
// seems access stores dates in the local timezone. guess you just hope
// you read it in the same timezone in which it was written!
- long time = (long)(buffer.getDouble() * MILLISECONDS_PER_DAY);
+ long dateBits = buffer.getLong();
+ long time = (long)(Double.longBitsToDouble(dateBits)
+ * MILLISECONDS_PER_DAY);
time -= MILLIS_BETWEEN_EPOCH_AND_1900;
time -= getTimeZoneOffset(time);
- return new Date(time);
+ return new DateExt(time, dateBits);
}
/**
@@ -652,7 +657,14 @@ public class Column implements Comparable<Column> {
{
if(value == null) {
buffer.putDouble(0d);
+ } if(value instanceof DateExt) {
+
+ // this is a Date value previously read from readDateValue(). use the
+ // original bits to store the value so we don't lose any precision
+ buffer.putLong(((DateExt)value).getDateBits());
+
} else {
+
// seems access stores dates in the local timezone. guess you just
// hope you read it in the same timezone in which it was written!
long time = ((value instanceof Date) ?
@@ -883,7 +895,8 @@ public class Column implements Comparable<Column> {
switch(getType()) {
case NUMERIC:
// don't ask me why numerics are "var length" columns...
- ByteBuffer buffer = getPageChannel().createBuffer(getLength(), order);
+ ByteBuffer buffer = getPageChannel().createBuffer(
+ getType().getFixedSize(), order);
writeNumericValue(buffer, obj);
buffer.flip();
return buffer;
@@ -1230,4 +1243,31 @@ public class Column implements Comparable<Column> {
return obj;
}
+ /**
+ * Date subclass which stashes the original date bits, in case we attempt to
+ * re-write the value (will not lose precision).
+ */
+ private static final class DateExt extends Date
+ {
+ private static final long serialVersionUID = 0L;
+
+ /** cached bits of the original date value */
+ private transient final long _dateBits;
+
+ private DateExt(long time, long dateBits) {
+ super(time);
+ _dateBits = dateBits;
+ }
+
+ public long getDateBits() {
+ return _dateBits;
+ }
+
+ private Object writeReplace() throws ObjectStreamException {
+ // if we are going to serialize this Date, convert it back to a normal
+ // Date (in case it is restored outside of the context of jackcess)
+ return new Date(super.getTime());
+ }
+ }
+
}
diff --git a/src/java/com/healthmarketscience/jackcess/Index.java b/src/java/com/healthmarketscience/jackcess/Index.java
index eb57194..a4fc26b 100644
--- a/src/java/com/healthmarketscience/jackcess/Index.java
+++ b/src/java/com/healthmarketscience/jackcess/Index.java
@@ -45,6 +45,7 @@ import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import com.healthmarketscience.jackcess.Index.ColumnDescriptor;
import static com.healthmarketscience.jackcess.IndexCodes.*;
@@ -779,41 +780,6 @@ public class Index implements Comparable<Index> {
}
/**
- * Determines if the given column is a text based column.
- */
- private static boolean isTextColumn(Column col) {
- return((col.getType() == DataType.TEXT) ||
- (col.getType() == DataType.MEMO));
- }
-
- /**
- * Determines if the given column is a boolean column.
- */
- private static boolean isBooleanColumn(Column col) {
- return(col.getType() == DataType.BOOLEAN);
- }
-
- /**
- * Determines if the given column is a integer based column.
- */
- private static boolean isIntegerColumn(Column col) {
- return((col.getType() == DataType.BYTE) ||
- (col.getType() == DataType.INT) ||
- (col.getType() == DataType.LONG));
- }
-
- /**
- * Determines if the given column is a floating point based column.
- */
- private static boolean isFloatingPointColumn(Column col) {
- return((col.getType() == DataType.NUMERIC) ||
- (col.getType() == DataType.MONEY) ||
- (col.getType() == DataType.FLOAT) ||
- (col.getType() == DataType.DOUBLE) ||
- (col.getType() == DataType.SHORT_DATE_TIME));
- }
-
- /**
* Flips the first bit in the byte at the given index.
*/
private static byte[] flipFirstBitInByte(byte[] value, int index)
@@ -842,15 +808,6 @@ public class Index implements Comparable<Index> {
// always write in big endian order
return column.write(value, 0, ByteOrder.BIG_ENDIAN).array();
}
-
- /**
- * Updates the given array as appropriate for the given index order and
- * returns it.
- */
- private static byte[] handleOrder(byte[] value, boolean isAscending) {
- // descending order is achieved by negating all the bits
- return (isAscending ? value : flipBytes(value));
- }
/**
* Converts an index value for a text column into the entry value (which
@@ -1010,18 +967,30 @@ public class Index implements Comparable<Index> {
private ColumnDescriptor newColumnDescriptor(Column col, byte flags)
throws IOException
{
- if(isTextColumn(col)) {
+ switch(col.getType()) {
+ case TEXT:
+ case MEMO:
return new TextColumnDescriptor(col, flags);
- } else if(isIntegerColumn(col)) {
+ case INT:
+ case LONG:
+ case MONEY:
return new IntegerColumnDescriptor(col, flags);
- } else if(isFloatingPointColumn(col)) {
+ case FLOAT:
+ case DOUBLE:
+ case SHORT_DATE_TIME:
return new FloatingPointColumnDescriptor(col, flags);
- } else if(isBooleanColumn(col)) {
+ case NUMERIC:
+ return new FixedPointColumnDescriptor(col, flags);
+ case BYTE:
+ return new ByteColumnDescriptor(col, flags);
+ case BOOLEAN:
return new BooleanColumnDescriptor(col, flags);
+
+ default:
+ // FIXME we can't modify this index at this point in time
+ _readOnly = true;
+ return new ReadOnlyColumnDescriptor(col, flags);
}
- // FIXME we can't modify this index at this point in time
- _readOnly = true;
- return new ReadOnlyColumnDescriptor(col, flags);
}
@@ -1106,11 +1075,18 @@ public class Index implements Comparable<Index> {
Object value, ByteArrayOutputStream bout)
throws IOException
{
- bout.write(
- handleOrder(
- flipFirstBitInByte(
- encodeNumberColumnValue(value, getColumn()), 0),
- isAscending()));
+ byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
+
+ // bit twiddling rules:
+ // - isAsc => flipFirstBit
+ // - !isAsc => flipFirstBit, flipBytes
+
+ flipFirstBitInByte(valueBytes, 0);
+ if(!isAscending()) {
+ flipBytes(valueBytes);
+ }
+
+ bout.write(valueBytes);
}
}
@@ -1132,12 +1108,94 @@ public class Index implements Comparable<Index> {
throws IOException
{
byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
- // if the number is negative, the first bit is set. in this case, we
- // flip all the bits
- if((valueBytes[0] & 0x80) != 0) {
+
+ // determine if the number is negative by testing if the first bit is
+ // set
+ boolean isNegative = ((valueBytes[0] & 0x80) != 0);
+
+ // bit twiddling rules:
+ // isAsc && !isNeg => flipFirstBit
+ // isAsc && isNeg => flipBytes
+ // !isAsc && !isNeg => flipFirstBit, flipBytes
+ // !isAsc && isNeg => nothing
+
+ if(!isNegative) {
+ flipFirstBitInByte(valueBytes, 0);
+ }
+ if(isNegative == isAscending()) {
flipBytes(valueBytes);
}
- bout.write(handleOrder(valueBytes, isAscending()));
+
+ bout.write(valueBytes);
+ }
+ }
+
+ /**
+ * ColumnDescriptor for fixed point based columns.
+ */
+ private static final class FixedPointColumnDescriptor
+ extends ColumnDescriptor
+ {
+ private FixedPointColumnDescriptor(Column column, byte flags)
+ throws IOException
+ {
+ super(column, flags);
+ }
+
+ @Override
+ protected void writeNonNullValue(
+ Object value, ByteArrayOutputStream bout)
+ throws IOException
+ {
+ byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
+
+ // determine if the number is negative by testing if the first bit is
+ // set
+ boolean isNegative = ((valueBytes[0] & 0x80) != 0);
+
+ // bit twiddling rules:
+ // isAsc && !isNeg => setReverseSignByte
+ // isAsc && isNeg => flipBytes, setReverseSignByte
+ // !isAsc && !isNeg => flipBytes, setReverseSignByte
+ // !isAsc && isNeg => setReverseSignByte
+
+ if(isNegative == isAscending()) {
+ flipBytes(valueBytes);
+ }
+
+ // reverse the sign byte (after any previous byte flipping)
+ valueBytes[0] = (isNegative ? (byte)0x00 : (byte)0xFF);
+
+ bout.write(valueBytes);
+ }
+ }
+
+ /**
+ * ColumnDescriptor for byte based columns.
+ */
+ private static final class ByteColumnDescriptor extends ColumnDescriptor
+ {
+ private ByteColumnDescriptor(Column column, byte flags)
+ throws IOException
+ {
+ super(column, flags);
+ }
+
+ @Override
+ protected void writeNonNullValue(
+ Object value, ByteArrayOutputStream bout)
+ throws IOException
+ {
+ byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
+
+ // bit twiddling rules:
+ // - isAsc => nothing
+ // - !isAsc => flipBytes
+ if(!isAscending()) {
+ flipBytes(valueBytes);
+ }
+
+ bout.write(valueBytes);
}
}