]> source.dussan.org Git - jackcess.git/commitdiff
handle different versions of general sort order correctly
authorJames Ahlborn <jtahlborn@yahoo.com>
Tue, 5 Apr 2011 04:31:16 +0000 (04:31 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Tue, 5 Apr 2011 04:31:16 +0000 (04:31 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@546 f203690c-595d-4dc9-a70b-905162fa7fd2

src/java/com/healthmarketscience/jackcess/Column.java
src/java/com/healthmarketscience/jackcess/Database.java
src/java/com/healthmarketscience/jackcess/IndexData.java
src/java/com/healthmarketscience/jackcess/JetFormat.java
test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java

index bb0ffd493ca5bc6e0bd4d8f69d5cd4a7b1585e49..420c581cbea0d71e047eae69495e6ac924a0de4e 100644 (file)
@@ -129,8 +129,16 @@ public class Column implements Comparable<Column> {
   // 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<Column> {
   /** 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<Column> {
       _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<Column> {
     _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<Column> {
         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<Column> {
     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<Column> {
     }
   }
 
+  /**
+   * 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 + ")";
+    }
+  }
+
 }
index de76b01632f77f210f57a765e98bce85e3eeaabe..5c38561457d8af2aac1c9e0111018cfbb3481282 100644 (file)
@@ -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;
   }
index 1540be77c213a0d51b7105d8aa9558421880cb2c..245bd8424d76da352867266670d0cb4a9e3e7d6e 100644 (file)
@@ -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:
index 0ce73ee10e0b636ecec034154d9d41afa700f325..173d96c41cbc1685a3b1607e960c98f505ba6657 100644 (file)
@@ -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<String,Database.FileFormat> 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<String,Database.FileFormat> 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<String,Database.FileFormat> 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<String,Database.FileFormat> getPossibleFileFormats() {
index 7c1dbe3bbc651375c2effbbf8a1a7b632efae82d..52c7b602785f19f9c5c5ea10363e7e8ce82068c4 100644 (file)
@@ -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();
     }
   }