}
public byte getPrecision() {
- return ((_precision != null) ? _precision :
- (byte)(_type.getHasScalePrecision() ? _type.getDefaultPrecision() : 0));
+ return ((_precision != null) ? _precision : (byte)_type.getDefaultPrecision());
}
/**
}
public byte getScale() {
- return ((_scale != null) ? _scale :
- (byte)(_type.getHasScalePrecision() ? _type.getDefaultScale() : 0));
+ return ((_scale != null) ? _scale : (byte)_type.getDefaultScale());
}
/**
public short getLength() {
return ((_length != null) ? _length :
(short)(!_type.isVariableLength() ? _type.getFixedSize() :
- (!_type.isLongValue() ? _type.getDefaultSize() : 0)));
+ _type.getDefaultSize()));
}
/**
*/
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.
* {@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}.
* 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) {
}
/** 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;
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;
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;
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;
_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;
_displayIndex = colNumber;
_fixedDataOffset = fixedOffset;
_varLenTableIndex = varLenIndex;
- _complexInfo = null;
}
/**
* @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() {
}
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() {
}
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() {
_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() {
}
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;
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);
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;
// 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 {
}
// 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
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());
if(_autoNumber) {
sb.append("lastAutoNumber", _autoNumberGenerator.getLast());
}
- if(_complexInfo != null) {
- sb.append("complexInfo", _complexInfo);
+ if(getComplexInfo() != null) {
+ sb.append("complexInfo", getComplexInfo());
}
return sb.toString();
}
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.
*/
}
}
- /**
- * 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.
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();
- }
- }
}
--- /dev/null
+/*
+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;
+ }
+}
--- /dev/null
+/*
+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();
+ }
+ }
+}
--- /dev/null
+/*
+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;
+ }
+}
--- /dev/null
+/*
+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;
+ }
+}
(short)umapRowNum);
pageChannel.writePage(umapBuf, umapPageNumber);
umapBuf = null;
- }
+ }
}
}
_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()) {
--- /dev/null
+/*
+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;
+ }
+}
--- /dev/null
+/*
+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;
+ }
+
+}