git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@7 f203690c-595d-4dc9-a70b-905162fa7fd2tags/rel_1_1
@@ -1,5 +1,6 @@ | |||
maven.artifact.legacy=false | |||
maven.changes.issue.template=http://sf.net/tracker/index.php?func=detail&aid=%ISSUE%&group_id=134943&atid=731445 | |||
maven.compile.compilerargs=-Xlint:all | |||
maven.compile.source=1.5 | |||
maven.compile.target=1.5 | |||
maven.javadoc.excludepackagenames=com.healthmarketscience.jackcess.scsu |
@@ -85,7 +85,7 @@ public class Column implements Comparable { | |||
/** Numeric scale */ | |||
private byte _scale; | |||
/** Data type */ | |||
private byte _type; | |||
private DataType _type; | |||
/** Format that the containing database is in */ | |||
private JetFormat _format; | |||
/** Used to read in LVAL pages */ | |||
@@ -111,16 +111,18 @@ public class Column implements Comparable { | |||
* @param offset Offset in the buffer at which the column definition starts | |||
* @param format Format that the containing database is in | |||
*/ | |||
public Column(ByteBuffer buffer, int offset, PageChannel pageChannel, JetFormat format) { | |||
public Column(ByteBuffer buffer, int offset, PageChannel pageChannel, JetFormat format) | |||
throws SQLException | |||
{ | |||
if (LOG.isDebugEnabled()) { | |||
LOG.debug("Column def block:\n" + ByteUtil.toHexString(buffer, offset, 25)); | |||
} | |||
_pageChannel = pageChannel; | |||
_format = format; | |||
setType(buffer.get(offset + format.OFFSET_COLUMN_TYPE)); | |||
setType(DataType.fromByte(buffer.get(offset + format.OFFSET_COLUMN_TYPE))); | |||
_columnNumber = buffer.getShort(offset + format.OFFSET_COLUMN_NUMBER); | |||
_columnLength = buffer.getShort(offset + format.OFFSET_COLUMN_LENGTH); | |||
if (_type == DataTypes.NUMERIC) { | |||
if (_type == DataType.NUMERIC) { | |||
_precision = buffer.get(offset + format.OFFSET_COLUMN_PRECISION); | |||
_scale = buffer.get(offset + format.OFFSET_COLUMN_SCALE); | |||
} | |||
@@ -151,35 +153,29 @@ public class Column implements Comparable { | |||
/** | |||
* Also sets the length and the variable length flag, inferred from the type | |||
*/ | |||
public void setType(byte type) { | |||
public void setType(DataType type) { | |||
_type = type; | |||
setLength((short) size()); | |||
switch (type) { | |||
case DataTypes.BOOLEAN: | |||
case DataTypes.BYTE: | |||
case DataTypes.INT: | |||
case DataTypes.LONG: | |||
case DataTypes.DOUBLE: | |||
case DataTypes.FLOAT: | |||
case DataTypes.SHORT_DATE_TIME: | |||
setVariableLength(false); | |||
break; | |||
case DataTypes.BINARY: | |||
case DataTypes.TEXT: | |||
setVariableLength(true); | |||
break; | |||
if (type == DataType.BOOLEAN || type == DataType.BYTE || | |||
type == DataType.INT || type == DataType.LONG || | |||
type == DataType.DOUBLE || type == DataType.FLOAT || | |||
type == DataType.SHORT_DATE_TIME) | |||
{ | |||
setVariableLength(false); | |||
} else if (type == DataType.BINARY || type == DataType.TEXT) { | |||
setVariableLength(true); | |||
} | |||
} | |||
public byte getType() { | |||
public DataType getType() { | |||
return _type; | |||
} | |||
public int getSQLType() throws SQLException { | |||
return DataTypes.toSQLType(_type); | |||
return _type.getSQLType(); | |||
} | |||
public void setSQLType(int type) throws SQLException { | |||
setType(DataTypes.fromSQLType(type)); | |||
setType(DataType.fromSQLType(type)); | |||
} | |||
public boolean isCompressedUnicode() { | |||
@@ -219,82 +215,80 @@ public class Column implements Comparable { | |||
public Object read(byte[] data, ByteOrder order) throws IOException { | |||
ByteBuffer buffer = ByteBuffer.wrap(data); | |||
buffer.order(order); | |||
switch (_type) { | |||
case DataTypes.BOOLEAN: | |||
throw new IOException("Tried to read a boolean from data instead of null mask."); | |||
case DataTypes.BYTE: | |||
return new Byte(buffer.get()); | |||
case DataTypes.INT: | |||
return new Short(buffer.getShort()); | |||
case DataTypes.LONG: | |||
return new Integer(buffer.getInt()); | |||
case DataTypes.DOUBLE: | |||
return new Double(buffer.getDouble()); | |||
case DataTypes.FLOAT: | |||
return new Float(buffer.getFloat()); | |||
case DataTypes.SHORT_DATE_TIME: | |||
long time = (long) ((buffer.getDouble() - DAYS_BETWEEN_EPOCH_AND_1900) * | |||
MILLISECONDS_PER_DAY); | |||
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); | |||
cal.setTimeInMillis(time); | |||
//Not sure why we're off by 1... | |||
cal.add(Calendar.DATE, 1); | |||
return cal.getTime(); | |||
case DataTypes.BINARY: | |||
return data; | |||
case DataTypes.TEXT: | |||
if (_compressedUnicode) { | |||
try { | |||
String rtn = new Expand().expand(data); | |||
//SCSU expander isn't handling the UTF-8-looking 2-byte combo that | |||
//prepends some of these strings. Rather than dig into that code, | |||
//I'm just stripping them off here. However, this is probably not | |||
//a great idea. | |||
if (rtn.length() > 2 && (int) rtn.charAt(0) == 255 && | |||
(int) rtn.charAt(1) == 254) | |||
{ | |||
rtn = rtn.substring(2); | |||
} | |||
//It also isn't handling short strings. | |||
if (rtn.length() > 1 && (int) rtn.charAt(1) == 0) { | |||
char[] fixed = new char[rtn.length() / 2]; | |||
for (int i = 0; i < fixed.length; i ++) { | |||
fixed[i] = rtn.charAt(i * 2); | |||
} | |||
rtn = new String(fixed); | |||
if (_type == DataType.BOOLEAN) { | |||
throw new IOException("Tried to read a boolean from data instead of null mask."); | |||
} else if (_type == DataType.BYTE) { | |||
return new Byte(buffer.get()); | |||
} else if (_type == DataType.INT) { | |||
return new Short(buffer.getShort()); | |||
} else if (_type == DataType.LONG) { | |||
return new Integer(buffer.getInt()); | |||
} else if (_type == DataType.DOUBLE) { | |||
return new Double(buffer.getDouble()); | |||
} else if (_type == DataType.FLOAT) { | |||
return new Float(buffer.getFloat()); | |||
} else if (_type == DataType.SHORT_DATE_TIME) { | |||
long time = (long) ((buffer.getDouble() - DAYS_BETWEEN_EPOCH_AND_1900) * | |||
MILLISECONDS_PER_DAY); | |||
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); | |||
cal.setTimeInMillis(time); | |||
//Not sure why we're off by 1... | |||
cal.add(Calendar.DATE, 1); | |||
return cal.getTime(); | |||
} else if (_type == DataType.BINARY) { | |||
return data; | |||
} else if (_type == DataType.TEXT) { | |||
if (_compressedUnicode) { | |||
try { | |||
String rtn = new Expand().expand(data); | |||
//SCSU expander isn't handling the UTF-8-looking 2-byte combo that | |||
//prepends some of these strings. Rather than dig into that code, | |||
//I'm just stripping them off here. However, this is probably not | |||
//a great idea. | |||
if (rtn.length() > 2 && (int) rtn.charAt(0) == 255 && | |||
(int) rtn.charAt(1) == 254) | |||
{ | |||
rtn = rtn.substring(2); | |||
} | |||
//It also isn't handling short strings. | |||
if (rtn.length() > 1 && (int) rtn.charAt(1) == 0) { | |||
char[] fixed = new char[rtn.length() / 2]; | |||
for (int i = 0; i < fixed.length; i ++) { | |||
fixed[i] = rtn.charAt(i * 2); | |||
} | |||
return rtn; | |||
} catch (IllegalInputException e) { | |||
throw new IOException("Can't expand text column"); | |||
} catch (EndOfInputException e) { | |||
throw new IOException("Can't expand text column"); | |||
rtn = new String(fixed); | |||
} | |||
} else { | |||
return _format.CHARSET.decode(ByteBuffer.wrap(data)).toString(); | |||
} | |||
case DataTypes.MONEY: | |||
//XXX | |||
return null; | |||
case DataTypes.OLE: | |||
if (data.length > 0) { | |||
return getLongValue(data); | |||
} else { | |||
return null; | |||
} | |||
case DataTypes.MEMO: | |||
if (data.length > 0) { | |||
return _format.CHARSET.decode(ByteBuffer.wrap(getLongValue(data))).toString(); | |||
} else { | |||
return null; | |||
return rtn; | |||
} catch (IllegalInputException e) { | |||
throw new IOException("Can't expand text column"); | |||
} catch (EndOfInputException e) { | |||
throw new IOException("Can't expand text column"); | |||
} | |||
case DataTypes.NUMERIC: | |||
//XXX | |||
} else { | |||
return _format.CHARSET.decode(ByteBuffer.wrap(data)).toString(); | |||
} | |||
} else if (_type == DataType.MONEY) { | |||
//XXX | |||
return null; | |||
} else if (_type == DataType.OLE) { | |||
if (data.length > 0) { | |||
return getLongValue(data); | |||
} else { | |||
return null; | |||
case DataTypes.UNKNOWN_0D: | |||
case DataTypes.GUID: | |||
} | |||
} else if (_type == DataType.MEMO) { | |||
if (data.length > 0) { | |||
return _format.CHARSET.decode(ByteBuffer.wrap(getLongValue(data))).toString(); | |||
} else { | |||
return null; | |||
default: | |||
throw new IOException("Unrecognized data type: " + _type); | |||
} | |||
} else if (_type == DataType.NUMERIC) { | |||
//XXX | |||
return null; | |||
} else if (_type == DataType.UNKNOWN_0D || _type == DataType.GUID) { | |||
return null; | |||
} else { | |||
throw new IOException("Unrecognized data type: " + _type); | |||
} | |||
} | |||
@@ -402,59 +396,48 @@ public class Column implements Comparable { | |||
*/ | |||
public ByteBuffer write(Object obj, ByteOrder order) throws IOException { | |||
int size = size(); | |||
if (_type == DataTypes.OLE || _type == DataTypes.MEMO) { | |||
if (_type == DataType.OLE || _type == DataType.MEMO) { | |||
size += ((byte[]) obj).length; | |||
} | |||
if (_type == DataTypes.TEXT) { | |||
if (_type == DataType.TEXT) { | |||
size = getLength(); | |||
} | |||
ByteBuffer buffer = ByteBuffer.allocate(size); | |||
buffer.order(order); | |||
switch (_type) { | |||
case DataTypes.BOOLEAN: | |||
break; | |||
case DataTypes.BYTE: | |||
buffer.put(((Byte) obj).byteValue()); | |||
break; | |||
case DataTypes.INT: | |||
buffer.putShort(((Short) obj).shortValue()); | |||
break; | |||
case DataTypes.LONG: | |||
buffer.putInt(((Integer) obj).intValue()); | |||
break; | |||
case DataTypes.DOUBLE: | |||
buffer.putDouble(((Double) obj).doubleValue()); | |||
break; | |||
case DataTypes.FLOAT: | |||
buffer.putFloat(((Float) obj).floatValue()); | |||
break; | |||
case DataTypes.SHORT_DATE_TIME: | |||
Calendar cal = Calendar.getInstance(); | |||
cal.setTime((Date) obj); | |||
long ms = cal.getTimeInMillis(); | |||
ms += (long) TimeZone.getDefault().getOffset(ms); | |||
buffer.putDouble((double) ms / MILLISECONDS_PER_DAY + | |||
DAYS_BETWEEN_EPOCH_AND_1900); | |||
break; | |||
case DataTypes.BINARY: | |||
buffer.put((byte[]) obj); | |||
break; | |||
case DataTypes.TEXT: | |||
CharSequence text = (CharSequence) obj; | |||
int maxChars = size / 2; | |||
if (text.length() > maxChars) { | |||
text = text.subSequence(0, maxChars); | |||
} | |||
buffer.put(encodeText(text)); | |||
break; | |||
case DataTypes.OLE: | |||
buffer.put(writeLongValue((byte[]) obj)); | |||
break; | |||
case DataTypes.MEMO: | |||
buffer.put(writeLongValue(encodeText((CharSequence) obj).array())); | |||
break; | |||
default: | |||
throw new IOException("Unsupported data type: " + _type); | |||
if (_type == DataType.BOOLEAN) { | |||
//Do nothing | |||
} else if (_type == DataType.BYTE) { | |||
buffer.put(((Byte) obj).byteValue()); | |||
} else if (_type == DataType.INT) { | |||
buffer.putShort(((Short) obj).shortValue()); | |||
} else if (_type == DataType.LONG) { | |||
buffer.putInt(((Integer) obj).intValue()); | |||
} else if (_type == DataType.DOUBLE) { | |||
buffer.putDouble(((Double) obj).doubleValue()); | |||
} else if (_type == DataType.FLOAT) { | |||
buffer.putFloat(((Float) obj).floatValue()); | |||
} else if (_type == DataType.SHORT_DATE_TIME) { | |||
Calendar cal = Calendar.getInstance(); | |||
cal.setTime((Date) obj); | |||
long ms = cal.getTimeInMillis(); | |||
ms += (long) TimeZone.getDefault().getOffset(ms); | |||
buffer.putDouble((double) ms / MILLISECONDS_PER_DAY + | |||
DAYS_BETWEEN_EPOCH_AND_1900); | |||
} else if (_type == DataType.BINARY) { | |||
buffer.put((byte[]) obj); | |||
} else if (_type == DataType.TEXT) { | |||
CharSequence text = (CharSequence) obj; | |||
int maxChars = size / 2; | |||
if (text.length() > maxChars) { | |||
text = text.subSequence(0, maxChars); | |||
} | |||
buffer.put(encodeText(text)); | |||
} else if (_type == DataType.OLE) { | |||
buffer.put(writeLongValue((byte[]) obj)); | |||
} else if (_type == DataType.MEMO) { | |||
buffer.put(writeLongValue(encodeText((CharSequence) obj).array())); | |||
} else { | |||
throw new IOException("Unsupported data type: " + _type); | |||
} | |||
buffer.flip(); | |||
return buffer; | |||
@@ -473,44 +456,41 @@ public class Column implements Comparable { | |||
* (applies to fixed-width columns) | |||
*/ | |||
public int size() { | |||
switch (_type) { | |||
case DataTypes.BOOLEAN: | |||
return 0; | |||
case DataTypes.BYTE: | |||
return 1; | |||
case DataTypes.INT: | |||
return 2; | |||
case DataTypes.LONG: | |||
return 4; | |||
case DataTypes.MONEY: | |||
case DataTypes.DOUBLE: | |||
return 8; | |||
case DataTypes.FLOAT: | |||
return 4; | |||
case DataTypes.SHORT_DATE_TIME: | |||
return 8; | |||
case DataTypes.BINARY: | |||
return 255; | |||
case DataTypes.TEXT: | |||
return 50 * 2; | |||
case DataTypes.OLE: | |||
return _format.SIZE_LONG_VALUE_DEF; | |||
case DataTypes.MEMO: | |||
return _format.SIZE_LONG_VALUE_DEF; | |||
case DataTypes.NUMERIC: | |||
throw new IllegalArgumentException("FIX ME"); | |||
case DataTypes.UNKNOWN_0D: | |||
case DataTypes.GUID: | |||
throw new IllegalArgumentException("FIX ME"); | |||
default: | |||
throw new IllegalArgumentException("Unrecognized data type: " + _type); | |||
if (_type == DataType.BOOLEAN) { | |||
return 0; | |||
} else if (_type == DataType.BYTE) { | |||
return 1; | |||
} else if (_type == DataType.INT) { | |||
return 2; | |||
} else if (_type == DataType.LONG) { | |||
return 4; | |||
} else if (_type == DataType.MONEY || _type == DataType.DOUBLE) { | |||
return 8; | |||
} else if (_type == DataType.FLOAT) { | |||
return 4; | |||
} else if (_type == DataType.SHORT_DATE_TIME) { | |||
return 8; | |||
} else if (_type == DataType.BINARY) { | |||
return 255; | |||
} else if (_type == DataType.TEXT) { | |||
return 50 * 2; | |||
} else if (_type == DataType.OLE) { | |||
return _format.SIZE_LONG_VALUE_DEF; | |||
} else if (_type == DataType.MEMO) { | |||
return _format.SIZE_LONG_VALUE_DEF; | |||
} else if (_type == DataType.NUMERIC) { | |||
throw new IllegalArgumentException("FIX ME"); | |||
} else if (_type == DataType.UNKNOWN_0D || _type == DataType.GUID) { | |||
throw new IllegalArgumentException("FIX ME"); | |||
} else { | |||
throw new IllegalArgumentException("Unrecognized data type: " + _type); | |||
} | |||
} | |||
public String toString() { | |||
StringBuffer rtn = new StringBuffer(); | |||
rtn.append("\tName: " + _name); | |||
rtn.append("\n\tType: 0x" + Integer.toHexString((int)_type)); | |||
rtn.append("\n\tType: 0x" + Integer.toHexString((int)_type.getValue())); | |||
rtn.append("\n\tNumber: " + _columnNumber); | |||
rtn.append("\n\tLength: " + _columnLength); | |||
rtn.append("\n\tVariable length: " + _variableLength); |
@@ -1,94 +0,0 @@ | |||
/* | |||
Copyright (c) 2005 Health Market Science, Inc. | |||
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 | |||
You can contact Health Market Science at info@healthmarketscience.com | |||
or at the following address: | |||
Health Market Science | |||
2700 Horizon Drive | |||
Suite 200 | |||
King of Prussia, PA 19406 | |||
*/ | |||
package com.healthmarketscience.jackcess; | |||
import java.sql.SQLException; | |||
import java.sql.Types; | |||
import org.apache.commons.collections.bidimap.DualHashBidiMap; | |||
import org.apache.commons.collections.BidiMap; | |||
/** | |||
* Access data types | |||
* @author Tim McCune | |||
*/ | |||
public final class DataTypes { | |||
public static final byte BOOLEAN = 0x01; | |||
public static final byte BYTE = 0x02; | |||
public static final byte INT = 0x03; | |||
public static final byte LONG = 0x04; | |||
public static final byte MONEY = 0x05; | |||
public static final byte FLOAT = 0x06; | |||
public static final byte DOUBLE = 0x07; | |||
public static final byte SHORT_DATE_TIME = 0x08; | |||
public static final byte BINARY = 0x09; | |||
public static final byte TEXT = 0x0A; | |||
public static final byte OLE = 0x0B; | |||
public static final byte MEMO = 0x0C; | |||
public static final byte UNKNOWN_0D = 0x0D; | |||
public static final byte GUID = 0x0F; | |||
public static final byte NUMERIC = 0x10; | |||
/** Map of Access data types to SQL data types */ | |||
private static BidiMap SQL_TYPES = new DualHashBidiMap(); | |||
static { | |||
SQL_TYPES.put(new Byte(BOOLEAN), new Integer(Types.BOOLEAN)); | |||
SQL_TYPES.put(new Byte(BYTE), new Integer(Types.TINYINT)); | |||
SQL_TYPES.put(new Byte(INT), new Integer(Types.SMALLINT)); | |||
SQL_TYPES.put(new Byte(LONG), new Integer(Types.INTEGER)); | |||
SQL_TYPES.put(new Byte(MONEY), new Integer(Types.DECIMAL)); | |||
SQL_TYPES.put(new Byte(FLOAT), new Integer(Types.FLOAT)); | |||
SQL_TYPES.put(new Byte(DOUBLE), new Integer(Types.DOUBLE)); | |||
SQL_TYPES.put(new Byte(SHORT_DATE_TIME), new Integer(Types.TIMESTAMP)); | |||
SQL_TYPES.put(new Byte(BINARY), new Integer(Types.BINARY)); | |||
SQL_TYPES.put(new Byte(TEXT), new Integer(Types.VARCHAR)); | |||
SQL_TYPES.put(new Byte(OLE), new Integer(Types.LONGVARBINARY)); | |||
SQL_TYPES.put(new Byte(MEMO), new Integer(Types.LONGVARCHAR)); | |||
} | |||
private DataTypes() {} | |||
public static int toSQLType(byte dataType) throws SQLException { | |||
Integer i = (Integer) SQL_TYPES.get(new Byte(dataType)); | |||
if (i != null) { | |||
return i.intValue(); | |||
} else { | |||
throw new SQLException("Unsupported data type: " + dataType); | |||
} | |||
} | |||
public static byte fromSQLType(int sqlType) throws SQLException { | |||
Byte b = (Byte) SQL_TYPES.getKey(new Integer(sqlType)); | |||
if (b != null) { | |||
return b.byteValue(); | |||
} else { | |||
throw new SQLException("Unsupported SQL type: " + sqlType); | |||
} | |||
} | |||
} |
@@ -75,7 +75,7 @@ public class Database { | |||
/** System catalog always lives on page 2 */ | |||
private static final int PAGE_SYSTEM_CATALOG = 2; | |||
private static final Integer ACM = new Integer(1048319); | |||
private static final int ACM = 1048319; | |||
/** Free space left in page for new usage map definition pages */ | |||
private static final short USAGE_MAP_DEF_FREE_SPACE = 3940; | |||
@@ -112,16 +112,16 @@ public class Database { | |||
/** Name of the table that contains system access control entries */ | |||
private static final String TABLE_SYSTEM_ACES = "MSysACEs"; | |||
/** System object type for table definitions */ | |||
private static final Short TYPE_TABLE = new Short((short) 1); | |||
private static final Short TYPE_TABLE = (short) 1; | |||
/** | |||
* All of the reserved words in Access that should be escaped when creating | |||
* table or column names (String) | |||
* table or column names | |||
*/ | |||
private static final Set RESERVED_WORDS = new HashSet(); | |||
private static final Set<String> RESERVED_WORDS = new HashSet<String>(); | |||
static { | |||
//Yup, there's a lot. | |||
RESERVED_WORDS.addAll(Arrays.asList(new String[] { | |||
RESERVED_WORDS.addAll(Arrays.asList( | |||
"add", "all", "alphanumeric", "alter", "and", "any", "application", "as", | |||
"asc", "assistant", "autoincrement", "avg", "between", "binary", "bit", | |||
"boolean", "by", "byte", "char", "character", "column", "compactdatabase", | |||
@@ -152,7 +152,7 @@ public class Database { | |||
"union", "unique", "update", "user", "value", "values", "var", "varp", | |||
"varbinary", "varchar", "where", "with", "workspace", "xor", "year", "yes", | |||
"yesno" | |||
})); | |||
)); | |||
} | |||
/** Buffer to hold database pages */ | |||
@@ -163,9 +163,8 @@ public class Database { | |||
private JetFormat _format; | |||
/** | |||
* Map of table names to page numbers containing their definition | |||
* (String -> Integer) | |||
*/ | |||
private Map _tables = new HashMap(); | |||
private Map<String, Integer> _tables = new HashMap<String, Integer>(); | |||
/** Reads and writes database pages */ | |||
private PageChannel _pageChannel; | |||
/** System catalog table */ | |||
@@ -177,7 +176,7 @@ public class Database { | |||
* Open an existing Database | |||
* @param mdbFile File containing the database | |||
*/ | |||
public static Database open(File mdbFile) throws IOException { | |||
public static Database open(File mdbFile) throws IOException, SQLException { | |||
return new Database(openChannel(mdbFile)); | |||
} | |||
@@ -186,7 +185,7 @@ public class Database { | |||
* @param mdbFile Location to write the new database to. <b>If this file | |||
* already exists, it will be overwritten.</b> | |||
*/ | |||
public static Database create(File mdbFile) throws IOException { | |||
public static Database create(File mdbFile) throws IOException, SQLException { | |||
FileChannel channel = openChannel(mdbFile); | |||
channel.transferFrom(Channels.newChannel( | |||
Thread.currentThread().getContextClassLoader().getResourceAsStream( | |||
@@ -204,7 +203,7 @@ public class Database { | |||
* FileChannel instead of a ReadableByteChannel because we need to | |||
* randomly jump around to various points in the file. | |||
*/ | |||
protected Database(FileChannel channel) throws IOException { | |||
protected Database(FileChannel channel) throws IOException, SQLException { | |||
_format = JetFormat.getFormat(channel); | |||
_pageChannel = new PageChannel(channel, _format); | |||
_buffer = _pageChannel.createPageBuffer(); | |||
@@ -229,7 +228,7 @@ public class Database { | |||
/** | |||
* Read the system catalog | |||
*/ | |||
private void readSystemCatalog() throws IOException { | |||
private void readSystemCatalog() throws IOException, SQLException { | |||
_pageChannel.readPage(_buffer, PAGE_SYSTEM_CATALOG); | |||
byte pageType = _buffer.get(); | |||
if (pageType != PageTypes.TABLE_DEF) { | |||
@@ -239,12 +238,12 @@ public class Database { | |||
_systemCatalog = new Table(_buffer, _pageChannel, _format, PAGE_SYSTEM_CATALOG); | |||
Map row; | |||
while ( (row = _systemCatalog.getNextRow(Arrays.asList( | |||
new String[] {COL_NAME, COL_TYPE, COL_ID}))) != null) | |||
COL_NAME, COL_TYPE, COL_ID))) != null) | |||
{ | |||
String name = (String) row.get(COL_NAME); | |||
if (name != null && TYPE_TABLE.equals(row.get(COL_TYPE))) { | |||
if (!name.startsWith(PREFIX_SYSTEM)) { | |||
_tables.put(row.get(COL_NAME), row.get(COL_ID)); | |||
_tables.put((String) row.get(COL_NAME), (Integer) row.get(COL_ID)); | |||
} else if (TABLE_SYSTEM_ACES.equals(name)) { | |||
readAccessControlEntries(((Integer) row.get(COL_ID)).intValue()); | |||
} | |||
@@ -261,7 +260,7 @@ public class Database { | |||
* Read the system access control entries table | |||
* @param pageNum Page number of the table def | |||
*/ | |||
private void readAccessControlEntries(int pageNum) throws IOException { | |||
private void readAccessControlEntries(int pageNum) throws IOException, SQLException { | |||
ByteBuffer buffer = _pageChannel.createPageBuffer(); | |||
_pageChannel.readPage(buffer, pageNum); | |||
byte pageType = buffer.get(); | |||
@@ -283,7 +282,7 @@ public class Database { | |||
* @param name Table name | |||
* @return The table, or null if it doesn't exist | |||
*/ | |||
public Table getTable(String name) throws IOException { | |||
public Table getTable(String name) throws IOException, SQLException { | |||
Integer pageNumber = (Integer) _tables.get(name); | |||
if (pageNumber == null) { | |||
@@ -306,7 +305,7 @@ public class Database { | |||
* @param columns List of Columns in the table | |||
*/ | |||
//XXX Set up 1-page rollback buffer? | |||
public void createTable(String name, List columns) throws IOException { | |||
public void createTable(String name, List<Column> columns) throws IOException { | |||
//There is some really bizarre bug in here where tables that start with | |||
//the letters a-m (only lower case) won't open in Access. :) | |||
@@ -345,7 +344,8 @@ public class Database { | |||
* @param columns List of Columns in the table | |||
* @param pageNumber Page number that this table definition will be written to | |||
*/ | |||
private void writeTableDefinition(ByteBuffer buffer, List columns, int pageNumber) | |||
private void writeTableDefinition(ByteBuffer buffer, List<Column> columns, | |||
int pageNumber) | |||
throws IOException { | |||
//Start writing the tdef | |||
buffer.put(PageTypes.TABLE_DEF); //Page type | |||
@@ -395,12 +395,12 @@ public class Database { | |||
for (iter = columns.iterator(); iter.hasNext(); columnNumber++) { | |||
Column col = (Column) iter.next(); | |||
int position = buffer.position(); | |||
buffer.put(col.getType()); | |||
buffer.put(col.getType().getValue()); | |||
buffer.put((byte) 0x59); //Unknown | |||
buffer.put((byte) 0x06); //Unknown | |||
buffer.putShort((short) 0); //Unknown | |||
buffer.putShort(columnNumber); //Column Number | |||
if (col.isVariableLength()) { | |||
if (col.getType().isVariableLength()) { | |||
buffer.putShort(variableOffset++); | |||
} else { | |||
buffer.putShort((short) 0); | |||
@@ -409,7 +409,7 @@ public class Database { | |||
buffer.put((byte) 0x09); //Unknown | |||
buffer.put((byte) 0x04); //Unknown | |||
buffer.putShort((short) 0); //Unknown | |||
if (col.isVariableLength()) { //Variable length | |||
if (col.getType().isVariableLength()) { //Variable length | |||
buffer.put((byte) 0x2); | |||
} else { | |||
buffer.put((byte) 0x3); | |||
@@ -421,11 +421,11 @@ public class Database { | |||
} | |||
buffer.putInt(0); //Unknown, but always 0. | |||
//Offset for fixed length columns | |||
if (col.isVariableLength()) { | |||
if (col.getType().isVariableLength()) { | |||
buffer.putShort((short) 0); | |||
} else { | |||
buffer.putShort(fixedOffset); | |||
fixedOffset += col.size(); | |||
fixedOffset += col.getType().getSize(); | |||
} | |||
buffer.putShort(col.getLength()); //Column length | |||
if (LOG.isDebugEnabled()) { | |||
@@ -542,7 +542,7 @@ public class Database { | |||
*/ | |||
public void copyTable(String name, ResultSet source) throws SQLException, IOException { | |||
ResultSetMetaData md = source.getMetaData(); | |||
List columns = new LinkedList(); | |||
List<Column> columns = new LinkedList<Column>(); | |||
int textCount = 0; | |||
int totalSize = 0; | |||
for (int i = 1; i <= md.getColumnCount(); i++) { | |||
@@ -562,23 +562,23 @@ public class Database { | |||
} | |||
short textSize = 0; | |||
if (textCount > 0) { | |||
textSize = (short) ((_format.MAX_RECORD_SIZE - totalSize) / textCount); | |||
if (textSize > _format.TEXT_FIELD_MAX_LENGTH) { | |||
textSize = _format.TEXT_FIELD_MAX_LENGTH; | |||
textSize = (short) ((JetFormat.MAX_RECORD_SIZE - totalSize) / textCount); | |||
if (textSize > JetFormat.TEXT_FIELD_MAX_LENGTH) { | |||
textSize = JetFormat.TEXT_FIELD_MAX_LENGTH; | |||
} | |||
} | |||
for (int i = 1; i <= md.getColumnCount(); i++) { | |||
Column column = new Column(); | |||
column.setName(escape(md.getColumnName(i))); | |||
column.setType(DataTypes.fromSQLType(md.getColumnType(i))); | |||
if (column.getType() == DataTypes.TEXT) { | |||
column.setType(DataType.fromSQLType(md.getColumnType(i))); | |||
if (column.getType() == DataType.TEXT) { | |||
column.setLength(textSize); | |||
} | |||
columns.add(column); | |||
} | |||
createTable(escape(name), columns); | |||
Table table = getTable(escape(name)); | |||
List rows = new ArrayList(); | |||
List<Object[]> rows = new ArrayList<Object[]>(); | |||
while (source.next()) { | |||
Object[] row = new Object[md.getColumnCount()]; | |||
for (int i = 0; i < row.length; i++) { | |||
@@ -601,26 +601,18 @@ public class Database { | |||
* @param f Source file to import | |||
* @param delim Regular expression representing the delimiter string. | |||
*/ | |||
public void importFile(String name, File f, | |||
String delim) | |||
throws IOException | |||
public void importFile(String name, File f, String delim) | |||
throws IOException, SQLException | |||
{ | |||
BufferedReader in = null; | |||
try | |||
{ | |||
try { | |||
in = new BufferedReader(new FileReader(f)); | |||
importReader(name, in, delim); | |||
} | |||
finally | |||
{ | |||
if (in != null) | |||
{ | |||
try | |||
{ | |||
} finally { | |||
if (in != null) { | |||
try { | |||
in.close(); | |||
} | |||
catch (IOException ex) | |||
{ | |||
} catch (IOException ex) { | |||
LOG.warn("Could not close file " + f.getAbsolutePath(), ex); | |||
} | |||
} | |||
@@ -634,42 +626,39 @@ public class Database { | |||
* @param in Source reader to import | |||
* @param delim Regular expression representing the delimiter string. | |||
*/ | |||
public void importReader(String name, BufferedReader in, | |||
String delim) | |||
throws IOException | |||
public void importReader(String name, BufferedReader in, String delim) | |||
throws IOException, SQLException | |||
{ | |||
String line = in.readLine(); | |||
if (line == null || line.trim().length() == 0) | |||
{ | |||
if (line == null || line.trim().length() == 0) { | |||
return; | |||
} | |||
String tableName = escape(name); | |||
int counter = 0; | |||
while(getTable(tableName) != null) | |||
{ | |||
while(getTable(tableName) != null) { | |||
tableName = escape(name + (counter++)); | |||
} | |||
List columns = new LinkedList(); | |||
List<Column> columns = new LinkedList<Column>(); | |||
String[] columnNames = line.split(delim); | |||
short textSize = (short) ((_format.MAX_RECORD_SIZE) / columnNames.length); | |||
if (textSize > _format.TEXT_FIELD_MAX_LENGTH) { | |||
textSize = _format.TEXT_FIELD_MAX_LENGTH; | |||
short textSize = (short) ((JetFormat.MAX_RECORD_SIZE) / columnNames.length); | |||
if (textSize > JetFormat.TEXT_FIELD_MAX_LENGTH) { | |||
textSize = JetFormat.TEXT_FIELD_MAX_LENGTH; | |||
} | |||
for (int i = 0; i < columnNames.length; i++) { | |||
Column column = new Column(); | |||
column.setName(escape(columnNames[i])); | |||
column.setType(DataTypes.TEXT); | |||
column.setType(DataType.TEXT); | |||
column.setLength(textSize); | |||
columns.add(column); | |||
} | |||
createTable(tableName, columns); | |||
Table table = getTable(tableName); | |||
List rows = new ArrayList(); | |||
List<String[]> rows = new ArrayList<String[]>(); | |||
while ((line = in.readLine()) != null) | |||
{ |
@@ -45,7 +45,7 @@ import org.apache.commons.lang.builder.CompareToBuilder; | |||
* Access table index | |||
* @author Tim McCune | |||
*/ | |||
public class Index implements Comparable { | |||
public class Index implements Comparable<Index> { | |||
/** Max number of columns in an index */ | |||
private static final int MAX_COLUMNS = 10; | |||
@@ -59,68 +59,68 @@ public class Index implements Comparable { | |||
private static BidiMap CODES = new DualHashBidiMap(); | |||
static { | |||
//These values are prefixed with a '43' | |||
CODES.put(new Character('^'), new Byte((byte) 2)); | |||
CODES.put(new Character('_'), new Byte((byte) 3)); | |||
CODES.put(new Character('{'), new Byte((byte) 9)); | |||
CODES.put(new Character('|'), new Byte((byte) 11)); | |||
CODES.put(new Character('}'), new Byte((byte) 13)); | |||
CODES.put(new Character('~'), new Byte((byte) 15)); | |||
CODES.put('^', (byte) 2); | |||
CODES.put('_', (byte) 3); | |||
CODES.put('{', (byte) 9); | |||
CODES.put('|', (byte) 11); | |||
CODES.put('}', (byte) 13); | |||
CODES.put('~', (byte) 15); | |||
//These values aren't. | |||
CODES.put(new Character(' '), new Byte((byte) 7)); | |||
CODES.put(new Character('#'), new Byte((byte) 12)); | |||
CODES.put(new Character('$'), new Byte((byte) 14)); | |||
CODES.put(new Character('%'), new Byte((byte) 16)); | |||
CODES.put(new Character('&'), new Byte((byte) 18)); | |||
CODES.put(new Character('('), new Byte((byte) 20)); | |||
CODES.put(new Character(')'), new Byte((byte) 22)); | |||
CODES.put(new Character('*'), new Byte((byte) 24)); | |||
CODES.put(new Character(','), new Byte((byte) 26)); | |||
CODES.put(new Character('/'), new Byte((byte) 30)); | |||
CODES.put(new Character(':'), new Byte((byte) 32)); | |||
CODES.put(new Character(';'), new Byte((byte) 34)); | |||
CODES.put(new Character('?'), new Byte((byte) 36)); | |||
CODES.put(new Character('@'), new Byte((byte) 38)); | |||
CODES.put(new Character('+'), new Byte((byte) 44)); | |||
CODES.put(new Character('<'), new Byte((byte) 46)); | |||
CODES.put(new Character('='), new Byte((byte) 48)); | |||
CODES.put(new Character('>'), new Byte((byte) 50)); | |||
CODES.put(new Character('0'), new Byte((byte) 54)); | |||
CODES.put(new Character('1'), new Byte((byte) 56)); | |||
CODES.put(new Character('2'), new Byte((byte) 58)); | |||
CODES.put(new Character('3'), new Byte((byte) 60)); | |||
CODES.put(new Character('4'), new Byte((byte) 62)); | |||
CODES.put(new Character('5'), new Byte((byte) 64)); | |||
CODES.put(new Character('6'), new Byte((byte) 66)); | |||
CODES.put(new Character('7'), new Byte((byte) 68)); | |||
CODES.put(new Character('8'), new Byte((byte) 70)); | |||
CODES.put(new Character('9'), new Byte((byte) 72)); | |||
CODES.put(new Character('A'), new Byte((byte) 74)); | |||
CODES.put(new Character('B'), new Byte((byte) 76)); | |||
CODES.put(new Character('C'), new Byte((byte) 77)); | |||
CODES.put(new Character('D'), new Byte((byte) 79)); | |||
CODES.put(new Character('E'), new Byte((byte) 81)); | |||
CODES.put(new Character('F'), new Byte((byte) 83)); | |||
CODES.put(new Character('G'), new Byte((byte) 85)); | |||
CODES.put(new Character('H'), new Byte((byte) 87)); | |||
CODES.put(new Character('I'), new Byte((byte) 89)); | |||
CODES.put(new Character('J'), new Byte((byte) 91)); | |||
CODES.put(new Character('K'), new Byte((byte) 92)); | |||
CODES.put(new Character('L'), new Byte((byte) 94)); | |||
CODES.put(new Character('M'), new Byte((byte) 96)); | |||
CODES.put(new Character('N'), new Byte((byte) 98)); | |||
CODES.put(new Character('O'), new Byte((byte) 100)); | |||
CODES.put(new Character('P'), new Byte((byte) 102)); | |||
CODES.put(new Character('Q'), new Byte((byte) 104)); | |||
CODES.put(new Character('R'), new Byte((byte) 105)); | |||
CODES.put(new Character('S'), new Byte((byte) 107)); | |||
CODES.put(new Character('T'), new Byte((byte) 109)); | |||
CODES.put(new Character('U'), new Byte((byte) 111)); | |||
CODES.put(new Character('V'), new Byte((byte) 113)); | |||
CODES.put(new Character('W'), new Byte((byte) 115)); | |||
CODES.put(new Character('X'), new Byte((byte) 117)); | |||
CODES.put(new Character('Y'), new Byte((byte) 118)); | |||
CODES.put(new Character('Z'), new Byte((byte) 120)); | |||
CODES.put(' ', (byte) 7); | |||
CODES.put('#', (byte) 12); | |||
CODES.put('$', (byte) 14); | |||
CODES.put('%', (byte) 16); | |||
CODES.put('&', (byte) 18); | |||
CODES.put('(', (byte) 20); | |||
CODES.put(')', (byte) 22); | |||
CODES.put('*', (byte) 24); | |||
CODES.put(',', (byte) 26); | |||
CODES.put('/', (byte) 30); | |||
CODES.put(':', (byte) 32); | |||
CODES.put(';', (byte) 34); | |||
CODES.put('?', (byte) 36); | |||
CODES.put('@', (byte) 38); | |||
CODES.put('+', (byte) 44); | |||
CODES.put('<', (byte) 46); | |||
CODES.put('=', (byte) 48); | |||
CODES.put('>', (byte) 50); | |||
CODES.put('0', (byte) 54); | |||
CODES.put('1', (byte) 56); | |||
CODES.put('2', (byte) 58); | |||
CODES.put('3', (byte) 60); | |||
CODES.put('4', (byte) 62); | |||
CODES.put('5', (byte) 64); | |||
CODES.put('6', (byte) 66); | |||
CODES.put('7', (byte) 68); | |||
CODES.put('8', (byte) 70); | |||
CODES.put('9', (byte) 72); | |||
CODES.put('A', (byte) 74); | |||
CODES.put('B', (byte) 76); | |||
CODES.put('C', (byte) 77); | |||
CODES.put('D', (byte) 79); | |||
CODES.put('E', (byte) 81); | |||
CODES.put('F', (byte) 83); | |||
CODES.put('G', (byte) 85); | |||
CODES.put('H', (byte) 87); | |||
CODES.put('I', (byte) 89); | |||
CODES.put('J', (byte) 91); | |||
CODES.put('K', (byte) 92); | |||
CODES.put('L', (byte) 94); | |||
CODES.put('M', (byte) 96); | |||
CODES.put('N', (byte) 98); | |||
CODES.put('O', (byte) 100); | |||
CODES.put('P', (byte) 102); | |||
CODES.put('Q', (byte) 104); | |||
CODES.put('R', (byte) 105); | |||
CODES.put('S', (byte) 107); | |||
CODES.put('T', (byte) 109); | |||
CODES.put('U', (byte) 111); | |||
CODES.put('V', (byte) 113); | |||
CODES.put('W', (byte) 115); | |||
CODES.put('X', (byte) 117); | |||
CODES.put('Y', (byte) 118); | |||
CODES.put('Z', (byte) 120); | |||
} | |||
/** Page number of the index data */ | |||
@@ -129,10 +129,9 @@ public class Index implements Comparable { | |||
/** Number of rows in the index */ | |||
private int _rowCount; | |||
private JetFormat _format; | |||
private List _allColumns; | |||
private SortedSet _entries = new TreeSet(); | |||
/** Map of columns to order (Column -> Byte) */ | |||
private Map _columns = new LinkedHashMap(); | |||
private SortedSet<Entry> _entries = new TreeSet<Entry>(); | |||
/** Map of columns to order */ | |||
private Map<Column, Byte> _columns = new LinkedHashMap<Column, Byte>(); | |||
private PageChannel _pageChannel; | |||
/** 0-based index number */ | |||
private int _indexNumber; | |||
@@ -205,10 +204,9 @@ public class Index implements Comparable { | |||
* @param buffer Buffer to read from | |||
* @param availableColumns Columns that this index may use | |||
*/ | |||
public void read(ByteBuffer buffer, List availableColumns) | |||
public void read(ByteBuffer buffer, List<Column> availableColumns) | |||
throws IOException | |||
{ | |||
_allColumns = availableColumns; | |||
for (int i = 0; i < MAX_COLUMNS; i++) { | |||
short columnNumber = buffer.getShort(); | |||
Byte order = new Byte(buffer.get()); | |||
@@ -257,8 +255,7 @@ public class Index implements Comparable { | |||
return rtn.toString(); | |||
} | |||
public int compareTo(Object obj) { | |||
Index other = (Index) obj; | |||
public int compareTo(Index other) { | |||
if (_indexNumber > other.getIndexNumber()) { | |||
return 1; | |||
} else if (_indexNumber < other.getIndexNumber()) { | |||
@@ -271,14 +268,14 @@ public class Index implements Comparable { | |||
/** | |||
* A single entry in an index (points to a single row) | |||
*/ | |||
private class Entry implements Comparable { | |||
private class Entry implements Comparable<Entry> { | |||
/** Page number on which the row is stored */ | |||
private int _page; | |||
/** Row number at which the row is stored */ | |||
private byte _row; | |||
/** Columns that are indexed */ | |||
private List _entryColumns = new ArrayList(); | |||
private List<EntryColumn> _entryColumns = new ArrayList<EntryColumn>(); | |||
/** | |||
* Create a new entry | |||
@@ -351,11 +348,10 @@ public class Index implements Comparable { | |||
return ("Page = " + _page + ", Row = " + _row + ", Columns = " + _entryColumns + "\n"); | |||
} | |||
public int compareTo(Object obj) { | |||
if (this == obj) { | |||
public int compareTo(Entry other) { | |||
if (this == other) { | |||
return 0; | |||
} | |||
Entry other = (Entry) obj; | |||
Iterator myIter = _entryColumns.iterator(); | |||
Iterator otherIter = other.getEntryColumns().iterator(); | |||
while (myIter.hasNext()) { | |||
@@ -380,7 +376,7 @@ public class Index implements Comparable { | |||
* A single column value within an index Entry; encapsulates column | |||
* definition and column value. | |||
*/ | |||
private class EntryColumn implements Comparable { | |||
private class EntryColumn implements Comparable<EntryColumn> { | |||
/** Column definition */ | |||
private Column _column; | |||
@@ -402,7 +398,7 @@ public class Index implements Comparable { | |||
_column = col; | |||
byte flag = buffer.get(); | |||
if (flag != (byte) 0) { | |||
if (col.getType() == DataTypes.TEXT) { | |||
if (col.getType() == DataType.TEXT) { | |||
StringBuffer sb = new StringBuffer(); | |||
byte b; | |||
while ( (b = buffer.get()) != (byte) 1) { | |||
@@ -417,7 +413,7 @@ public class Index implements Comparable { | |||
buffer.get(); //Forward past 0x00 | |||
_value = sb.toString(); | |||
} else { | |||
byte[] data = new byte[col.size()]; | |||
byte[] data = new byte[col.getType().getSize()]; | |||
buffer.get(data); | |||
_value = (Comparable) col.read(data, ByteOrder.BIG_ENDIAN); | |||
//ints and shorts are stored in index as value + 2147483648 | |||
@@ -439,7 +435,7 @@ public class Index implements Comparable { | |||
*/ | |||
public void write(ByteBuffer buffer) throws IOException { | |||
buffer.put((byte) 0x7F); | |||
if (_column.getType() == DataTypes.TEXT) { | |||
if (_column.getType() == DataType.TEXT) { | |||
String s = (String) _value; | |||
for (int i = 0; i < s.length(); i++) { | |||
Byte b = (Byte) CODES.get(new Character(Character.toUpperCase(s.charAt(i)))); | |||
@@ -489,7 +485,7 @@ public class Index implements Comparable { | |||
} | |||
return rtn; | |||
} else { | |||
return _column.size(); | |||
return _column.getType().getSize(); | |||
} | |||
} | |||
@@ -497,8 +493,8 @@ public class Index implements Comparable { | |||
return String.valueOf(_value); | |||
} | |||
public int compareTo(Object obj) { | |||
return new CompareToBuilder().append(_value, ((EntryColumn) obj).getValue()) | |||
public int compareTo(EntryColumn other) { | |||
return new CompareToBuilder().append(_value, other.getValue()) | |||
.toComparison(); | |||
} | |||
} |
@@ -29,6 +29,7 @@ package com.healthmarketscience.jackcess; | |||
import java.io.IOException; | |||
import java.nio.ByteBuffer; | |||
import java.sql.SQLException; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
@@ -74,10 +75,10 @@ public class Table { | |||
private short _columnCount; | |||
/** Format of the database that contains this table */ | |||
private JetFormat _format; | |||
/** List of columns in this table (Column) */ | |||
private List _columns = new ArrayList(); | |||
/** List of indexes on this table (Index) */ | |||
private List _indexes = new ArrayList(); | |||
/** List of columns in this table */ | |||
private List<Column> _columns = new ArrayList<Column>(); | |||
/** List of indexes on this table */ | |||
private List<Index> _indexes = new ArrayList<Index>(); | |||
/** Used to read in pages */ | |||
private PageChannel _pageChannel; | |||
/** Usage map of pages that this table owns */ | |||
@@ -99,7 +100,7 @@ public class Table { | |||
* @param pageNumber Page number of the table definition | |||
*/ | |||
protected Table(ByteBuffer buffer, PageChannel pageChannel, JetFormat format, int pageNumber) | |||
throws IOException | |||
throws IOException, SQLException | |||
{ | |||
_buffer = buffer; | |||
_pageChannel = pageChannel; | |||
@@ -121,7 +122,7 @@ public class Table { | |||
/** | |||
* Only called by unit tests | |||
*/ | |||
void setColumns(List columns) { | |||
void setColumns(List<Column> columns) { | |||
_columns = columns; | |||
} | |||
@@ -141,17 +142,19 @@ public class Table { | |||
} | |||
/** | |||
* @return The next row in this table (Column name (String) -> Column value (Object)) | |||
* @return The next row in this table (Column name -> Column value) | |||
*/ | |||
public Map getNextRow() throws IOException { | |||
public Map<String, Object> getNextRow() throws IOException { | |||
return getNextRow(null); | |||
} | |||
/** | |||
* @param columnNames Only column names in this collection will be returned | |||
* @return The next row in this table (Column name (String) -> Column value (Object)) | |||
* @return The next row in this table (Column name -> Column value) | |||
*/ | |||
public Map getNextRow(Collection columnNames) throws IOException { | |||
public Map<String, Object> getNextRow(Collection<String> columnNames) | |||
throws IOException | |||
{ | |||
if (!positionAtNextRow()) { | |||
return null; | |||
} | |||
@@ -161,7 +164,7 @@ public class Table { | |||
_buffer.limit() - _buffer.position())); | |||
} | |||
short columnCount = _buffer.getShort(); //Number of columns in this table | |||
Map rtn = new LinkedHashMap(columnCount); | |||
Map<String, Object> rtn = new LinkedHashMap<String, Object>(columnCount); | |||
NullMask nullMask = new NullMask(columnCount); | |||
_buffer.position(_buffer.limit() - nullMask.byteSize()); //Null mask at end | |||
nullMask.read(_buffer); | |||
@@ -196,12 +199,12 @@ public class Table { | |||
Column column = (Column) iter.next(); | |||
boolean isNull = nullMask.isNull(columnNumber); | |||
Object value = null; | |||
if (column.getType() == DataTypes.BOOLEAN) { | |||
if (column.getType() == DataType.BOOLEAN) { | |||
value = new Boolean(!isNull); //Boolean values are stored in the null mask | |||
} else if (!isNull) { | |||
if (!column.isVariableLength()) { | |||
if (!column.getType().isVariableLength()) { | |||
//Read in fixed length column data | |||
columnData = new byte[column.size()]; | |||
columnData = new byte[column.getType().getSize()]; | |||
_buffer.get(columnData); | |||
} else { | |||
//Refer to already-read-in variable length data | |||
@@ -247,7 +250,7 @@ public class Table { | |||
/** | |||
* Read the table definition | |||
*/ | |||
private void readPage() throws IOException { | |||
private void readPage() throws IOException, SQLException { | |||
if (LOG.isDebugEnabled()) { | |||
_buffer.rewind(); | |||
LOG.debug("Table def block:\n" + ByteUtil.toHexString(_buffer, | |||
@@ -316,9 +319,7 @@ public class Table { | |||
* Add a single row to this table and write it to disk | |||
*/ | |||
public void addRow(Object[] row) throws IOException { | |||
List rows = new ArrayList(1); | |||
rows.add(row); | |||
addRows(rows); | |||
addRows(Collections.singletonList(row)); | |||
} | |||
/** | |||
@@ -429,7 +430,7 @@ public class Table { | |||
Iterator iter; | |||
int index = 0; | |||
Column col; | |||
List row = new ArrayList(Arrays.asList(rowArray)); | |||
List<Object> row = new ArrayList<Object>(Arrays.asList(rowArray)); | |||
//Append null for arrays that are too small | |||
for (int i = rowArray.length; i < _columnCount; i++) { | |||
@@ -438,13 +439,13 @@ public class Table { | |||
for (iter = _columns.iterator(); iter.hasNext() && index < row.size(); index++) { | |||
col = (Column) iter.next(); | |||
if (!col.isVariableLength()) { | |||
if (!col.getType().isVariableLength()) { | |||
//Fixed length column data comes first | |||
if (row.get(index) != null) { | |||
buffer.put(col.write(row.get(index))); | |||
} | |||
} | |||
if (col.getType() == DataTypes.BOOLEAN) { | |||
if (col.getType() == DataType.BOOLEAN) { | |||
if (row.get(index) != null) { | |||
if (!((Boolean) row.get(index)).booleanValue()) { | |||
//Booleans are stored in the null mask | |||
@@ -463,7 +464,7 @@ public class Table { | |||
for (iter = _columns.iterator(); iter.hasNext() && index < row.size(); index++) { | |||
col = (Column) iter.next(); | |||
short offset = (short) buffer.position(); | |||
if (col.isVariableLength()) { | |||
if (col.getType().isVariableLength()) { | |||
if (row.get(index) != null) { | |||
buffer.put(col.write(row.get(index))); | |||
} |
@@ -57,8 +57,8 @@ public abstract class UsageMap { | |||
private short _rowStart; | |||
/** Format of the database that contains this usage map */ | |||
private JetFormat _format; | |||
/** List of page numbers used (Integer) */ | |||
private List _pageNumbers = new ArrayList(); | |||
/** List of page numbers used */ | |||
private List<Integer> _pageNumbers = new ArrayList<Integer>(); | |||
/** Buffer that contains the usage map declaration page */ | |||
private ByteBuffer _dataBuffer; | |||
/** Used to read in pages */ | |||
@@ -125,7 +125,7 @@ public abstract class UsageMap { | |||
return _rowStart; | |||
} | |||
public List getPageNumbers() { | |||
public List<Integer> getPageNumbers() { | |||
return _pageNumbers; | |||
} | |||
@@ -36,6 +36,9 @@ package com.healthmarketscience.jackcess.scsu; | |||
public class EndOfInputException | |||
extends java.lang.Exception | |||
{ | |||
private static final long serialVersionUID = 1; | |||
public EndOfInputException(){ | |||
super("The input string or input byte array ended prematurely"); | |||
} |
@@ -35,6 +35,9 @@ package com.healthmarketscience.jackcess.scsu; | |||
*/ | |||
public class IllegalInputException extends java.lang.Exception | |||
{ | |||
private static final long serialVersionUID = 1; | |||
public IllegalInputException(){ | |||
super("The input character array or input byte array contained illegal sequences of bytes or characters"); | |||
} |
@@ -9,11 +9,6 @@ import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.healthmarketscience.jackcess.Column; | |||
import com.healthmarketscience.jackcess.DataTypes; | |||
import com.healthmarketscience.jackcess.Database; | |||
import com.healthmarketscience.jackcess.Table; | |||
import junit.framework.TestCase; | |||
/** | |||
@@ -31,25 +26,26 @@ public class DatabaseTest extends TestCase { | |||
private Database create() throws Exception { | |||
File tmp = File.createTempFile("databaseTest", ".mdb"); | |||
tmp.deleteOnExit(); | |||
//tmp.deleteOnExit(); | |||
return Database.create(tmp); | |||
} | |||
public void testGetColumns() throws Exception { | |||
List columns = open().getTable("Table1").getColumns(); | |||
assertEquals(9, columns.size()); | |||
checkColumn(columns, 0, "A", DataTypes.TEXT); | |||
checkColumn(columns, 1, "B", DataTypes.TEXT); | |||
checkColumn(columns, 2, "C", DataTypes.BYTE); | |||
checkColumn(columns, 3, "D", DataTypes.INT); | |||
checkColumn(columns, 4, "E", DataTypes.LONG); | |||
checkColumn(columns, 5, "F", DataTypes.DOUBLE); | |||
checkColumn(columns, 6, "G", DataTypes.SHORT_DATE_TIME); | |||
checkColumn(columns, 7, "H", DataTypes.MONEY); | |||
checkColumn(columns, 8, "I", DataTypes.BOOLEAN); | |||
checkColumn(columns, 0, "A", DataType.TEXT); | |||
checkColumn(columns, 1, "B", DataType.TEXT); | |||
checkColumn(columns, 2, "C", DataType.BYTE); | |||
checkColumn(columns, 3, "D", DataType.INT); | |||
checkColumn(columns, 4, "E", DataType.LONG); | |||
checkColumn(columns, 5, "F", DataType.DOUBLE); | |||
checkColumn(columns, 6, "G", DataType.SHORT_DATE_TIME); | |||
checkColumn(columns, 7, "H", DataType.MONEY); | |||
checkColumn(columns, 8, "I", DataType.BOOLEAN); | |||
} | |||
private void checkColumn(List columns, int columnNumber, String name, byte dataType) | |||
private void checkColumn(List columns, int columnNumber, String name, | |||
DataType dataType) | |||
throws Exception { | |||
Column column = (Column) columns.get(columnNumber); | |||
assertEquals(name, column.getName()); | |||
@@ -130,7 +126,7 @@ public class DatabaseTest extends TestCase { | |||
Database db = create(); | |||
createTestTable(db); | |||
int count = 1000; | |||
List rows = new ArrayList(count); | |||
List<Object[]> rows = new ArrayList<Object[]>(count); | |||
Object[] row = new Object[9]; | |||
row[0] = "Tim"; | |||
row[1] = "R"; | |||
@@ -160,42 +156,42 @@ public class DatabaseTest extends TestCase { | |||
} | |||
private void createTestTable(Database db) throws Exception { | |||
List columns = new ArrayList(); | |||
List<Column> columns = new ArrayList<Column>(); | |||
Column col = new Column(); | |||
col.setName("A"); | |||
col.setType(DataTypes.TEXT); | |||
col.setType(DataType.TEXT); | |||
columns.add(col); | |||
col = new Column(); | |||
col.setName("B"); | |||
col.setType(DataTypes.TEXT); | |||
col.setType(DataType.TEXT); | |||
columns.add(col); | |||
col = new Column(); | |||
col.setName("C"); | |||
col.setType(DataTypes.TEXT); | |||
col.setType(DataType.TEXT); | |||
columns.add(col); | |||
col = new Column(); | |||
col.setName("D"); | |||
col.setType(DataTypes.LONG); | |||
col.setType(DataType.LONG); | |||
columns.add(col); | |||
col = new Column(); | |||
col.setName("E"); | |||
col.setType(DataTypes.BYTE); | |||
col.setType(DataType.BYTE); | |||
columns.add(col); | |||
col = new Column(); | |||
col.setName("F"); | |||
col.setType(DataTypes.DOUBLE); | |||
col.setType(DataType.DOUBLE); | |||
columns.add(col); | |||
col = new Column(); | |||
col.setName("G"); | |||
col.setType(DataTypes.FLOAT); | |||
col.setType(DataType.FLOAT); | |||
columns.add(col); | |||
col = new Column(); | |||
col.setName("H"); | |||
col.setType(DataTypes.INT); | |||
col.setType(DataType.INT); | |||
columns.add(col); | |||
col = new Column(); | |||
col.setName("I"); | |||
col.setType(DataTypes.SHORT_DATE_TIME); | |||
col.setType(DataType.SHORT_DATE_TIME); | |||
columns.add(col); | |||
db.createTable("test", columns); | |||
} |
@@ -6,10 +6,6 @@ import java.nio.ByteBuffer; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import com.healthmarketscience.jackcess.Column; | |||
import com.healthmarketscience.jackcess.DataTypes; | |||
import com.healthmarketscience.jackcess.Table; | |||
import junit.framework.TestCase; | |||
/** | |||
@@ -23,12 +19,12 @@ public class TableTest extends TestCase { | |||
public void testCreateRow() throws Exception { | |||
Table table = new Table(); | |||
List columns = new ArrayList(); | |||
List<Column> columns = new ArrayList<Column>(); | |||
Column col = new Column(); | |||
col.setType(DataTypes.INT); | |||
col.setType(DataType.INT); | |||
columns.add(col); | |||
col = new Column(); | |||
col.setType(DataTypes.TEXT); | |||
col.setType(DataType.TEXT); | |||
columns.add(col); | |||
columns.add(col); | |||
table.setColumns(columns); |