From e6f3acefd378d699fb4188226f0e722c235f3942 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Tue, 5 Apr 2011 04:31:16 +0000 Subject: [PATCH] handle different versions of general sort order correctly git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@546 f203690c-595d-4dc9-a70b-905162fa7fd2 --- .../healthmarketscience/jackcess/Column.java | 122 +++++++++++++++--- .../jackcess/Database.java | 7 +- .../jackcess/IndexData.java | 16 ++- .../jackcess/JetFormat.java | 22 ++-- .../jackcess/DatabaseTest.java | 3 +- 5 files changed, 132 insertions(+), 38 deletions(-) diff --git a/src/java/com/healthmarketscience/jackcess/Column.java b/src/java/com/healthmarketscience/jackcess/Column.java index bb0ffd4..420c581 100644 --- a/src/java/com/healthmarketscience/jackcess/Column.java +++ b/src/java/com/healthmarketscience/jackcess/Column.java @@ -129,8 +129,16 @@ public class Column implements Comparable { // 0x10: replication related field (or hidden?) // 0x80: hyperlink (some memo based thing) - /** the "general" text sort order */ - public static final short GENERAL_SORT_ORDER = 1033; + /** the value for the "general" sort order */ + private static final short GENERAL_SORT_ORDER_VALUE = 1033; + + /** the "general" text sort order, legacy version (access 2000-2007) */ + public static final SortOrder GENERAL_LEGACY_SORT_ORDER = + new SortOrder(GENERAL_SORT_ORDER_VALUE, (byte)0); + + /** the "general" text sort order, latest version (access 2010+) */ + public static final SortOrder GENERAL_SORT_ORDER = + new SortOrder(GENERAL_SORT_ORDER_VALUE, (byte)1); /** pattern matching textual guid strings (allows for optional surrounding '{' and '}') */ @@ -169,7 +177,7 @@ public class Column implements Comparable { /** the index of the variable length data in the var len offset table */ private int _varLenTableIndex; /** the collating sort order for a text field */ - private short _textSortOrder = GENERAL_SORT_ORDER; + private SortOrder _textSortOrder; /** the auto number generator for this column (if autonumber column) */ private AutoNumberGenerator _autoNumberGenerator; /** properties for this column, if any */ @@ -215,12 +223,8 @@ public class Column implements Comparable { _scale = buffer.get(offset + getFormat().OFFSET_COLUMN_SCALE); } else if(_type.isTextual()) { // co-located w/ precision/scale - _textSortOrder = buffer.getShort( - offset + getFormat().OFFSET_COLUMN_PRECISION); - if(_textSortOrder == 0) { - // probably a file we wrote, before handling sort order - _textSortOrder = GENERAL_SORT_ORDER; - } + _textSortOrder = readSortOrder( + buffer, offset + getFormat().OFFSET_COLUMN_PRECISION, getFormat()); } byte flags = buffer.get(offset + getFormat().OFFSET_COLUMN_FLAGS); _variableLength = ((flags & FIXED_LEN_FLAG_MASK) == 0); @@ -355,11 +359,11 @@ public class Column implements Comparable { _scale = newScale; } - public short getTextSortOrder() { + public SortOrder getTextSortOrder() { return _textSortOrder; } - public void setTextSortOrder(short newTextSortOrder) { + public void setTextSortOrder(SortOrder newTextSortOrder) { _textSortOrder = newTextSortOrder; } @@ -1679,16 +1683,19 @@ public class Column implements Comparable { buffer.putShort((short) 0); } buffer.putShort(columnNumber); //Column Number again - if(col.getType().getHasScalePrecision()) { - buffer.put(col.getPrecision()); // numeric precision - buffer.put(col.getScale()); // numeric scale - } else if(col.getType().isTextual()) { - buffer.putShort(col.getTextSortOrder()); + if(col.getType().isTextual()) { + // this will write 4 bytes + writeSortOrder(buffer, col._textSortOrder, format); } else { - buffer.put((byte) 0x00); //unused - buffer.put((byte) 0x00); //unused + if(col.getType().getHasScalePrecision()) { + buffer.put(col.getPrecision()); // numeric precision + buffer.put(col.getScale()); // numeric scale + } else { + buffer.put((byte) 0x00); //unused + buffer.put((byte) 0x00); //unused + } + buffer.putShort((short) 0); //Unknown } - buffer.putShort((short) 0); //Unknown buffer.put(getColumnBitFlags(col)); // misc col flags if (col.isCompressedUnicode()) { //Compressed buffer.put((byte) 1); @@ -1733,6 +1740,43 @@ public class Column implements Comparable { return flags; } + /** + * Reads the sort order info from the given buffer from the given position. + */ + static SortOrder readSortOrder(ByteBuffer buffer, int position, + JetFormat format) + { + short value = buffer.getShort(position); + byte version = buffer.get(position + 3); + if(value == 0) { + // probably a file we wrote, before handling sort order + return format.DEFAULT_SORT_ORDER; + } + + if(value == GENERAL_SORT_ORDER_VALUE) { + if(version == GENERAL_LEGACY_SORT_ORDER.getVersion()) { + return GENERAL_LEGACY_SORT_ORDER; + } + if(version == GENERAL_SORT_ORDER.getVersion()) { + return GENERAL_SORT_ORDER; + } + } + return new SortOrder(value, version); + } + + /** + * Writes the sort order info to the given buffer at the current position. + */ + private static void writeSortOrder(ByteBuffer buffer, SortOrder sortOrder, + JetFormat format) { + if(sortOrder == null) { + sortOrder = format.DEFAULT_SORT_ORDER; + } + buffer.putShort(sortOrder.getValue()); + buffer.put((byte)0x00); // unknown + buffer.put(sortOrder.getVersion()); + } + /** * Date subclass which stashes the original date bits, in case we attempt to * re-write the value (will not lose precision). @@ -1877,4 +1921,44 @@ public class Column implements Comparable { } } + /** + * Information about the sort order (collation) for a textual column. + */ + public static final class SortOrder + { + private final short _value; + private final byte _version; + + public SortOrder(short value, byte version) { + _value = value; + _version = version; + } + + public short getValue() { + return _value; + } + + public byte getVersion() { + return _version; + } + + @Override + public int hashCode() { + return _value; + } + + @Override + public boolean equals(Object o) { + return ((this == o) || + ((o != null) && (getClass() == o.getClass()) && + (_value == ((SortOrder)o)._value) && + (_version == ((SortOrder)o)._version))); + } + + @Override + public String toString() { + return _value + "(" + _version + ")"; + } + } + } diff --git a/src/java/com/healthmarketscience/jackcess/Database.java b/src/java/com/healthmarketscience/jackcess/Database.java index de76b01..5c38561 100644 --- a/src/java/com/healthmarketscience/jackcess/Database.java +++ b/src/java/com/healthmarketscience/jackcess/Database.java @@ -399,7 +399,7 @@ public class Database /** timezone to use when handling dates */ private TimeZone _timeZone; /** language sort order to be used for textual columns */ - private Short _defaultSortOrder; + private Column.SortOrder _defaultSortOrder; /** the ordering used for table columns */ private Table.ColumnOrder _columnOrder; /** cache of in-use tables */ @@ -893,11 +893,12 @@ public class Database * @return the currently configured database default language sort order for * textual columns */ - public short getDefaultSortOrder() throws IOException { + public Column.SortOrder getDefaultSortOrder() throws IOException { if(_defaultSortOrder == null) { _pageChannel.readPage(_buffer, 0); - _defaultSortOrder = _buffer.getShort(_format.OFFSET_SORT_ORDER); + _defaultSortOrder = Column.readSortOrder( + _buffer, _format.OFFSET_SORT_ORDER, _format); } return _defaultSortOrder; } diff --git a/src/java/com/healthmarketscience/jackcess/IndexData.java b/src/java/com/healthmarketscience/jackcess/IndexData.java index 1540be7..245bd84 100644 --- a/src/java/com/healthmarketscience/jackcess/IndexData.java +++ b/src/java/com/healthmarketscience/jackcess/IndexData.java @@ -1179,14 +1179,16 @@ public abstract class IndexData { switch(col.getType()) { case TEXT: case MEMO: - if(col.getTextSortOrder() != Column.GENERAL_SORT_ORDER) { - // unsupported sort order - setReadOnly(); - return new ReadOnlyColumnDescriptor(col, flags); + Column.SortOrder sortOrder = col.getTextSortOrder(); + if(Column.GENERAL_LEGACY_SORT_ORDER.equals(sortOrder)) { + return new GenLegTextColumnDescriptor(col, flags); } - return(col.getFormat().LEGACY_TEXT_INDEXES ? - new GenLegTextColumnDescriptor(col, flags) : - new GenTextColumnDescriptor(col, flags)); + if(Column.GENERAL_SORT_ORDER.equals(sortOrder)) { + return new GenTextColumnDescriptor(col, flags); + } + // unsupported sort order + setReadOnly(); + return new ReadOnlyColumnDescriptor(col, flags); case INT: case LONG: case MONEY: diff --git a/src/java/com/healthmarketscience/jackcess/JetFormat.java b/src/java/com/healthmarketscience/jackcess/JetFormat.java index 0ce73ee..173d96c 100644 --- a/src/java/com/healthmarketscience/jackcess/JetFormat.java +++ b/src/java/com/healthmarketscience/jackcess/JetFormat.java @@ -250,9 +250,9 @@ public abstract class JetFormat { public final int MAX_INDEX_NAME_LENGTH; public final boolean LEGACY_NUMERIC_INDEXES; - public final boolean LEGACY_TEXT_INDEXES; public final Charset CHARSET; + public final Column.SortOrder DEFAULT_SORT_ORDER; /** * @param channel the database file. @@ -377,9 +377,9 @@ public abstract class JetFormat { MAX_INDEX_NAME_LENGTH = defineMaxIndexNameLength(); LEGACY_NUMERIC_INDEXES = defineLegacyNumericIndexes(); - LEGACY_TEXT_INDEXES = defineLegacyTextIndexes(); CHARSET = defineCharset(); + DEFAULT_SORT_ORDER = defineDefaultSortOrder(); } protected abstract boolean defineReadOnly(); @@ -472,9 +472,9 @@ public abstract class JetFormat { protected abstract int defineMaxIndexNameLength(); protected abstract Charset defineCharset(); + protected abstract Column.SortOrder defineDefaultSortOrder(); protected abstract boolean defineLegacyNumericIndexes(); - protected abstract boolean defineLegacyTextIndexes(); protected abstract Map getPossibleFileFormats(); @@ -668,10 +668,12 @@ public abstract class JetFormat { protected boolean defineLegacyNumericIndexes() { return true; } @Override - protected boolean defineLegacyTextIndexes() { return true; } + protected Charset defineCharset() { return Charset.defaultCharset(); } @Override - protected Charset defineCharset() { return Charset.defaultCharset(); } + protected Column.SortOrder defineDefaultSortOrder() { + return Column.GENERAL_LEGACY_SORT_ORDER; + } @Override protected Map getPossibleFileFormats() @@ -868,10 +870,12 @@ public abstract class JetFormat { protected boolean defineLegacyNumericIndexes() { return true; } @Override - protected boolean defineLegacyTextIndexes() { return true; } + protected Charset defineCharset() { return Charset.forName("UTF-16LE"); } @Override - protected Charset defineCharset() { return Charset.forName("UTF-16LE"); } + protected Column.SortOrder defineDefaultSortOrder() { + return Column.GENERAL_LEGACY_SORT_ORDER; + } @Override protected Map getPossibleFileFormats() @@ -923,7 +927,9 @@ public abstract class JetFormat { } @Override - protected boolean defineLegacyTextIndexes() { return false; } + protected Column.SortOrder defineDefaultSortOrder() { + return Column.GENERAL_SORT_ORDER; + } @Override protected Map getPossibleFileFormats() { diff --git a/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java b/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java index 7c1dbe3..52c7b60 100644 --- a/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java @@ -1136,7 +1136,8 @@ public class DatabaseTest extends TestCase { for (final TestDB testDB : SUPPORTED_DBS_TEST_FOR_READ) { Database db = open(testDB); - assertEquals(Column.GENERAL_SORT_ORDER, db.getDefaultSortOrder()); + assertEquals(db.getFormat().DEFAULT_SORT_ORDER, + db.getDefaultSortOrder()); db.close(); } } -- 2.39.5