summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2012-09-23 15:39:02 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2012-09-23 15:39:02 +0000
commitfe78870364c29c07a8f4269b9c38ccd244665ed0 (patch)
tree6b2554d5e008e0f99e2337aaa393641cbc465214 /src
parent3e547e1d59bd61e8d1ad9bab945af5d0eafba061 (diff)
downloadjackcess-fe78870364c29c07a8f4269b9c38ccd244665ed0.tar.gz
jackcess-fe78870364c29c07a8f4269b9c38ccd244665ed0.zip
Added DatabaseBuilder in for more convenient and flexible Database open/create
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@641 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src')
-rw-r--r--src/changes/changes.xml4
-rw-r--r--src/java/com/healthmarketscience/jackcess/Database.java236
-rw-r--r--src/java/com/healthmarketscience/jackcess/DatabaseBuilder.java163
-rw-r--r--src/java/com/healthmarketscience/jackcess/PageChannel.java11
4 files changed, 343 insertions, 71 deletions
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index b27fa1e..afd3161 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -9,6 +9,10 @@
Add some more functionality to Joiner to facilitate integrity
enforcement (hasRows and deleteRows).
</action>
+ <action dev="jahlborn" type="add">
+ Added DatabaseBuilder in for more convenient and flexible Database
+ open/create.
+ </action>
</release>
<release version="1.2.8" date="2012-07-10">
<action dev="jahlborn" type="update" issue="3523179">
diff --git a/src/java/com/healthmarketscience/jackcess/Database.java b/src/java/com/healthmarketscience/jackcess/Database.java
index 4ef116a..1c6f347 100644
--- a/src/java/com/healthmarketscience/jackcess/Database.java
+++ b/src/java/com/healthmarketscience/jackcess/Database.java
@@ -486,6 +486,7 @@ public class Database
* @param mdbFile File containing the database
*
* @see #open(File,boolean)
+ * @see DatabaseBuilder for more flexible Database opening
* @usage _general_method_
*/
public static Database open(File mdbFile) throws IOException {
@@ -494,17 +495,18 @@ public class Database
/**
* Open an existing Database. If the existing file is not writeable or the
- * readOnly flag is <code>true</code>, the file will be opened read-only.
+ * readOnly flag is {@code true}, the file will be opened read-only.
* Auto-syncing is enabled for the returned Database.
* <p>
* Equivalent to:
* {@code open(mdbFile, readOnly, DEFAULT_AUTO_SYNC);}
*
* @param mdbFile File containing the database
- * @param readOnly iff <code>true</code>, force opening file in read-only
+ * @param readOnly iff {@code true}, force opening file in read-only
* mode
*
* @see #open(File,boolean,boolean)
+ * @see DatabaseBuilder for more flexible Database opening
* @usage _general_method_
*/
public static Database open(File mdbFile, boolean readOnly)
@@ -515,9 +517,9 @@ public class Database
/**
* Open an existing Database. If the existing file is not writeable or the
- * readOnly flag is <code>true</code>, the file will be opened read-only.
+ * readOnly flag is {@code true}, the file will be opened read-only.
* @param mdbFile File containing the database
- * @param readOnly iff <code>true</code>, force opening file in read-only
+ * @param readOnly iff {@code true}, force opening file in read-only
* mode
* @param autoSync whether or not to enable auto-syncing on write. if
* {@code true}, writes will be immediately flushed to disk.
@@ -526,7 +528,9 @@ public class Database
* updates. if {@code false}, flushing to disk happens at
* the jvm's leisure, which can be much faster, but may
* leave the database in an inconsistent state if failures
- * are encountered during writing.
+ * are encountered during writing. Writes may be flushed at
+ * any time using {@link #flush}.
+ * @see DatabaseBuilder for more flexible Database opening
* @usage _general_method_
*/
public static Database open(File mdbFile, boolean readOnly, boolean autoSync)
@@ -537,9 +541,9 @@ public class Database
/**
* Open an existing Database. If the existing file is not writeable or the
- * readOnly flag is <code>true</code>, the file will be opened read-only.
+ * readOnly flag is {@code true}, the file will be opened read-only.
* @param mdbFile File containing the database
- * @param readOnly iff <code>true</code>, force opening file in read-only
+ * @param readOnly iff {@code true}, force opening file in read-only
* mode
* @param autoSync whether or not to enable auto-syncing on write. if
* {@code true}, writes will be immediately flushed to disk.
@@ -548,9 +552,11 @@ public class Database
* updates. if {@code false}, flushing to disk happens at
* the jvm's leisure, which can be much faster, but may
* leave the database in an inconsistent state if failures
- * are encountered during writing.
+ * are encountered during writing. Writes may be flushed at
+ * any time using {@link #flush}.
* @param charset Charset to use, if {@code null}, uses default
* @param timeZone TimeZone to use, if {@code null}, uses default
+ * @see DatabaseBuilder for more flexible Database opening
* @usage _intermediate_method_
*/
public static Database open(File mdbFile, boolean readOnly, boolean autoSync,
@@ -562,9 +568,9 @@ public class Database
/**
* Open an existing Database. If the existing file is not writeable or the
- * readOnly flag is <code>true</code>, the file will be opened read-only.
+ * readOnly flag is {@code true}, the file will be opened read-only.
* @param mdbFile File containing the database
- * @param readOnly iff <code>true</code>, force opening file in read-only
+ * @param readOnly iff {@code true}, force opening file in read-only
* mode
* @param autoSync whether or not to enable auto-syncing on write. if
* {@code true}, writes will be immediately flushed to disk.
@@ -573,11 +579,13 @@ public class Database
* updates. if {@code false}, flushing to disk happens at
* the jvm's leisure, which can be much faster, but may
* leave the database in an inconsistent state if failures
- * are encountered during writing.
+ * are encountered during writing. Writes may be flushed at
+ * any time using {@link #flush}.
* @param charset Charset to use, if {@code null}, uses default
* @param timeZone TimeZone to use, if {@code null}, uses default
* @param provider CodecProvider for handling page encoding/decoding, may be
* {@code null} if no special encoding is necessary
+ * @see DatabaseBuilder for more flexible Database opening
* @usage _intermediate_method_
*/
public static Database open(File mdbFile, boolean readOnly, boolean autoSync,
@@ -585,34 +593,82 @@ public class Database
CodecProvider provider)
throws IOException
{
- if(!mdbFile.exists() || !mdbFile.canRead()) {
- throw new FileNotFoundException("given file does not exist: " + mdbFile);
+ return open(mdbFile, readOnly, null, autoSync, charset, timeZone,
+ provider);
+ }
+
+ /**
+ * Open an existing Database. If the existing file is not writeable or the
+ * readOnly flag is {@code true}, the file will be opened read-only.
+ * @param mdbFile File containing the database
+ * @param readOnly iff {@code true}, force opening file in read-only
+ * mode
+ * @param channel pre-opened FileChannel. if provided explicitly, it will
+ * not be closed by this Database instance
+ * @param autoSync whether or not to enable auto-syncing on write. if
+ * {@code true}, writes will be immediately flushed to disk.
+ * This leaves the database in a (fairly) consistent state
+ * on each write, but can be very inefficient for many
+ * updates. if {@code false}, flushing to disk happens at
+ * the jvm's leisure, which can be much faster, but may
+ * leave the database in an inconsistent state if failures
+ * are encountered during writing. Writes may be flushed at
+ * any time using {@link #flush}.
+ * @param charset Charset to use, if {@code null}, uses default
+ * @param timeZone TimeZone to use, if {@code null}, uses default
+ * @param provider CodecProvider for handling page encoding/decoding, may be
+ * {@code null} if no special encoding is necessary
+ * @usage _advanced_method_
+ */
+ static Database open(File mdbFile, boolean readOnly, FileChannel channel,
+ boolean autoSync, Charset charset, TimeZone timeZone,
+ CodecProvider provider)
+ throws IOException
+ {
+ boolean closeChannel = false;
+ if(channel == null) {
+ if(!mdbFile.exists() || !mdbFile.canRead()) {
+ throw new FileNotFoundException("given file does not exist: " +
+ mdbFile);
+ }
+
+ // force read-only for non-writable files
+ readOnly |= !mdbFile.canWrite();
+
+ // open file channel
+ channel = openChannel(mdbFile, readOnly);
+ closeChannel = true;
}
- // force read-only for non-writable files
- readOnly |= !mdbFile.canWrite();
+ boolean success = false;
+ try {
+
+ if(!readOnly) {
- // open file channel
- FileChannel channel = openChannel(mdbFile, readOnly);
+ // verify that format supports writing
+ JetFormat jetFormat = JetFormat.getFormat(channel);
- if(!readOnly) {
+ if(jetFormat.READ_ONLY) {
+ throw new IOException("jet format '" + jetFormat +
+ "' does not support writing");
+ }
+ }
- // verify that format supports writing
- JetFormat jetFormat = JetFormat.getFormat(channel);
+ Database db = new Database(mdbFile, channel, closeChannel, autoSync,
+ null, charset, timeZone, provider);
+ success = true;
+ return db;
- if(jetFormat.READ_ONLY) {
- // shutdown the channel (quietly)
+ } finally {
+ if(!success && closeChannel) {
+ // something blew up, shutdown the channel (quietly)
try {
channel.close();
} catch(Exception ignored) {
// we don't care
}
- throw new IOException("jet format '" + jetFormat + "' does not support writing");
}
}
-
- return new Database(mdbFile, channel, autoSync, null, charset, timeZone,
- provider);
}
/**
@@ -625,6 +681,7 @@ public class Database
* already exists, it will be overwritten.</b>
*
* @see #create(File,boolean)
+ * @see DatabaseBuilder for more flexible Database creation
* @usage _general_method_
*/
public static Database create(File mdbFile) throws IOException {
@@ -642,6 +699,7 @@ public class Database
* already exists, it will be overwritten.</b>
*
* @see #create(File,boolean)
+ * @see DatabaseBuilder for more flexible Database creation
* @usage _general_method_
*/
public static Database create(FileFormat fileFormat, File mdbFile)
@@ -665,7 +723,9 @@ public class Database
* updates. if {@code false}, flushing to disk happens at
* the jvm's leisure, which can be much faster, but may
* leave the database in an inconsistent state if failures
- * are encountered during writing.
+ * are encountered during writing. Writes may be flushed at
+ * any time using {@link #flush}.
+ * @see DatabaseBuilder for more flexible Database creation
* @usage _general_method_
*/
public static Database create(File mdbFile, boolean autoSync)
@@ -686,7 +746,9 @@ public class Database
* updates. if {@code false}, flushing to disk happens at
* the jvm's leisure, which can be much faster, but may
* leave the database in an inconsistent state if failures
- * are encountered during writing.
+ * are encountered during writing. Writes may be flushed at
+ * any time using {@link #flush}.
+ * @see DatabaseBuilder for more flexible Database creation
* @usage _general_method_
*/
public static Database create(FileFormat fileFormat, File mdbFile,
@@ -708,9 +770,11 @@ public class Database
* updates. if {@code false}, flushing to disk happens at
* the jvm's leisure, which can be much faster, but may
* leave the database in an inconsistent state if failures
- * are encountered during writing.
+ * are encountered during writing. Writes may be flushed at
+ * any time using {@link #flush}.
* @param charset Charset to use, if {@code null}, uses default
* @param timeZone TimeZone to use, if {@code null}, uses default
+ * @see DatabaseBuilder for more flexible Database creation
* @usage _intermediate_method_
*/
public static Database create(FileFormat fileFormat, File mdbFile,
@@ -718,16 +782,63 @@ public class Database
TimeZone timeZone)
throws IOException
{
+ return create(fileFormat, mdbFile, null, autoSync, charset, timeZone);
+ }
+
+ /**
+ * Create a new Database for the given fileFormat
+ * @param fileFormat version of new database.
+ * @param mdbFile Location to write the new database to. <b>If this file
+ * already exists, it will be overwritten.</b>
+ * @param channel pre-opened FileChannel. if provided explicitly, it will
+ * not be closed by this Database instance
+ * @param autoSync whether or not to enable auto-syncing on write. if
+ * {@code true}, writes will be immediately flushed to disk.
+ * This leaves the database in a (fairly) consistent state
+ * on each write, but can be very inefficient for many
+ * updates. if {@code false}, flushing to disk happens at
+ * the jvm's leisure, which can be much faster, but may
+ * leave the database in an inconsistent state if failures
+ * are encountered during writing. Writes may be flushed at
+ * any time using {@link #flush}.
+ * @param charset Charset to use, if {@code null}, uses default
+ * @param timeZone TimeZone to use, if {@code null}, uses default
+ * @usage _advanced_method_
+ */
+ static Database create(FileFormat fileFormat, File mdbFile,
+ FileChannel channel, boolean autoSync,
+ Charset charset, TimeZone timeZone)
+ throws IOException
+ {
if (fileFormat.getJetFormat().READ_ONLY) {
throw new IOException("jet format '" + fileFormat.getJetFormat() + "' does not support writing");
}
- FileChannel channel = openChannel(mdbFile, false);
- channel.truncate(0);
- transferFrom(channel, getResourceAsStream(fileFormat._emptyFile));
- channel.force(true);
- return new Database(mdbFile, channel, autoSync, fileFormat, charset,
- timeZone, null);
+ boolean closeChannel = false;
+ if(channel == null) {
+ channel = openChannel(mdbFile, false);
+ closeChannel = true;
+ }
+
+ boolean success = false;
+ try {
+ channel.truncate(0);
+ transferFrom(channel, getResourceAsStream(fileFormat._emptyFile));
+ channel.force(true);
+ Database db = new Database(mdbFile, channel, closeChannel, autoSync,
+ fileFormat, charset, timeZone, null);
+ success = true;
+ return db;
+ } finally {
+ if(!success && closeChannel) {
+ // something blew up, shutdown the channel (quietly)
+ try {
+ channel.close();
+ } catch(Exception ignored) {
+ // we don't care
+ }
+ }
+ }
}
/**
@@ -763,46 +874,33 @@ public class Database
* updates. if {@code false}, flushing to disk happens at
* the jvm's leisure, which can be much faster, but may
* leave the database in an inconsistent state if failures
- * are encountered during writing.
+ * are encountered during writing. Writes may be flushed at
+ * any time using {@link #flush}.
* @param fileFormat version of new database (if known)
* @param charset Charset to use, if {@code null}, uses default
* @param timeZone TimeZone to use, if {@code null}, uses default
*/
- protected Database(File file, FileChannel channel, boolean autoSync,
- FileFormat fileFormat, Charset charset, TimeZone timeZone,
- CodecProvider provider)
+ protected Database(File file, FileChannel channel, boolean closeChannel,
+ boolean autoSync, FileFormat fileFormat, Charset charset,
+ TimeZone timeZone, CodecProvider provider)
throws IOException
{
- boolean success = false;
- try {
- _file = file;
- _format = JetFormat.getFormat(channel);
- _charset = ((charset == null) ? getDefaultCharset(_format) : charset);
- _columnOrder = getDefaultColumnOrder();
- _fileFormat = fileFormat;
- _pageChannel = new PageChannel(channel, _format, autoSync);
- _timeZone = ((timeZone == null) ? getDefaultTimeZone() : timeZone);
- if(provider == null) {
- provider = DefaultCodecProvider.INSTANCE;
- }
- // note, it's slighly sketchy to pass ourselves along partially
- // constructed, but only our _format and _pageChannel refs should be
- // needed
- _pageChannel.initialize(this, provider);
- _buffer = _pageChannel.createPageBuffer();
- readSystemCatalog();
- success = true;
-
- } finally {
- if(!success && (channel != null)) {
- // something blew up, shutdown the channel (quietly)
- try {
- channel.close();
- } catch(Exception ignored) {
- // we don't care
- }
- }
- }
+ _file = file;
+ _format = JetFormat.getFormat(channel);
+ _charset = ((charset == null) ? getDefaultCharset(_format) : charset);
+ _columnOrder = getDefaultColumnOrder();
+ _fileFormat = fileFormat;
+ _pageChannel = new PageChannel(channel, closeChannel, _format, autoSync);
+ _timeZone = ((timeZone == null) ? getDefaultTimeZone() : timeZone);
+ if(provider == null) {
+ provider = DefaultCodecProvider.INSTANCE;
+ }
+ // note, it's slighly sketchy to pass ourselves along partially
+ // constructed, but only our _format and _pageChannel refs should be
+ // needed
+ _pageChannel.initialize(this, provider);
+ _buffer = _pageChannel.createPageBuffer();
+ readSystemCatalog();
}
/**
diff --git a/src/java/com/healthmarketscience/jackcess/DatabaseBuilder.java b/src/java/com/healthmarketscience/jackcess/DatabaseBuilder.java
new file mode 100644
index 0000000..fa0a394
--- /dev/null
+++ b/src/java/com/healthmarketscience/jackcess/DatabaseBuilder.java
@@ -0,0 +1,163 @@
+/*
+Copyright (c) 2012 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+import java.util.TimeZone;
+
+/**
+ * Builder style class for opening/creating a Database.
+ *
+ * @author James Ahlborn
+ */
+public class DatabaseBuilder
+{
+ /** the file name of the mdb to open/create */
+ private File _mdbFile;
+ /** whether or not to open existing mdb read-only */
+ private boolean _readOnly;
+ /** whether or not to auto-sync writes to the filesystem */
+ private boolean _autoSync = Database.DEFAULT_AUTO_SYNC;
+ /** optional charset for mdbs with unspecified charsets */
+ private Charset _charset;
+ /** optional timezone override for interpreting dates */
+ private TimeZone _timeZone;
+ /** optional CodecProvider for handling encoded mdbs */
+ private CodecProvider _codecProvider;
+ /** FileFormat to use when creating a new mdb */
+ private Database.FileFormat _fileFormat;
+ /** optional pre-opened FileChannel, will _not_ be closed by Database
+ close */
+ private FileChannel _channel;
+
+ public DatabaseBuilder() {
+ this(null);
+ }
+
+ public DatabaseBuilder(File mdbFile) {
+ _mdbFile = mdbFile;
+ }
+
+ /**
+ * File containing an existing database for {@link #open} or target file for
+ * new database for {@link #create} (in which case, <b>tf this file already
+ * exists, it will be overwritten.</b>)
+ * @usage _general_method_
+ */
+ public DatabaseBuilder setFile(File mdbFile) {
+ _mdbFile = mdbFile;
+ return this;
+ }
+
+ /**
+ * Sets flag which, iff {@code true}, will force opening file in
+ * read-only mode ({@link #open} only).
+ * @usage _general_method_
+ */
+ public DatabaseBuilder setReadOnly(boolean readOnly) {
+ _readOnly = readOnly;
+ return this;
+ }
+
+ /**
+ * Sets whether or not to enable auto-syncing on write. if {@code true},
+ * writes will be immediately flushed to disk. This leaves the database in
+ * a (fairly) consistent state on each write, but can be very inefficient
+ * for many updates. if {@code false}, flushing to disk happens at the
+ * jvm's leisure, which can be much faster, but may leave the database in an
+ * inconsistent state if failures are encountered during writing. Writes
+ * may be flushed at any time using {@link Database#flush}.
+ * @usage _intermediate_method_
+ */
+ public DatabaseBuilder setAutoSync(boolean autoSync) {
+ _autoSync = autoSync;
+ return this;
+ }
+
+ /**
+ * Sets the Charset to use, if {@code null}, uses default.
+ * @usage _intermediate_method_
+ */
+ public DatabaseBuilder setCharset(Charset charset) {
+ _charset = charset;
+ return this;
+ }
+
+ /**
+ * Sets the TimeZone to use for interpreting dates, if {@code null}, uses
+ * default
+ * @usage _intermediate_method_
+ */
+ public DatabaseBuilder setTimeZone(TimeZone timeZone) {
+ _timeZone = timeZone;
+ return this;
+ }
+
+ /**
+ * Sets the CodecProvider for handling page encoding/decoding, may be
+ * {@code null} if no special encoding is necessary
+ * @usage _intermediate_method_
+ */
+ public DatabaseBuilder setCodecProvider(CodecProvider codecProvider) {
+ _codecProvider = codecProvider;
+ return this;
+ }
+
+ /**
+ * Sets the version of new database ({@link #create} only).
+ * @usage _general_method_
+ */
+ public DatabaseBuilder setFileFormat(Database.FileFormat fileFormat) {
+ _fileFormat = fileFormat;
+ return this;
+ }
+
+ /**
+ * Sets a pre-opened FileChannel. if provided explicitly, <i>it will not be
+ * closed by the Database instance</i>. This allows ultimate control of
+ * where the mdb file exists (which may not be on disk, e.g.
+ * {@link MemFileChannel}). If provided, the File parameter will be
+ * available from {@link Database#getFile}, but otherwise ignored.
+ * @usage _advanced_method_
+ */
+ public DatabaseBuilder setChannel(FileChannel channel) {
+ _channel = channel;
+ return this;
+ }
+
+ /**
+ * Opens an existingnew Database using the configured information.
+ */
+ public Database open() throws IOException {
+ return Database.open(_mdbFile, _readOnly, _channel, _autoSync, _charset,
+ _timeZone, _codecProvider);
+ }
+
+ /**
+ * Creates a new Database using the configured information.
+ */
+ public Database create() throws IOException {
+ return Database.create(_fileFormat, _mdbFile, _channel, _autoSync, _charset,
+ _timeZone);
+ }
+}
diff --git a/src/java/com/healthmarketscience/jackcess/PageChannel.java b/src/java/com/healthmarketscience/jackcess/PageChannel.java
index ae30ba7..cd2a03c 100644
--- a/src/java/com/healthmarketscience/jackcess/PageChannel.java
+++ b/src/java/com/healthmarketscience/jackcess/PageChannel.java
@@ -62,6 +62,8 @@ public class PageChannel implements Channel, Flushable {
/** Channel containing the database */
private final FileChannel _channel;
+ /** whether or not the _channel should be closed by this class */
+ private final boolean _closeChannel;
/** Format of the database in the channel */
private final JetFormat _format;
/** whether or not to force all writes to disk immediately */
@@ -84,10 +86,12 @@ public class PageChannel implements Channel, Flushable {
* @param channel Channel containing the database
* @param format Format of the database in the channel
*/
- public PageChannel(FileChannel channel, JetFormat format, boolean autoSync)
+ public PageChannel(FileChannel channel, boolean closeChannel,
+ JetFormat format, boolean autoSync)
throws IOException
{
_channel = channel;
+ _closeChannel = closeChannel;
_format = format;
_autoSync = autoSync;
}
@@ -115,6 +119,7 @@ public class PageChannel implements Channel, Flushable {
throw new IllegalArgumentException();
}
_channel = null;
+ _closeChannel = false;
_format = JetFormat.VERSION_4;
_autoSync = false;
}
@@ -338,7 +343,9 @@ public class PageChannel implements Channel, Flushable {
public void close() throws IOException {
flush();
- _channel.close();
+ if(_closeChannel) {
+ _channel.close();
+ }
}
public boolean isOpen() {