]> source.dussan.org Git - jackcess.git/commitdiff
refactor ColumnImpl into multiple type specific sub-classes
authorJames Ahlborn <jtahlborn@yahoo.com>
Thu, 7 Aug 2014 03:40:00 +0000 (03:40 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Thu, 7 Aug 2014 03:40:00 +0000 (03:40 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@865 f203690c-595d-4dc9-a70b-905162fa7fd2

src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java
src/main/java/com/healthmarketscience/jackcess/DataType.java
src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnImpl.java [new file with mode: 0644]
src/main/java/com/healthmarketscience/jackcess/impl/LongValueColumnImpl.java [new file with mode: 0644]
src/main/java/com/healthmarketscience/jackcess/impl/MemoColumnImpl.java [new file with mode: 0644]
src/main/java/com/healthmarketscience/jackcess/impl/NumericColumnImpl.java [new file with mode: 0644]
src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
src/main/java/com/healthmarketscience/jackcess/impl/TextColumnImpl.java [new file with mode: 0644]
src/main/java/com/healthmarketscience/jackcess/impl/UnsupportedColumnImpl.java [new file with mode: 0644]

index f6b3cdc9774be8a5f5ef20dc5e453927d8ac36e2..dc0b5662346874b9fe32761dad85fbc9a5029cc5 100644 (file)
@@ -119,8 +119,7 @@ public class ColumnBuilder {
   }
 
   public byte getPrecision() {
-    return ((_precision != null) ? _precision :
-            (byte)(_type.getHasScalePrecision() ? _type.getDefaultPrecision() : 0));
+    return ((_precision != null) ? _precision : (byte)_type.getDefaultPrecision());
   }
 
   /**
@@ -132,8 +131,7 @@ public class ColumnBuilder {
   }
 
   public byte getScale() {
-    return ((_scale != null) ? _scale :
-            (byte)(_type.getHasScalePrecision() ? _type.getDefaultScale() : 0));
+    return ((_scale != null) ? _scale : (byte)_type.getDefaultScale());
   }
 
   /**
@@ -147,7 +145,7 @@ public class ColumnBuilder {
   public short getLength() {
     return ((_length != null) ? _length :
             (short)(!_type.isVariableLength() ? _type.getFixedSize() :
-                    (!_type.isLongValue() ? _type.getDefaultSize() : 0)));
+                    _type.getDefaultSize()));
   }
 
   /**
index a1ccbd2492c23f217259332ed392c114f047ceaa..3abe78acd0b631103fe085aa6128bf1815487ec1 100644 (file)
@@ -114,13 +114,13 @@ public enum DataType {
    */
   TEXT((byte) 0x0A, Types.VARCHAR, null, true, false, 0,
        50 * JetFormat.TEXT_FIELD_UNIT_SIZE,
-       (int)JetFormat.TEXT_FIELD_MAX_LENGTH, JetFormat.TEXT_FIELD_UNIT_SIZE),
+       JetFormat.TEXT_FIELD_MAX_LENGTH, JetFormat.TEXT_FIELD_UNIT_SIZE),
   /**
    * Corresponds to a java {@code byte[]} of max length 16777215 bytes.
    * Accepts a {@code byte[]}, or {@code null}.  Equivalent to SQL
    * {@link Types#LONGVARBINARY}, {@link Types#BLOB}.
    */
-  OLE((byte) 0x0B, Types.LONGVARBINARY, null, true, true, 0, null, 0x3FFFFFFF,
+  OLE((byte) 0x0B, Types.LONGVARBINARY, null, true, true, 0, 0, 0x3FFFFFFF,
       1),
   /**
    * Corresponds to a java {@link String} of max length 8388607 chars.
@@ -128,7 +128,7 @@ public enum DataType {
    * {@code null}.  Equivalent to SQL {@link Types#LONGVARCHAR}, {@link
    * Types#CLOB}.
    */
-  MEMO((byte) 0x0C, Types.LONGVARCHAR, null, true, true, 0, null, 0x3FFFFFFF,
+  MEMO((byte) 0x0C, Types.LONGVARCHAR, null, true, true, 0, 0, 0x3FFFFFFF,
        JetFormat.TEXT_FIELD_UNIT_SIZE),
   /**
    * Unknown data.  Handled like {@link #BINARY}.
@@ -170,13 +170,15 @@ public enum DataType {
    * Placeholder type for a variable length type which is not currently
    * supported.  Handled like {@link #BINARY}.
    */
-  UNSUPPORTED_VARLEN((byte) 0xFF, null, null, true, false, 0, null, 0x3FFFFFFF,
+  UNSUPPORTED_VARLEN((byte) 0xFF, null, null, true, false, 0, 0, 0x3FFFFFFF,
       1);
 
   /** Map of SQL types to Access data types */
-  private static Map<Integer, DataType> SQL_TYPES = new HashMap<Integer, DataType>();
+  private static final Map<Integer, DataType> SQL_TYPES =
+    new HashMap<Integer, DataType>();
   /** Alternate map of SQL types to Access data types */
-  private static Map<Integer, DataType> ALT_SQL_TYPES = new HashMap<Integer, DataType>();
+  private static final Map<Integer, DataType> ALT_SQL_TYPES =
+    new HashMap<Integer, DataType>();
   static {
     for (DataType type : DataType.values()) {
       if (type._sqlType != null) {
@@ -210,71 +212,71 @@ public enum DataType {
   }
 
   /** is this a variable length field */
-  private boolean _variableLength;
+  private final boolean _variableLength;
   /** is this a long value field */
-  private boolean _longValue;
+  private final boolean _longValue;
   /** does this field have scale/precision */
-  private boolean _hasScalePrecision;
+  private final boolean _hasScalePrecision;
   /** Internal Access value */
-  private byte _value;
+  private final byte _value;
   /** Size in bytes of fixed length columns */
-  private Integer _fixedSize;
+  private final Integer _fixedSize;
   /** min in bytes size for var length columns */
-  private Integer _minSize;
+  private final int _minSize;
   /** default size in bytes for var length columns */
-  private Integer _defaultSize;
+  private final int _defaultSize;
   /** Max size in bytes for var length columns */
-  private Integer _maxSize;
+  private final int _maxSize;
   /** SQL type equivalent, or null if none defined */
-  private Integer _sqlType;
+  private final Integer _sqlType;
   /** min scale value */
-  private Integer _minScale;
+  private final int _minScale;
   /** the default scale value */
-  private Integer _defaultScale;
+  private final int _defaultScale;
   /** max scale value */
-  private Integer _maxScale;
+  private final int _maxScale;
   /** min precision value */
-  private Integer _minPrecision;
+  private final int _minPrecision;
   /** the default precision value */
-  private Integer _defaultPrecision;
+  private final int _defaultPrecision;
   /** max precision value */
-  private Integer _maxPrecision;
+  private final int _maxPrecision;
   /** the number of bytes per "unit" for this data type */
-  private int _unitSize;
+  private final int _unitSize;
   
   private DataType(byte value) {
     this(value, null, null);
   }
   
   private DataType(byte value, Integer sqlType, Integer fixedSize) {
-    this(value, sqlType, fixedSize, false, false, null, null, null, 1);
+    this(value, sqlType, fixedSize, false, false, 0, 0, 0, 1);
   }
 
   private DataType(byte value, Integer sqlType, Integer fixedSize,
                    boolean variableLength,
                    boolean longValue,
-                   Integer minSize,
-                   Integer defaultSize,
-                   Integer maxSize,
+                   int minSize,
+                   int defaultSize,
+                   int maxSize,
                    int unitSize) {
     this(value, sqlType, fixedSize, variableLength, longValue,
          minSize, defaultSize, maxSize,
-         false, null, null, null, null, null, null, unitSize);
+         false, 0, 0, 0, 0, 0, 0, unitSize);
   }
   
   private DataType(byte value, Integer sqlType, Integer fixedSize,
                    boolean variableLength,
                    boolean longValue,
-                   Integer minSize,
-                   Integer defaultSize,
-                   Integer maxSize,
+                   int minSize,
+                   int defaultSize,
+                   int maxSize,
                    boolean hasScalePrecision,
-                   Integer minScale,
-                   Integer defaultScale,
-                   Integer maxScale,
-                   Integer minPrecision,
-                   Integer defaultPrecision,
-                   Integer maxPrecision,
+                   int minScale,
+                   int defaultScale,
+                   int maxScale,
+                   int minPrecision,
+                   int defaultPrecision,
+                   int maxPrecision,
                    int unitSize) {
     _value = value;
     _sqlType = sqlType;
index 6cac9fbfec2628724f96c4ff924d8431858d70bb..ce8ff63a40dab96a42a86b4e7d01efa6f97a7b40 100644 (file)
@@ -59,7 +59,6 @@ import com.healthmarketscience.jackcess.Table;
 import com.healthmarketscience.jackcess.complex.ComplexColumnInfo;
 import com.healthmarketscience.jackcess.complex.ComplexValue;
 import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
-import com.healthmarketscience.jackcess.impl.complex.ComplexColumnInfoImpl;
 import com.healthmarketscience.jackcess.impl.complex.ComplexValueForeignKeyImpl;
 import com.healthmarketscience.jackcess.impl.scsu.Compress;
 import com.healthmarketscience.jackcess.impl.scsu.EndOfInputException;
@@ -182,12 +181,6 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
   private static final byte[] TEXT_COMPRESSION_HEADER = 
   { (byte)0xFF, (byte)0XFE };
 
-  /** placeholder for column which is not numeric */
-  private static final NumericInfo DEFAULT_NUMERIC_INFO = new NumericInfo();
-
-  /** placeholder for column which is not textual */
-  private static final TextInfo DEFAULT_TEXT_INFO = new TextInfo();
-
   
   /** owning table */
   private final TableImpl _table;
@@ -211,18 +204,10 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
   private final int _fixedDataOffset;
   /** the index of the variable length data in the var len offset table */
   private final int _varLenTableIndex;
-  /** information specific to numeric columns */
-  private NumericInfo _numericInfo = DEFAULT_NUMERIC_INFO;
-  /** information specific to text columns */
-  private TextInfo _textInfo = DEFAULT_TEXT_INFO;
   /** the auto number generator for this column (if autonumber column) */
   private final AutoNumberGenerator _autoNumberGenerator;
-  /** additional information specific to complex columns */
-  private final ComplexColumnInfo<? extends ComplexValue> _complexInfo;
   /** properties for this column, if any */
   private PropertyMap _props;  
-  /** Holds additional info for writing long values */
-  private LongValueBufferHolder _lvalBufferH;
   /** Validator for writing new values */
   private ColumnValidator _validator = SimpleColumnValidator.INSTANCE;
   
@@ -240,11 +225,6 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
       _columnLength = (short)type.getMaxSize();
     }
     _variableLength = type.isVariableLength();
-    if(type.getHasScalePrecision()) {
-      modifyNumericInfo();
-      _numericInfo._scale = (byte)type.getDefaultScale();
-      _numericInfo._precision =(byte)type.getDefaultPrecision();
-    }
     _autoNumber = false;
     _autoNumberGenerator = null;
     _columnNumber = (short)colNumber;
@@ -252,7 +232,6 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
     _displayIndex = colNumber;
     _fixedDataOffset = fixedOffset;
     _varLenTableIndex = varLenIndex;
-    _complexInfo = null;
   }
     
   /**
@@ -262,94 +241,97 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
    * @param offset Offset in the buffer at which the column definition starts
    * @usage _advanced_method_
    */
-  public ColumnImpl(TableImpl table, ByteBuffer buffer, int offset, 
-                    int displayIndex)
+  ColumnImpl(TableImpl table, ByteBuffer buffer, int offset, int displayIndex,
+             DataType type, byte flags)
     throws IOException
   {
     _table = table;
     _displayIndex = displayIndex;
+    _type = type;
     
-    byte colType = buffer.get(offset + getFormat().OFFSET_COLUMN_TYPE);
     _columnNumber = buffer.getShort(offset + getFormat().OFFSET_COLUMN_NUMBER);
     _columnLength = buffer.getShort(offset + getFormat().OFFSET_COLUMN_LENGTH);
     
-    byte flags = buffer.get(offset + getFormat().OFFSET_COLUMN_FLAGS);
     _variableLength = ((flags & FIXED_LEN_FLAG_MASK) == 0);
     _autoNumber = ((flags & (AUTO_NUMBER_FLAG_MASK | AUTO_NUMBER_GUID_FLAG_MASK))
                    != 0);
+    
+    _autoNumberGenerator = createAutoNumberGenerator();
+    
+    if(_variableLength) {
+      _varLenTableIndex = buffer.getShort(offset + getFormat().OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
+      _fixedDataOffset = 0;
+    } else {
+      _fixedDataOffset = buffer.getShort(offset + getFormat().OFFSET_COLUMN_FIXED_DATA_OFFSET);
+      _varLenTableIndex = 0;
+    }
+  }
+
+  /**
+   * Creates the appropriate ColumnImpl class and reads a column definition in
+   * from a buffer
+   * @param table owning table
+   * @param buffer Buffer containing column definition
+   * @param offset Offset in the buffer at which the column definition starts
+   * @usage _advanced_method_
+   */
+  public static ColumnImpl create(TableImpl table, ByteBuffer buffer, int offset,
+                                  int displayIndex)
+    throws IOException
+  {
+    byte colType = buffer.get(offset + table.getFormat().OFFSET_COLUMN_TYPE);
+    byte flags = buffer.get(offset + table.getFormat().OFFSET_COLUMN_FLAGS);
 
     DataType type = null;
     try {
       type = DataType.fromByte(colType);
     } catch(IOException e) {
       LOG.warn("Unsupported column type " + colType);
-      type = (_variableLength ? DataType.UNSUPPORTED_VARLEN :
+      boolean variableLength = ((flags & FIXED_LEN_FLAG_MASK) == 0);
+      type = (variableLength ? DataType.UNSUPPORTED_VARLEN :
               DataType.UNSUPPORTED_FIXEDLEN);
-      setUnknownDataType(colType);
+      return new UnsupportedColumnImpl(table, buffer, offset, displayIndex, type,
+                                       flags, colType);
     }
-    _type = type;
-    
-    if (_type.getHasScalePrecision()) {
-      modifyNumericInfo();
-      _numericInfo._precision = buffer.get(offset +
-                                           getFormat().OFFSET_COLUMN_PRECISION);
-      _numericInfo._scale = buffer.get(offset + getFormat().OFFSET_COLUMN_SCALE);
-    } else if(_type.isTextual()) {
-      modifyTextInfo();
-
-      // co-located w/ precision/scale
-      _textInfo._sortOrder = readSortOrder(
-          buffer, offset + getFormat().OFFSET_COLUMN_SORT_ORDER, getFormat());
-      int cpOffset = getFormat().OFFSET_COLUMN_CODE_PAGE;
-      if(cpOffset >= 0) {
-        _textInfo._codePage = buffer.getShort(offset + cpOffset);
-      }
 
-      _textInfo._compressedUnicode = ((buffer.get(offset +
-        getFormat().OFFSET_COLUMN_COMPRESSED_UNICODE) & 1) == 1);
+    switch(type) {
+    case TEXT:
+      return new TextColumnImpl(table, buffer, offset, displayIndex, type,
+                                flags);
+    case MEMO:
+      return new MemoColumnImpl(table, buffer, offset, displayIndex, type,
+                                flags);
+    case COMPLEX_TYPE:
+      return new ComplexColumnImpl(table, buffer, offset, displayIndex, type,
+                                   flags);
+    default:
+      // fall through
+    }
 
-      if(_type == DataType.MEMO) {
-        // only memo fields can be hyperlinks
-        _textInfo._hyperlink = ((flags & HYPERLINK_FLAG_MASK) != 0);
-      }
+    if(type.getHasScalePrecision()) {
+      return new NumericColumnImpl(table, buffer, offset, displayIndex, type,
+                                   flags);
     }
-    
-    _autoNumberGenerator = createAutoNumberGenerator();
-    
-    if(_variableLength) {
-      _varLenTableIndex = buffer.getShort(offset + getFormat().OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
-      _fixedDataOffset = 0;
-    } else {
-      _fixedDataOffset = buffer.getShort(offset + getFormat().OFFSET_COLUMN_FIXED_DATA_OFFSET);
-      _varLenTableIndex = 0;
+    if(type.isLongValue()) {
+      return new LongValueColumnImpl(table, buffer, offset, displayIndex, type,
+                                     flags);
     }
-
-    // load complex info
-    if(_type == DataType.COMPLEX_TYPE) {
-      _complexInfo = ComplexColumnSupport.create(this, buffer, offset);
-    } else {
-      _complexInfo = null;
-    } 
+    
+    return new ColumnImpl(table, buffer, offset, displayIndex, type, flags);
   }
 
    /**
    * Sets the usage maps for this column.
    */
   void setUsageMaps(UsageMap ownedPages, UsageMap freeSpacePages) {
-    _lvalBufferH = new UmapLongValueBufferHolder(ownedPages, freeSpacePages);
+    // base does nothing
   }
 
   /**
    * Secondary column initialization after the table is fully loaded.
    */
   void postTableLoadInit() throws IOException {
-    if(getType().isLongValue() && (_lvalBufferH == null)) {
-      _lvalBufferH = new LegacyLongValueBufferHolder();
-    }
-    if(_complexInfo != null) {
-      ((ComplexColumnInfoImpl<? extends ComplexValue>)_complexInfo)
-      .postTableLoadInit();
-    }
+    // base does nothing
   }
 
   public TableImpl getTable() {
@@ -427,29 +409,29 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
   }
   
   public boolean isCompressedUnicode() {
-    return _textInfo._compressedUnicode;
+    return false;
   }
 
   public byte getPrecision() {
-    return _numericInfo._precision;
+    return (byte)getType().getDefaultPrecision();
   }
   
   public byte getScale() {
-    return _numericInfo._scale;
+    return (byte)getType().getDefaultScale();
   }
 
   /**
    * @usage _intermediate_method_
    */
   public SortOrder getTextSortOrder() {
-    return _textInfo._sortOrder;
+    return null;
   }
 
   /**
    * @usage _intermediate_method_
    */
   public short getTextCodePage() {
-    return _textInfo._codePage;
+    return 0;
   }
 
   public short getLength() {
@@ -487,31 +469,30 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
   }
   
   public ColumnImpl getVersionHistoryColumn() {
-    return _textInfo._versionHistoryCol;
+    return null;
   }
 
-   /**
+  /**
    * Returns the number of database pages owned by this column.
    * @usage _intermediate_method_
    */
   public int getOwnedPageCount() {
-    return ((_lvalBufferH == null) ? 0 : _lvalBufferH.getOwnedPageCount());
+    return 0;
   }
 
   /**
    * @usage _advanced_method_
    */
   public void setVersionHistoryColumn(ColumnImpl versionHistoryCol) {
-    modifyTextInfo();
-    _textInfo._versionHistoryCol = versionHistoryCol;
+    throw new UnsupportedOperationException();
   }
 
   public boolean isHyperlink() {
-    return _textInfo._hyperlink;
+    return false;
   }
   
   public ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
-    return _complexInfo;
+    return null;
   }
 
   public ColumnValidator getColumnValidator() {
@@ -541,15 +522,12 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
     _validator = newValidator;
   }
   
-  private void setUnknownDataType(byte type) {
-    // slight hack, stash the original type in the _scale
-    modifyNumericInfo();
-    _numericInfo._scale = type;
+  byte getOriginalDataType() {
+    return _type.getValue();
   }
 
-  private byte getUnknownDataType() {
-    // slight hack, we stashed the real type in the _scale
-    return _numericInfo._scale;
+  LongValueBufferHolder getLongValueBufferHolder() {
+    return null;
   }
   
   private AutoNumberGenerator createAutoNumberGenerator() {
@@ -585,18 +563,6 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
     }
     return _props;
   }
-
-  private void modifyNumericInfo() {
-    if(_numericInfo == DEFAULT_NUMERIC_INFO) {
-      _numericInfo = new NumericInfo();
-    }
-  }
-  
-  private void modifyTextInfo() {
-    if(_textInfo == DEFAULT_TEXT_INFO) {
-      _textInfo = new TextInfo();
-    }
-  }
   
   public Object setRowValue(Object[] rowArray, Object value) {
     rowArray[_columnIndex] = value;
@@ -1128,12 +1094,13 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
       ByteBuffer lvalPage = null;
       int firstLvalPageNum = PageChannel.INVALID_PAGE_NUMBER;
       byte firstLvalRow = 0;
-
+      LongValueBufferHolder lvalBufferH = getLongValueBufferHolder();
+      
       // write other page(s)
       switch(type) {
       case LONG_VALUE_TYPE_OTHER_PAGE:
-        lvalPage = _lvalBufferH.getLongValuePage(value.length);
-        firstLvalPageNum = _lvalBufferH.getPageNumber();
+        lvalPage = lvalBufferH.getLongValuePage(value.length);
+        firstLvalPageNum = lvalBufferH.getPageNumber();
         firstLvalRow = (byte)TableImpl.addDataPageRow(lvalPage, value.length,
                                                   getFormat(), 0);
         lvalPage.put(value);
@@ -1145,8 +1112,8 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
         ByteBuffer buffer = ByteBuffer.wrap(value);
         int remainingLen = buffer.remaining();
         buffer.limit(0);
-        lvalPage = _lvalBufferH.getLongValuePage(remainingLen);
-        firstLvalPageNum = _lvalBufferH.getPageNumber();
+        lvalPage = lvalBufferH.getLongValuePage(remainingLen);
+        firstLvalPageNum = lvalBufferH.getPageNumber();
         firstLvalRow = (byte)TableImpl.getRowsOnDataPage(lvalPage, getFormat());
         int lvalPageNum = firstLvalPageNum;
         ByteBuffer nextLvalPage = null;
@@ -1163,10 +1130,10 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
           // figure out if we will need another page, and if so, allocate it
           if(chunkLength < remainingLen) {
             // force a new page to be allocated for the chunk after this
-            _lvalBufferH.clear();
-            nextLvalPage = _lvalBufferH.getLongValuePage(
+            lvalBufferH.clear();
+            nextLvalPage = lvalBufferH.getLongValuePage(
                 (remainingLen - chunkLength) + 4);
-            nextLvalPageNum = _lvalBufferH.getPageNumber();
+            nextLvalPageNum = lvalBufferH.getPageNumber();
             nextLvalRowNum = TableImpl.getRowsOnDataPage(nextLvalPage, 
                                                          getFormat());
           } else {
@@ -1176,8 +1143,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
           }
 
           // add row to this page
-          byte lvalRow = (byte)TableImpl.addDataPageRow(lvalPage, chunkLength + 4,
-                                                        getFormat(), 0);
+          TableImpl.addDataPageRow(lvalPage, chunkLength + 4, getFormat(), 0);
           
           // write next page info
           lvalPage.put((byte)nextLvalRowNum); // row number
@@ -1585,20 +1551,17 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
   public String toString() {
     ToStringBuilder sb = CustomToStringStyle.builder(this)
       .append("name", "(" + _table.getName() + ") " + _name);
-    byte typeValue = _type.getValue();
-    if(_type.isUnsupported()) {
-      typeValue = getUnknownDataType();
-    }
+    byte typeValue = getOriginalDataType();
     sb.append("type", "0x" + Integer.toHexString(typeValue) +
               " (" + _type + ")")
       .append("number", _columnNumber)
       .append("length", _columnLength)
       .append("variableLength", _variableLength);
     if(_type.isTextual()) {
-      sb.append("compressedUnicode", _textInfo._compressedUnicode)
-        .append("textSortOrder", _textInfo._sortOrder);
-      if(_textInfo._codePage > 0) {
-        sb.append("textCodePage", _textInfo._codePage);
+      sb.append("compressedUnicode", isCompressedUnicode())
+        .append("textSortOrder", getTextSortOrder());
+      if(getTextCodePage() > 0) {
+        sb.append("textCodePage", getTextCodePage());
       }
       if(isAppendOnly()) {
         sb.append("appendOnly", isAppendOnly());
@@ -1610,8 +1573,8 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
     if(_autoNumber) {
       sb.append("lastAutoNumber", _autoNumberGenerator.getLast());
     }
-    if(_complexInfo != null) {
-      sb.append("complexInfo", _complexInfo);
+    if(getComplexInfo() != null) {
+      sb.append("complexInfo", getComplexInfo());
     }
     return sb.toString();
   }
@@ -1937,6 +1900,16 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
     return new SortOrder(value, version);
   }
 
+  /**
+   * Reads the column cade page info from the given buffer, if supported for
+   * this db.
+   */
+  static short readCodePage(ByteBuffer buffer, int offset, JetFormat format)
+  {
+      int cpOffset = format.OFFSET_COLUMN_CODE_PAGE;
+      return ((cpOffset >= 0) ? buffer.getShort(offset + cpOffset) : 0);
+  }
+  
   /**
    * Writes the sort order info to the given buffer at the current position.
    */
@@ -2218,40 +2191,10 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
     }
   }
 
-  /**
-   * Information specific to numeric types.
-   */
-  private static final class NumericInfo
-  {
-    /** Numeric precision */
-    private byte _precision;
-    /** Numeric scale */
-    private byte _scale;
-  }
-
-  /**
-   * Information specific to textual types.
-   */
-  private static final class TextInfo
-  {
-    /** whether or not they are compressed */ 
-    private boolean _compressedUnicode;
-    /** the collating sort order for a text field */
-    private SortOrder _sortOrder;
-    /** the code page for a text field (for certain db versions) */
-    private short _codePage;
-    /** complex column which tracks the version history for this "append only"
-        column */
-    private ColumnImpl _versionHistoryCol;
-    /** whether or not this is a hyperlink column (only possible for columns
-        of type MEMO) */
-    private boolean _hyperlink;
-  }
-
   /**
    * Manages secondary page buffers for long value writing.
    */
-  private abstract class LongValueBufferHolder
+  abstract class LongValueBufferHolder
   {
     /**
      * Returns a long value data page with space for data of the given length.
@@ -2300,79 +2243,4 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
 
     protected abstract TempPageHolder getBufferHolder();
   }
-
-  /**
-   * Manages a common, shared extra page for long values.  This is legacy
-   * behavior from before it was understood that there were additional usage
-   * maps for each columns.
-   */
-  private final class LegacyLongValueBufferHolder extends LongValueBufferHolder
-  {
-    @Override
-    protected TempPageHolder getBufferHolder() {
-      return getTable().getLongValueBuffer();
-    }
-  }
-
-  /**
-   * Manages the column usage maps for long values.
-   */
-  private final class UmapLongValueBufferHolder extends LongValueBufferHolder
-  {
-    /** Usage map of pages that this column owns */
-    private final UsageMap _ownedPages;
-    /** Usage map of pages that this column owns with free space on them */
-    private final UsageMap _freeSpacePages;
-    /** page buffer used to write "long value" data */
-    private final TempPageHolder _longValueBufferH =
-      TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
-
-    private UmapLongValueBufferHolder(UsageMap ownedPages,
-                                      UsageMap freeSpacePages) {
-      _ownedPages = ownedPages;
-      _freeSpacePages = freeSpacePages;
-    }
-
-    @Override
-    protected TempPageHolder getBufferHolder() {
-      return _longValueBufferH;
-    }
-
-    @Override
-    public int getOwnedPageCount() {
-      return _ownedPages.getPageCount();
-    }
-
-    @Override
-    protected ByteBuffer findNewPage(int dataLength) throws IOException {
-
-      // 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;
-        }
-        // discard this page and allocate a new one
-        clear();
-      }
-
-      // nothing found on current pages, need new page
-      newPage = super.findNewPage(dataLength);
-      int pageNumber = getPageNumber();
-      _ownedPages.addPageNumber(pageNumber);
-      _freeSpacePages.addPageNumber(pageNumber);
-      return newPage;
-    }
-
-    @Override
-    public void clear() throws IOException {
-      int pageNumber = getPageNumber();
-      if(pageNumber != PageChannel.INVALID_PAGE_NUMBER) {
-        _freeSpacePages.removePageNumber(pageNumber, true);
-      }
-      super.clear();
-    }
-  }
 }
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnImpl.java
new file mode 100644 (file)
index 0000000..85036a6
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+Copyright (c) 2014 James Ahlborn
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+*/
+
+package com.healthmarketscience.jackcess.impl;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import com.healthmarketscience.jackcess.DataType;
+import com.healthmarketscience.jackcess.complex.ComplexColumnInfo;
+import com.healthmarketscience.jackcess.complex.ComplexValue;
+import com.healthmarketscience.jackcess.impl.complex.ComplexColumnInfoImpl;
+
+/**
+ * ColumnImpl subclass which is used for complex data types.
+ * 
+ * @author James Ahlborn
+ * @usage _advanced_class_
+ */
+class ComplexColumnImpl extends ColumnImpl 
+{
+  /** additional information specific to complex columns */
+  private final ComplexColumnInfo<? extends ComplexValue> _complexInfo;
+
+  ComplexColumnImpl(TableImpl table, ByteBuffer buffer, int offset, 
+                    int displayIndex, DataType type, byte flags)
+    throws IOException
+  {
+    super(table, buffer, offset, displayIndex, type, flags);
+    _complexInfo = ComplexColumnSupport.create(this, buffer, offset);
+  }
+
+  @Override
+  void postTableLoadInit() throws IOException {
+    if(_complexInfo != null) {
+      ((ComplexColumnInfoImpl<? extends ComplexValue>)_complexInfo)
+        .postTableLoadInit();
+    }
+    super.postTableLoadInit();
+  }
+
+  @Override
+  public ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
+    return _complexInfo;
+  }
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/LongValueColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/LongValueColumnImpl.java
new file mode 100644 (file)
index 0000000..3ac1093
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+Copyright (c) 2014 James Ahlborn
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+*/
+
+package com.healthmarketscience.jackcess.impl;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import com.healthmarketscience.jackcess.DataType;
+
+/**
+ * ColumnImpl subclass which is used for long value data types.
+ * 
+ * @author James Ahlborn
+ * @usage _advanced_class_
+ */
+class LongValueColumnImpl extends ColumnImpl 
+{
+  /** Holds additional info for writing long values */
+  private LongValueBufferHolder _lvalBufferH;
+
+  LongValueColumnImpl(TableImpl table, ByteBuffer buffer, int offset, 
+                      int displayIndex, DataType type, byte flags)
+    throws IOException
+  {
+    super(table, buffer, offset, displayIndex, type, flags);
+  }
+
+  @Override
+  LongValueBufferHolder getLongValueBufferHolder() {
+    return _lvalBufferH;
+  }
+    
+  @Override
+  public int getOwnedPageCount() {
+    return ((_lvalBufferH == null) ? 0 : _lvalBufferH.getOwnedPageCount());
+  }
+  
+  @Override
+  void setUsageMaps(UsageMap ownedPages, UsageMap freeSpacePages) {
+    _lvalBufferH = new UmapLongValueBufferHolder(ownedPages, freeSpacePages);
+  }
+
+  @Override
+  void postTableLoadInit() throws IOException {
+    if(_lvalBufferH == null) {
+      _lvalBufferH = new LegacyLongValueBufferHolder();
+    }
+    super.postTableLoadInit();
+  }
+  
+  /**
+   * Manages a common, shared extra page for long values.  This is legacy
+   * behavior from before it was understood that there were additional usage
+   * maps for each columns.
+   */
+  private final class LegacyLongValueBufferHolder extends LongValueBufferHolder
+  {
+    @Override
+    protected TempPageHolder getBufferHolder() {
+      return getTable().getLongValueBuffer();
+    }
+  }
+
+  /**
+   * Manages the column usage maps for long values.
+   */
+  private final class UmapLongValueBufferHolder extends LongValueBufferHolder
+  {
+    /** Usage map of pages that this column owns */
+    private final UsageMap _ownedPages;
+    /** Usage map of pages that this column owns with free space on them */
+    private final UsageMap _freeSpacePages;
+    /** page buffer used to write "long value" data */
+    private final TempPageHolder _longValueBufferH =
+      TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
+
+    private UmapLongValueBufferHolder(UsageMap ownedPages,
+                                      UsageMap freeSpacePages) {
+      _ownedPages = ownedPages;
+      _freeSpacePages = freeSpacePages;
+    }
+
+    @Override
+    protected TempPageHolder getBufferHolder() {
+      return _longValueBufferH;
+    }
+
+    @Override
+    public int getOwnedPageCount() {
+      return _ownedPages.getPageCount();
+    }
+
+    @Override
+    protected ByteBuffer findNewPage(int dataLength) throws IOException {
+
+      // 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;
+        }
+        // discard this page and allocate a new one
+        clear();
+      }
+
+      // nothing found on current pages, need new page
+      newPage = super.findNewPage(dataLength);
+      int pageNumber = getPageNumber();
+      _ownedPages.addPageNumber(pageNumber);
+      _freeSpacePages.addPageNumber(pageNumber);
+      return newPage;
+    }
+
+    @Override
+    public void clear() throws IOException {
+      int pageNumber = getPageNumber();
+      if(pageNumber != PageChannel.INVALID_PAGE_NUMBER) {
+        _freeSpacePages.removePageNumber(pageNumber, true);
+      }
+      super.clear();
+    }
+  }
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/MemoColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/MemoColumnImpl.java
new file mode 100644 (file)
index 0000000..8a9c742
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+Copyright (c) 2014 James Ahlborn
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+*/
+
+package com.healthmarketscience.jackcess.impl;
+
+import java.io.IOException;
+import com.healthmarketscience.jackcess.DataType;
+import java.nio.ByteBuffer;
+
+/**
+ * ColumnImpl subclass which is used for Memo data types.
+ *
+ * @author James Ahlborn
+ * @usage _advanced_class_
+ */
+class MemoColumnImpl extends LongValueColumnImpl 
+{
+  /** whether or not they are compressed */ 
+  private final boolean _compressedUnicode;
+  /** the collating sort order for a text field */
+  private final SortOrder _sortOrder;
+  /** the code page for a text field (for certain db versions) */
+  private final short _codePage;
+  /** complex column which tracks the version history for this "append only"
+      column */
+  private ColumnImpl _versionHistoryCol;
+  /** whether or not this is a hyperlink column (only possible for columns
+      of type MEMO) */
+  private boolean _hyperlink;
+
+  MemoColumnImpl(TableImpl table, ByteBuffer buffer, int offset, 
+                 int displayIndex, DataType type, byte flags)
+    throws IOException
+  {
+    super(table, buffer, offset, displayIndex, type, flags);
+
+      // co-located w/ precision/scale
+      _sortOrder = readSortOrder(
+          buffer, offset + getFormat().OFFSET_COLUMN_SORT_ORDER, getFormat());
+      _codePage = readCodePage(buffer, offset, getFormat());
+
+      _compressedUnicode = ((buffer.get(offset +
+        getFormat().OFFSET_COLUMN_COMPRESSED_UNICODE) & 1) == 1);
+
+      // only memo fields can be hyperlinks
+      _hyperlink = ((flags & HYPERLINK_FLAG_MASK) != 0);
+  }
+
+  @Override
+  public boolean isCompressedUnicode() {
+    return _compressedUnicode;
+  }
+  
+  @Override
+  public short getTextCodePage() {
+    return _codePage;
+  }
+  
+  @Override
+  public SortOrder getTextSortOrder() {
+    return _sortOrder;
+  }
+
+  @Override
+  public ColumnImpl getVersionHistoryColumn() {
+    return _versionHistoryCol;
+  }
+
+  @Override
+  public void setVersionHistoryColumn(ColumnImpl versionHistoryCol) {
+    _versionHistoryCol = versionHistoryCol;
+  }
+  
+  @Override
+  public boolean isHyperlink() {
+    return _hyperlink;
+  }
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/NumericColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/NumericColumnImpl.java
new file mode 100644 (file)
index 0000000..667315a
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+Copyright (c) 2014 James Ahlborn
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+*/
+
+package com.healthmarketscience.jackcess.impl;
+
+import java.io.IOException;
+import com.healthmarketscience.jackcess.DataType;
+import java.nio.ByteBuffer;
+
+/**
+ * ColumnImpl subclass which is used for numeric data types.
+ *
+ * @author James Ahlborn
+ * @usage _advanced_class_
+ */
+class NumericColumnImpl extends ColumnImpl 
+{
+  /** Numeric precision */
+  private final byte _precision;
+  /** Numeric scale */
+  private final byte _scale;
+
+  NumericColumnImpl(TableImpl table, ByteBuffer buffer, int offset, 
+                    int displayIndex, DataType type, byte flags)
+    throws IOException
+  {
+    super(table, buffer, offset, displayIndex, type, flags);
+
+    _precision = buffer.get(offset + getFormat().OFFSET_COLUMN_PRECISION);
+    _scale = buffer.get(offset + getFormat().OFFSET_COLUMN_SCALE);
+  }
+
+  @Override
+  public byte getPrecision() {
+    return _precision;
+  }
+  
+  @Override
+  public byte getScale() {
+    return _scale;
+  }  
+}
index 4107abb95594d7bf30008dbf1572e15ebf86f18a..3f2284709ddc2c425529e0fc050f910d24f17a73 100644 (file)
@@ -1269,7 +1269,7 @@ public class TableImpl implements Table
                          (short)umapRowNum);
         pageChannel.writePage(umapBuf, umapPageNumber);
         umapBuf = null;
-  }
+      }
     }
   }
 
@@ -1305,7 +1305,7 @@ public class TableImpl implements Table
         _indexCount * getFormat().SIZE_INDEX_DEFINITION;
     int dispIndex = 0;
     for (int i = 0; i < columnCount; i++) {
-      ColumnImpl column = new ColumnImpl(this, tableBuffer,
+      ColumnImpl column = ColumnImpl.create(this, tableBuffer,
           colOffset + (i * getFormat().SIZE_COLUMN_HEADER), dispIndex++);
       _columns.add(column);
       if(column.isVariableLength()) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TextColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/TextColumnImpl.java
new file mode 100644 (file)
index 0000000..0966d5b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+Copyright (c) 2014 James Ahlborn
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+*/
+
+package com.healthmarketscience.jackcess.impl;
+
+import java.io.IOException;
+import com.healthmarketscience.jackcess.DataType;
+import java.nio.ByteBuffer;
+
+/**
+ * ColumnImpl subclass which is used for Text data types.
+ *
+ * @author James Ahlborn
+ * @usage _advanced_class_
+ */
+class TextColumnImpl extends ColumnImpl 
+{
+  /** whether or not they are compressed */ 
+  private final boolean _compressedUnicode;
+  /** the collating sort order for a text field */
+  private final SortOrder _sortOrder;
+  /** the code page for a text field (for certain db versions) */
+  private final short _codePage;
+
+  TextColumnImpl(TableImpl table, ByteBuffer buffer, int offset, 
+                 int displayIndex, DataType type, byte flags)
+    throws IOException
+  {
+    super(table, buffer, offset, displayIndex, type, flags);
+
+      // co-located w/ precision/scale
+      _sortOrder = readSortOrder(
+          buffer, offset + getFormat().OFFSET_COLUMN_SORT_ORDER, getFormat());
+      _codePage = readCodePage(buffer, offset, getFormat());
+
+      _compressedUnicode = ((buffer.get(offset +
+        getFormat().OFFSET_COLUMN_COMPRESSED_UNICODE) & 1) == 1);
+  }
+
+  @Override
+  public boolean isCompressedUnicode() {
+    return _compressedUnicode;
+  }
+  
+  @Override
+  public short getTextCodePage() {
+    return _codePage;
+  }
+  
+  @Override
+  public SortOrder getTextSortOrder() {
+    return _sortOrder;
+  }
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/UnsupportedColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/UnsupportedColumnImpl.java
new file mode 100644 (file)
index 0000000..5165a53
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+Copyright (c) 2014 James Ahlborn
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+*/
+
+package com.healthmarketscience.jackcess.impl;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import com.healthmarketscience.jackcess.DataType;
+
+/**
+ * ColumnImpl subclass which is used for unknown/unsupported data types.
+ * 
+ * @author James Ahlborn
+ * @usage _advanced_class_
+ */
+class UnsupportedColumnImpl extends ColumnImpl 
+{
+  private final byte _originalType;
+  
+  UnsupportedColumnImpl(TableImpl table, ByteBuffer buffer, int offset, 
+                        int displayIndex, DataType type, byte flags,
+                        byte originalType)
+    throws IOException
+  {
+    super(table, buffer, offset, displayIndex, type, flags);
+    _originalType = originalType;
+  }
+
+  @Override
+  byte getOriginalDataType() {
+    return _originalType;
+  }
+  
+}