aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2017-05-24 03:26:58 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2017-05-24 03:26:58 +0000
commit9ad8aaabc77b773bee4419edc794f63ee2a16dbb (patch)
tree5397bcbd54a681b98ed2a005357f3112354f9cb5
parent9a7f57b361e43603888d91901987bbfff4b72695 (diff)
parent4c087727dc6fd9739a44b6874001304b629938f4 (diff)
downloadjackcess-9ad8aaabc77b773bee4419edc794f63ee2a16dbb.tar.gz
jackcess-9ad8aaabc77b773bee4419edc794f63ee2a16dbb.zip
merge trunk changes through r1100
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/exprs@1101 f203690c-595d-4dc9-a70b-905162fa7fd2
-rw-r--r--pom.xml11
-rw-r--r--src/changes/changes.xml15
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java16
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java295
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java4
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/PropertiesTest.java7
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/TestUtil.java30
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/impl/AutoNumberTest.java24
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java7
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java159
-rw-r--r--src/test/resources/emptyJet4.mdbbin0 -> 77824 bytes
12 files changed, 530 insertions, 40 deletions
diff --git a/pom.xml b/pom.xml
index 9bf3ce6..1b8b3ec 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
<artifactId>jackcess</artifactId>
<name>Jackcess</name>
<description>A pure Java library for reading from and writing to MS Access databases.</description>
- <version>2.1.7-SNAPSHOT</version>
+ <version>2.1.8-SNAPSHOT</version>
<url>http://jackcess.sf.net</url>
<inceptionYear>2005</inceptionYear>
<developers>
@@ -128,10 +128,6 @@
<value>log4j_test.properties</value>
</property>
<property>
- <name>com.healthmarketscience.jackcess.bigIndex</name>
- <value>${jackcess.bigIndex}</value>
- </property>
- <property>
<name>com.healthmarketscience.jackcess.testFormats</name>
<value>${jackcess.testFormats}</value>
</property>
@@ -247,6 +243,7 @@
<issueLinkTemplatePerSystem>
<SourceForge2Features>http://sourceforge.net/p/jackcess/feature-requests/%ISSUE%</SourceForge2Features>
<SourceForge2Patches>http://sourceforge.net/p/jackcess/patches/%ISSUE%</SourceForge2Patches>
+ <GitHubPullRequests>https://github.com/jahlborn/jackcess/pull/%ISSUE%</GitHubPullRequests>
</issueLinkTemplatePerSystem>
</configuration>
</plugin>
@@ -312,8 +309,4 @@
<url>scp://shell.sourceforge.net/home/project-web/jackcess/htdocs</url>
</site>
</distributionManagement>
- <properties>
- <jackcess.bigIndex>true</jackcess.bigIndex>
- <jackcess.testFormats>V1997,V2000,V2003,V2007,V2010</jackcess.testFormats>
- </properties>
</project>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index a172b92..f2c0e5e 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -4,12 +4,25 @@
<author email="javajedi@users.sf.net">Tim McCune</author>
</properties>
<body>
- <release version="2.1.7" date="TBD">
+ <release version="2.1.7" date="2017-05-17">
<action dev="jahlborn" type="update">
Implement support for partial index lookups. Efficient IndexCursor
lookups can now be done with multi-column indexes using only some of
the columns in the index.
</action>
+ <action dev="jahlborn" type="update" system="GitHubPullRequests"
+ issue="2">
+ Cover the GENERIC_JET4 format in unit tests, thanks to Gord Thompson.
+ </action>
+ <action dev="jahlborn" type="update" system="GitHubPullRequests"
+ issue="3">
+ Allow inserting negative auto number fields, thanks to Gord Thompson.
+ </action>
+ <action dev="jahlborn" type="update" system="SourceForge2Features"
+ issue="36">
+ Add CustomLinkResolver which facilitates loading linked tables from
+ files which are not access databases.
+ </action>
</release>
<release version="2.1.6" date="2016-11-29">
<action dev="jahlborn" type="update" system="SourceForge2Features"
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
index 203ad82..02b69ea 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
@@ -2015,7 +2015,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
throws IOException
{
int inAutoNum = toNumber(inRowValue).intValue();
- if(inAutoNum <= INVALID_AUTO_NUMBER) {
+ if(inAutoNum <= INVALID_AUTO_NUMBER && !getTable().isAllowAutoNumberInsert()) {
throw new IOException(withErrorContext(
"Invalid auto number value " + inAutoNum));
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
index 319879b..1c66859 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
@@ -455,10 +455,10 @@ public class DatabaseImpl implements Database
boolean success = false;
try {
channel.truncate(0);
- transferFrom(channel, getResourceAsStream(details.getEmptyFilePath()));
+ transferDbFrom(channel, getResourceAsStream(details.getEmptyFilePath()));
channel.force(true);
DatabaseImpl db = new DatabaseImpl(mdbFile, channel, closeChannel, autoSync,
- fileFormat, charset, timeZone, null);
+ fileFormat, charset, timeZone, null);
success = true;
return db;
} finally {
@@ -509,8 +509,8 @@ public class DatabaseImpl implements Database
* @param timeZone TimeZone to use, if {@code null}, uses default
*/
protected DatabaseImpl(File file, FileChannel channel, boolean closeChannel,
- boolean autoSync, FileFormat fileFormat, Charset charset,
- TimeZone timeZone, CodecProvider provider)
+ boolean autoSync, FileFormat fileFormat, Charset charset,
+ TimeZone timeZone, CodecProvider provider)
throws IOException
{
_file = file;
@@ -984,7 +984,7 @@ public class DatabaseImpl implements Database
* @param includeSystemTables whether to consider returning a system table
* @return The table, or null if it doesn't exist
*/
- private TableImpl getTable(String name, boolean includeSystemTables)
+ protected TableImpl getTable(String name, boolean includeSystemTables)
throws IOException
{
TableInfo tableInfo = getTableInfo(name, includeSystemTables);
@@ -1961,10 +1961,10 @@ public class DatabaseImpl implements Database
}
/**
- * Copies the given InputStream to the given channel using the most
+ * Copies the given db InputStream to the given channel using the most
* efficient means possible.
*/
- private static void transferFrom(FileChannel channel, InputStream in)
+ protected static void transferDbFrom(FileChannel channel, InputStream in)
throws IOException
{
ReadableByteChannel readChannel = Channels.newChannel(in);
@@ -2004,7 +2004,7 @@ public class DatabaseImpl implements Database
return pwdMask;
}
- static InputStream getResourceAsStream(String resourceName)
+ protected static InputStream getResourceAsStream(String resourceName)
throws IOException
{
InputStream stream = DatabaseImpl.class.getClassLoader()
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java b/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java
new file mode 100644
index 0000000..7992d71
--- /dev/null
+++ b/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java
@@ -0,0 +1,295 @@
+/*
+Copyright (c) 2017 James Ahlborn
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package com.healthmarketscience.jackcess.util;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.util.Random;
+
+import com.healthmarketscience.jackcess.Database;
+import com.healthmarketscience.jackcess.Database.FileFormat;
+import com.healthmarketscience.jackcess.Table;
+import com.healthmarketscience.jackcess.impl.ByteUtil;
+import com.healthmarketscience.jackcess.impl.DatabaseImpl;
+import com.healthmarketscience.jackcess.impl.TableImpl;
+
+/**
+ * Utility base implementaton of LinkResolver which facilitates loading linked
+ * tables from files which are not access databases. The LinkResolver API
+ * ultimately presents linked table information to the primary database using
+ * the jackcess {@link Database} and {@link Table} classes. In order to
+ * consume linked tables in non-mdb files, they need to somehow be coerced
+ * into the appropriate form. The approach taken by this utility is to make
+ * it easy to copy the external tables into a temporary mdb file for
+ * consumption by the primary database.
+ * <p>
+ * The primary features of this utility:
+ * <ul>
+ * <li>Supports custom behavior for non-mdb files and default behavior for mdb
+ * files, see {@link #loadCustomFile}</li>
+ * <li>Temp db can be an actual file or entirely in memory</li>
+ * <li>Linked tables are loaded on-demand, see {@link #loadCustomTable}</li>
+ * <li>Temp db files will be automatically deleted on close</li>
+ * </ul>
+ *
+ * @author James Ahlborn
+ * @usage _intermediate_class_
+ */
+public abstract class CustomLinkResolver implements LinkResolver
+{
+ private static final Random DB_ID = new Random();
+
+ private static final String MEM_DB_PREFIX = "memdb_";
+ private static final String FILE_DB_PREFIX = "linkeddb_";
+
+ /** the default file format used for temp dbs */
+ public static final FileFormat DEFAULT_FORMAT = FileFormat.V2000;
+ /** temp dbs default to the filesystem, not in memory */
+ public static final boolean DEFAULT_IN_MEMORY = false;
+ /** temp dbs end up in the system temp dir by default */
+ public static final File DEFAULT_TEMP_DIR = null;
+
+ private final FileFormat _defaultFormat;
+ private final boolean _defaultInMemory;
+ private final File _defaultTempDir;
+
+ /**
+ * Creates a CustomLinkResolver using the default behavior for creating temp
+ * dbs, see {@link #DEFAULT_FORMAT}, {@link #DEFAULT_IN_MEMORY} and
+ * {@link #DEFAULT_TEMP_DIR}.
+ */
+ protected CustomLinkResolver() {
+ this(DEFAULT_FORMAT, DEFAULT_IN_MEMORY, DEFAULT_TEMP_DIR);
+ }
+
+ /**
+ * Creates a CustomLinkResolver with the given default behavior for creating
+ * temp dbs.
+ *
+ * @param defaultFormat the default format for the temp db
+ * @param defaultInMemory whether or not the temp db should be entirely in
+ * memory by default (while this will be faster, it
+ * should only be used if table data is expected to
+ * fit entirely in memory)
+ * @param defaultTempDir the default temp dir for a file based temp db
+ * ({@code null} for the system defaqult temp
+ * directory)
+ */
+ protected CustomLinkResolver(FileFormat defaultFormat, boolean defaultInMemory,
+ File defaultTempDir)
+ {
+ _defaultFormat = defaultFormat;
+ _defaultInMemory = defaultInMemory;
+ _defaultTempDir = defaultTempDir;
+ }
+
+ protected FileFormat getDefaultFormat() {
+ return _defaultFormat;
+ }
+
+ protected boolean isDefaultInMemory() {
+ return _defaultInMemory;
+ }
+
+ protected File getDefaultTempDirectory() {
+ return _defaultTempDir;
+ }
+
+ /**
+ * Custom implementation is:
+ * <pre>
+ * // attempt to load the linkeeFileName as a custom file
+ * Object customFile = loadCustomFile(linkerDb, linkeeFileName);
+ *
+ * if(customFile != null) {
+ * // this is a custom file, create and return relevant temp db
+ * return createTempDb(customFile, getDefaultFormat(), isDefaultInMemory(),
+ * getDefaultTempDirectory());
+ * }
+ *
+ * // not a custmom file, load using the default behavior
+ * return LinkResolver.DEFAULT.resolveLinkedDatabase(linkerDb, linkeeFileName);
+ * </pre>
+ *
+ * @see #loadCustomFile
+ * @see #createTempDb
+ * @see LinkResolver#DEFAULT
+ */
+ public Database resolveLinkedDatabase(Database linkerDb, String linkeeFileName)
+ throws IOException
+ {
+ Object customFile = loadCustomFile(linkerDb, linkeeFileName);
+ if(customFile != null) {
+ return createTempDb(customFile, getDefaultFormat(), isDefaultInMemory(),
+ getDefaultTempDirectory());
+ }
+ return LinkResolver.DEFAULT.resolveLinkedDatabase(linkerDb, linkeeFileName);
+ }
+
+ /**
+ * Creates a temporary database for holding the table data from
+ * linkeeFileName.
+ *
+ * @param customFile custom file state returned from {@link #loadCustomFile}
+ * @param format the access format for the temp db
+ * @param inMemory whether or not the temp db should be entirely in memory
+ * (while this will be faster, it should only be used if
+ * table data is expected to fit entirely in memory)
+ * @param tempDir the temp dir for a file based temp db ({@code null} for
+ * the system default temp directory)
+ *
+ * @return the temp db for holding the linked table info
+ */
+ protected Database createTempDb(Object customFile, FileFormat format,
+ boolean inMemory, File tempDir)
+ throws IOException
+ {
+ File dbFile = null;
+ FileChannel channel = null;
+ boolean success = false;
+ try {
+
+ if(inMemory) {
+ dbFile = new File(MEM_DB_PREFIX + DB_ID.nextLong() +
+ format.getFileExtension());
+ channel = MemFileChannel.newChannel();
+ } else {
+ dbFile = File.createTempFile(FILE_DB_PREFIX, format.getFileExtension(),
+ tempDir);
+ channel = new RandomAccessFile(dbFile, DatabaseImpl.RW_CHANNEL_MODE)
+ .getChannel();
+ }
+
+ TempDatabaseImpl.initDbChannel(channel, format);
+ TempDatabaseImpl db = new TempDatabaseImpl(this, customFile, dbFile,
+ channel, format);
+ success = true;
+ return db;
+
+ } finally {
+ if(!success) {
+ ByteUtil.closeQuietly(channel);
+ deleteDbFile(dbFile);
+ closeCustomFile(customFile);
+ }
+ }
+ }
+
+ private static void deleteDbFile(File dbFile) {
+ if((dbFile != null) && (dbFile.getName().startsWith(FILE_DB_PREFIX))) {
+ dbFile.delete();
+ }
+ }
+
+ private static void closeCustomFile(Object customFile) {
+ if(customFile instanceof Closeable) {
+ ByteUtil.closeQuietly((Closeable)customFile);
+ }
+ }
+
+ /**
+ * Called by {@link #resolveLinkedDatabase} to determine whether the
+ * linkeeFileName should be treated as a custom file (thus utiliziing a temp
+ * db) or a normal access db (loaded via the default behavior). Loads any
+ * state necessary for subsequently loading data from linkeeFileName.
+ * <p>
+ * The returned custom file state object will be maintained with the temp db
+ * and passed to {@link #loadCustomTable} whenever a new table needs to be
+ * loaded. Also, if this object is {@link Closeable}, it will be closed
+ * with the temp db.
+ *
+ * @param linkerDb the primary database in which the link is defined
+ * @param linkeeFileName the name of the linked file
+ *
+ * @return non-{@code null} if linkeeFileName should be treated as a custom
+ * file (using a temp db) or {@code null} if it should be treated as
+ * a normal access db.
+ */
+ protected abstract Object loadCustomFile(
+ Database linkerDb, String linkeeFileName) throws IOException;
+
+ /**
+ * Called by an instance of a temp db when a missing table is first requested.
+ *
+ * @param tempDb the temp db instance which should be populated with the
+ * relevant table info for the given tableName
+ * @param customFile custom file state returned from {@link #loadCustomFile}
+ * @param tableName the name of the table which is requested from the linked
+ * file
+ *
+ * @return {@code true} if the table was available in the linked file,
+ * {@code false} otherwise
+ */
+ protected abstract boolean loadCustomTable(
+ Database tempDb, Object customFile, String tableName)
+ throws IOException;
+
+
+ /**
+ * Subclass of DatabaseImpl which allows us to load tables "on demand" as
+ * well as delete the temporary db on close.
+ */
+ private static class TempDatabaseImpl extends DatabaseImpl
+ {
+ private final CustomLinkResolver _resolver;
+ private final Object _customFile;
+
+ protected TempDatabaseImpl(CustomLinkResolver resolver, Object customFile,
+ File file, FileChannel channel,
+ FileFormat fileFormat)
+ throws IOException
+ {
+ super(file, channel, true, false, fileFormat, null, null, null);
+ _resolver = resolver;
+ _customFile = customFile;
+ }
+
+ @Override
+ protected TableImpl getTable(String name, boolean includeSystemTables)
+ throws IOException
+ {
+ TableImpl table = super.getTable(name, includeSystemTables);
+ if((table == null) &&
+ _resolver.loadCustomTable(this, _customFile, name)) {
+ table = super.getTable(name, includeSystemTables);
+ }
+ return table;
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ super.close();
+ } finally {
+ deleteDbFile(getFile());
+ closeCustomFile(_customFile);
+ }
+ }
+
+ static FileChannel initDbChannel(FileChannel channel, FileFormat format)
+ throws IOException
+ {
+ FileFormatDetails details = getFileFormatDetails(format);
+ transferDbFrom(channel, getResourceAsStream(details.getEmptyFilePath()));
+ return channel;
+ }
+ }
+
+}
diff --git a/src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java b/src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java
index 6a6fd34..025e180 100644
--- a/src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java
@@ -742,7 +742,9 @@ public class DatabaseTest extends TestCase
Arrays.asList("MSysObjects", "MSysQueries", "MSysACES",
"MSysRelationships"));
- if (fileFormat.ordinal() < FileFormat.V2003.ordinal()) {
+ if (fileFormat == FileFormat.GENERIC_JET4) {
+ assertNull("file format: " + fileFormat, db.getSystemTable("MSysAccessObjects"));
+ } else if (fileFormat.ordinal() < FileFormat.V2003.ordinal()) {
assertNotNull("file format: " + fileFormat, db.getSystemTable("MSysAccessObjects"));
sysTables.add("MSysAccessObjects");
} else {
diff --git a/src/test/java/com/healthmarketscience/jackcess/PropertiesTest.java b/src/test/java/com/healthmarketscience/jackcess/PropertiesTest.java
index a5ec859..89ab4c4 100644
--- a/src/test/java/com/healthmarketscience/jackcess/PropertiesTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/PropertiesTest.java
@@ -24,7 +24,6 @@ import java.util.List;
import java.util.UUID;
import static com.healthmarketscience.jackcess.Database.*;
-import static com.healthmarketscience.jackcess.DatabaseTest.*;
import com.healthmarketscience.jackcess.impl.DatabaseImpl;
import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
import com.healthmarketscience.jackcess.impl.PropertyMapImpl;
@@ -349,6 +348,12 @@ public class PropertiesTest extends TestCase
public void testCreateDbProperties() throws Exception
{
for(FileFormat ff : SUPPORTED_FILEFORMATS) {
+
+ if(ff == FileFormat.GENERIC_JET4) {
+ // weirdo format, no properties
+ continue;
+ }
+
File file = TestUtil.createTempFile(false);
Database db = new DatabaseBuilder(file)
.setFileFormat(ff)
diff --git a/src/test/java/com/healthmarketscience/jackcess/TestUtil.java b/src/test/java/com/healthmarketscience/jackcess/TestUtil.java
index c6856bc..3317c7f 100644
--- a/src/test/java/com/healthmarketscience/jackcess/TestUtil.java
+++ b/src/test/java/com/healthmarketscience/jackcess/TestUtil.java
@@ -41,6 +41,7 @@ import com.healthmarketscience.jackcess.impl.ByteUtil;
import com.healthmarketscience.jackcess.impl.DatabaseImpl;
import com.healthmarketscience.jackcess.impl.IndexData;
import com.healthmarketscience.jackcess.impl.IndexImpl;
+import com.healthmarketscience.jackcess.impl.JetFormatTest;
import com.healthmarketscience.jackcess.impl.JetFormatTest.TestDB;
import com.healthmarketscience.jackcess.impl.RowIdImpl;
import com.healthmarketscience.jackcess.impl.RowImpl;
@@ -84,7 +85,8 @@ public class TestUtil
public static Database open(FileFormat fileFormat, File file, boolean inMem)
throws Exception
{
- FileChannel channel = (inMem ? MemFileChannel.newChannel(file, "rw")
+ FileChannel channel = (inMem ? MemFileChannel.newChannel(
+ file, DatabaseImpl.RW_CHANNEL_MODE)
: null);
final Database db = new DatabaseBuilder(file).setReadOnly(true)
.setAutoSync(getTestAutoSync()).setChannel(channel).open();
@@ -122,8 +124,32 @@ public class TestUtil
throws Exception
{
FileChannel channel = (inMem ? MemFileChannel.newChannel() : null);
+
+ if (fileFormat == FileFormat.GENERIC_JET4) {
+ // while we don't support creating GENERIC_JET4 as a jackcess feature,
+ // we do want to be able to test these types of dbs
+ InputStream inStream = null;
+ OutputStream outStream = null;
+ try {
+ inStream = TestUtil.class.getClassLoader()
+ .getResourceAsStream("emptyJet4.mdb");
+ File f = createTempFile(keep);
+ if (channel != null) {
+ JetFormatTest.transferDbFrom(channel, inStream);
+ } else {
+ ByteUtil.copy(inStream, outStream = new FileOutputStream(f));
+ outStream.close();
+ }
+ return new DatabaseBuilder(f)
+ .setAutoSync(getTestAutoSync()).setChannel(channel).open();
+ } finally {
+ ByteUtil.closeQuietly(inStream);
+ ByteUtil.closeQuietly(outStream);
+ }
+ }
+
return new DatabaseBuilder(createTempFile(keep)).setFileFormat(fileFormat)
- .setAutoSync(getTestAutoSync()).setChannel(channel).create();
+ .setAutoSync(getTestAutoSync()).setChannel(channel).create();
}
diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/AutoNumberTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/AutoNumberTest.java
index 37eec77..eae2c25 100644
--- a/src/test/java/com/healthmarketscience/jackcess/impl/AutoNumberTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/impl/AutoNumberTest.java
@@ -242,12 +242,7 @@ public class AutoNumberTest extends TestCase
assertEquals(13, ((TableImpl)table).getLastLongAutoNumber());
- try {
- table.addRow(-10, "uh-uh");
- fail("IOException should have been thrown");
- } catch(IOException e) {
- // success
- }
+ table.addRow(-10, "non-positives are now allowed");
row = table.addRow(Column.AUTO_NUMBER, "row14");
assertEquals(14, ((Integer)row[0]).intValue());
@@ -262,23 +257,18 @@ public class AutoNumberTest extends TestCase
assertEquals(45, ((TableImpl)table).getLastLongAutoNumber());
- row13.put("a", -1);
-
- try {
- table.updateRow(row13);
- fail("IOException should have been thrown");
- } catch(IOException e) {
- // success
- }
+ row13.put("a", -1); // non-positives are now allowed
+ table.updateRow(row13);
assertEquals(45, ((TableImpl)table).getLastLongAutoNumber());
row13.put("a", 55);
- table.setAllowAutoNumberInsert(null);
+ // reset to db-level policy (which in this case is "false")
+ table.setAllowAutoNumberInsert(null);
- row13 = table.updateRow(row13);
- assertEquals(45, row13.get("a"));
+ row13 = table.updateRow(row13); // no change, as confirmed by...
+ assertEquals(-1, row13.get("a"));
assertEquals(45, ((TableImpl)table).getLastLongAutoNumber());
diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java
index 4efbd60..b302985 100644
--- a/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java
@@ -1,6 +1,8 @@
package com.healthmarketscience.jackcess.impl;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.nio.channels.NonWritableChannelException;
import java.util.ArrayList;
@@ -263,4 +265,9 @@ public class JetFormatTest extends TestCase {
}
}
+ public static void transferDbFrom(FileChannel channel, InputStream in)
+ throws IOException
+ {
+ DatabaseImpl.transferDbFrom(channel, in);
+ }
}
diff --git a/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java b/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java
new file mode 100644
index 0000000..31a8853
--- /dev/null
+++ b/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java
@@ -0,0 +1,159 @@
+/*
+Copyright (c) 2017 James Ahlborn
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package com.healthmarketscience.jackcess.util;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import com.healthmarketscience.jackcess.ColumnBuilder;
+import com.healthmarketscience.jackcess.DataType;
+import com.healthmarketscience.jackcess.Database;
+import com.healthmarketscience.jackcess.Database.FileFormat;
+import com.healthmarketscience.jackcess.Table;
+import com.healthmarketscience.jackcess.TableBuilder;
+import junit.framework.TestCase;
+import static com.healthmarketscience.jackcess.TestUtil.*;
+import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
+
+/**
+ *
+ * @author James Ahlborn
+ */
+public class CustomLinkResolverTest extends TestCase
+{
+
+ public CustomLinkResolverTest(String name) {
+ super(name);
+ }
+
+ public void testCustomLinkResolver() throws Exception {
+ for(final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
+ Database db = create(fileFormat);
+
+ db.setLinkResolver(new TestLinkResolver());
+
+ db.createLinkedTable("Table1", "testFile1.txt", "Table1");
+ db.createLinkedTable("Table2", "testFile2.txt", "OtherTable2");
+ db.createLinkedTable("Table3", "missingFile3.txt", "MissingTable3");
+ db.createLinkedTable("Table4", "testFile2.txt", "MissingTable4");
+
+ Table t1 = db.getTable("Table1");
+ assertNotNull(t1);
+ assertNotSame(db, t1.getDatabase());
+
+ assertTable(createExpectedTable(createExpectedRow("id", 0,
+ "data1", "row0"),
+ createExpectedRow("id", 1,
+ "data1", "row1"),
+ createExpectedRow("id", 2,
+ "data1", "row2")),
+ t1);
+
+ Table t2 = db.getTable("Table2");
+ assertNotNull(t2);
+ assertNotSame(db, t2.getDatabase());
+
+ assertTable(createExpectedTable(createExpectedRow("id", 3,
+ "data2", "row3"),
+ createExpectedRow("id", 4,
+ "data2", "row4"),
+ createExpectedRow("id", 5,
+ "data2", "row5")),
+ t2);
+
+ assertNull(db.getTable("Table4"));
+
+ try {
+ db.getTable("Table3");
+ fail("FileNotFoundException should have been thrown");
+ } catch(FileNotFoundException e) {
+ // success
+ }
+
+ db.close();
+ }
+ }
+
+ private static class TestLinkResolver extends CustomLinkResolver
+ {
+ private TestLinkResolver()
+ {
+ super(DEFAULT_FORMAT, true, DEFAULT_TEMP_DIR);
+ }
+
+ @Override
+ protected Object loadCustomFile(
+ Database linkerDb, String linkeeFileName) throws IOException
+ {
+ return (("testFile1.txt".equals(linkeeFileName) ||
+ "testFile2.txt".equals(linkeeFileName)) ?
+ linkeeFileName : null);
+ }
+
+ @Override
+ protected boolean loadCustomTable(
+ Database tempDb, Object customFile, String tableName)
+ throws IOException
+ {
+ if("Table1".equals(tableName)) {
+
+ assertEquals("testFile1.txt", customFile);
+ Table t = new TableBuilder(tableName)
+ .addColumn(new ColumnBuilder("id", DataType.LONG))
+ .addColumn(new ColumnBuilder("data1", DataType.TEXT))
+ .toTable(tempDb);
+
+ for(int i = 0; i < 3; ++i) {
+ t.addRow(i, "row" + i);
+ }
+
+ return true;
+
+ } else if("OtherTable2".equals(tableName)) {
+
+ assertEquals("testFile2.txt", customFile);
+ Table t = new TableBuilder(tableName)
+ .addColumn(new ColumnBuilder("id", DataType.LONG))
+ .addColumn(new ColumnBuilder("data2", DataType.TEXT))
+ .toTable(tempDb);
+
+ for(int i = 3; i < 6; ++i) {
+ t.addRow(i, "row" + i);
+ }
+
+ return true;
+
+ } else if("Table4".equals(tableName)) {
+
+ assertEquals("testFile2.txt", customFile);
+ return false;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected Database createTempDb(Object customFile, FileFormat format,
+ boolean inMemory, File tempDir)
+ throws IOException
+ {
+ inMemory = "testFile1.txt".equals(customFile);
+ return super.createTempDb(customFile, format, inMemory, tempDir);
+ }
+ }
+}
diff --git a/src/test/resources/emptyJet4.mdb b/src/test/resources/emptyJet4.mdb
new file mode 100644
index 0000000..7367d01
--- /dev/null
+++ b/src/test/resources/emptyJet4.mdb
Binary files differ