git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/jdk8@1233 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-3.0.0
@@ -21,6 +21,7 @@ import java.io.File; | |||
import java.io.Flushable; | |||
import java.io.IOException; | |||
import java.nio.charset.Charset; | |||
import java.nio.file.Path; | |||
import java.util.ConcurrentModificationException; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
@@ -29,8 +30,8 @@ import java.util.Set; | |||
import java.util.TimeZone; | |||
import com.healthmarketscience.jackcess.expr.EvalConfig; | |||
import com.healthmarketscience.jackcess.query.Query; | |||
import com.healthmarketscience.jackcess.impl.DatabaseImpl; | |||
import com.healthmarketscience.jackcess.query.Query; | |||
import com.healthmarketscience.jackcess.util.ColumnValidatorFactory; | |||
import com.healthmarketscience.jackcess.util.ErrorHandler; | |||
import com.healthmarketscience.jackcess.util.LinkResolver; | |||
@@ -178,6 +179,11 @@ public interface Database extends Iterable<Table>, Closeable, Flushable | |||
*/ | |||
public File getFile(); | |||
/** | |||
* Returns the File underlying this Database | |||
*/ | |||
public Path getPath(); | |||
/** | |||
* @return The names of all of the user tables | |||
* @usage _general_method_ |
@@ -20,6 +20,7 @@ import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.channels.FileChannel; | |||
import java.nio.charset.Charset; | |||
import java.nio.file.Path; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Calendar; | |||
import java.util.Date; | |||
@@ -51,10 +52,10 @@ import com.healthmarketscience.jackcess.util.MemFileChannel; | |||
* @author James Ahlborn | |||
* @usage _general_class_ | |||
*/ | |||
public class DatabaseBuilder | |||
public class DatabaseBuilder | |||
{ | |||
/** the file name of the mdb to open/create */ | |||
private File _mdbFile; | |||
private Path _mdbFile; | |||
/** whether or not to open existing mdb read-only */ | |||
private boolean _readOnly; | |||
/** whether or not to auto-sync writes to the filesystem */ | |||
@@ -77,12 +78,16 @@ public class DatabaseBuilder | |||
/** database user-defined (if any) */ | |||
private Map<String,PropertyMap.Property> _userProps; | |||
public DatabaseBuilder() { | |||
this(null); | |||
this((Path)null); | |||
} | |||
public DatabaseBuilder(File mdbFile) { | |||
this(toPath(mdbFile)); | |||
} | |||
public DatabaseBuilder(Path mdbFile) { | |||
_mdbFile = mdbFile; | |||
} | |||
@@ -93,6 +98,16 @@ public class DatabaseBuilder | |||
* @usage _general_method_ | |||
*/ | |||
public DatabaseBuilder setFile(File mdbFile) { | |||
return setPath(toPath(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 setPath(Path mdbFile) { | |||
_mdbFile = mdbFile; | |||
return this; | |||
} | |||
@@ -183,7 +198,7 @@ public class DatabaseBuilder | |||
public DatabaseBuilder putDatabaseProperty(String name, Object value) { | |||
return putDatabaseProperty(name, null, value); | |||
} | |||
/** | |||
* Sets the database property with the given name and type to the given | |||
* value. | |||
@@ -193,7 +208,7 @@ public class DatabaseBuilder | |||
_dbProps = putProperty(_dbProps, name, type, value); | |||
return this; | |||
} | |||
/** | |||
* Sets the summary database property with the given name to the given | |||
* value. Attempts to determine the type of the property (see | |||
@@ -203,7 +218,7 @@ public class DatabaseBuilder | |||
public DatabaseBuilder putSummaryProperty(String name, Object value) { | |||
return putSummaryProperty(name, null, value); | |||
} | |||
/** | |||
* Sets the summary database property with the given name and type to | |||
* the given value. | |||
@@ -223,7 +238,7 @@ public class DatabaseBuilder | |||
public DatabaseBuilder putUserDefinedProperty(String name, Object value) { | |||
return putUserDefinedProperty(name, null, value); | |||
} | |||
/** | |||
* Sets the user-defined database property with the given name and type to | |||
* the given value. | |||
@@ -257,7 +272,7 @@ public class DatabaseBuilder | |||
* Creates a new Database using the configured information. | |||
*/ | |||
public Database create() throws IOException { | |||
Database db = DatabaseImpl.create(_fileFormat, _mdbFile, _channel, _autoSync, | |||
Database db = DatabaseImpl.create(_fileFormat, _mdbFile, _channel, _autoSync, | |||
_charset, _timeZone); | |||
if(_dbProps != null) { | |||
PropertyMap props = db.getDatabaseProperties(); | |||
@@ -281,19 +296,19 @@ public class DatabaseBuilder | |||
* Open an existing Database. If the existing file is not writeable, the | |||
* file will be opened read-only. Auto-syncing is enabled for the returned | |||
* Database. | |||
* | |||
* | |||
* @param mdbFile File containing the database | |||
* | |||
* | |||
* @see DatabaseBuilder for more flexible Database opening | |||
* @usage _general_method_ | |||
*/ | |||
public static Database open(File mdbFile) throws IOException { | |||
return new DatabaseBuilder(mdbFile).open(); | |||
} | |||
/** | |||
* 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> | |||
@@ -301,8 +316,8 @@ public class DatabaseBuilder | |||
* @see DatabaseBuilder for more flexible Database creation | |||
* @usage _general_method_ | |||
*/ | |||
public static Database create(Database.FileFormat fileFormat, File mdbFile) | |||
throws IOException | |||
public static Database create(Database.FileFormat fileFormat, File mdbFile) | |||
throws IOException | |||
{ | |||
return new DatabaseBuilder(mdbFile).setFileFormat(fileFormat).create(); | |||
} | |||
@@ -330,4 +345,8 @@ public class DatabaseBuilder | |||
} | |||
return cal; | |||
} | |||
private static Path toPath(File file) { | |||
return ((file != null) ? file.toPath() : null); | |||
} | |||
} |
@@ -20,7 +20,6 @@ import java.io.File; | |||
import java.io.FileNotFoundException; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.RandomAccessFile; | |||
import java.lang.ref.ReferenceQueue; | |||
import java.lang.ref.WeakReference; | |||
import java.nio.ByteBuffer; | |||
@@ -28,6 +27,10 @@ import java.nio.channels.Channels; | |||
import java.nio.channels.FileChannel; | |||
import java.nio.channels.ReadableByteChannel; | |||
import java.nio.charset.Charset; | |||
import java.nio.file.Files; | |||
import java.nio.file.OpenOption; | |||
import java.nio.file.Path; | |||
import java.nio.file.StandardOpenOption; | |||
import java.text.SimpleDateFormat; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
@@ -198,9 +201,11 @@ public class DatabaseImpl implements Database | |||
SYSTEM_OBJECT_FLAG | ALT_SYSTEM_OBJECT_FLAG; | |||
/** read-only channel access mode */ | |||
public static final String RO_CHANNEL_MODE = "r"; | |||
public static final OpenOption[] RO_CHANNEL_OPTS = | |||
{StandardOpenOption.READ}; | |||
/** read/write channel access mode */ | |||
public static final String RW_CHANNEL_MODE = "rw"; | |||
public static final OpenOption[] RW_CHANNEL_OPTS = | |||
{StandardOpenOption.READ, StandardOpenOption.WRITE}; | |||
/** Name of the system object that is the parent of all tables */ | |||
private static final String SYSTEM_OBJECT_NAME_TABLES = "Tables"; | |||
@@ -252,7 +257,7 @@ public class DatabaseImpl implements Database | |||
Pattern.compile("[\\p{Cntrl}.!`\\]\\[]"); | |||
/** the File of the database */ | |||
private final File _file; | |||
private final Path _file; | |||
/** the simple name of the database */ | |||
private final String _name; | |||
/** Buffer to hold database pages */ | |||
@@ -362,20 +367,20 @@ public class DatabaseImpl implements Database | |||
* @usage _advanced_method_ | |||
*/ | |||
public static DatabaseImpl open( | |||
File mdbFile, boolean readOnly, FileChannel channel, | |||
Path 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()) { | |||
if(!Files.isReadable(mdbFile)) { | |||
throw new FileNotFoundException("given file does not exist: " + | |||
mdbFile); | |||
} | |||
// force read-only for non-writable files | |||
readOnly |= !mdbFile.canWrite(); | |||
readOnly |= !Files.isWritable(mdbFile); | |||
// open file channel | |||
channel = openChannel(mdbFile, readOnly); | |||
@@ -431,7 +436,7 @@ public class DatabaseImpl implements Database | |||
* @param timeZone TimeZone to use, if {@code null}, uses default | |||
* @usage _advanced_method_ | |||
*/ | |||
public static DatabaseImpl create(FileFormat fileFormat, File mdbFile, | |||
public static DatabaseImpl create(FileFormat fileFormat, Path mdbFile, | |||
FileChannel channel, boolean autoSync, | |||
Charset charset, TimeZone timeZone) | |||
throws IOException | |||
@@ -482,11 +487,11 @@ public class DatabaseImpl implements Database | |||
* that name cannot be created, or if some other error occurs | |||
* while opening or creating the file | |||
*/ | |||
static FileChannel openChannel(final File mdbFile, final boolean readOnly) | |||
throws FileNotFoundException | |||
static FileChannel openChannel(Path mdbFile, boolean readOnly) | |||
throws IOException | |||
{ | |||
final String mode = (readOnly ? RO_CHANNEL_MODE : RW_CHANNEL_MODE); | |||
return new RandomAccessFile(mdbFile, mode).getChannel(); | |||
OpenOption[] opts = (readOnly ? RO_CHANNEL_OPTS : RW_CHANNEL_OPTS); | |||
return FileChannel.open(mdbFile, opts); | |||
} | |||
/** | |||
@@ -508,7 +513,7 @@ public class DatabaseImpl implements Database | |||
* @param charset Charset to use, if {@code null}, uses default | |||
* @param timeZone TimeZone to use, if {@code null}, uses default | |||
*/ | |||
protected DatabaseImpl(File file, FileChannel channel, boolean closeChannel, | |||
protected DatabaseImpl(Path file, FileChannel channel, boolean closeChannel, | |||
boolean autoSync, FileFormat fileFormat, Charset charset, | |||
TimeZone timeZone, CodecProvider provider) | |||
throws IOException | |||
@@ -536,6 +541,10 @@ public class DatabaseImpl implements Database | |||
} | |||
public File getFile() { | |||
return ((_file != null) ? _file.toFile() : null); | |||
} | |||
public Path getPath() { | |||
return _file; | |||
} | |||
@@ -2096,11 +2105,11 @@ public class DatabaseImpl implements Database | |||
FILE_FORMAT_DETAILS.put(fileFormat, new FileFormatDetails(emptyFile, format)); | |||
} | |||
private static String getName(File file) { | |||
private static String getName(Path file) { | |||
if(file == null) { | |||
return "<UNKNOWN.DB>"; | |||
} | |||
return file.getName(); | |||
return file.getFileName().toString(); | |||
} | |||
private String withErrorContext(String msg) { |
@@ -17,10 +17,11 @@ 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.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.nio.file.Paths; | |||
import java.util.Random; | |||
import com.healthmarketscience.jackcess.Database; | |||
@@ -64,11 +65,11 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
/** 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; | |||
public static final Path DEFAULT_TEMP_DIR = null; | |||
private final FileFormat _defaultFormat; | |||
private final boolean _defaultInMemory; | |||
private final File _defaultTempDir; | |||
private final Path _defaultTempDir; | |||
/** | |||
* Creates a CustomLinkResolver using the default behavior for creating temp | |||
@@ -93,7 +94,7 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
* directory) | |||
*/ | |||
protected CustomLinkResolver(FileFormat defaultFormat, boolean defaultInMemory, | |||
File defaultTempDir) | |||
Path defaultTempDir) | |||
{ | |||
_defaultFormat = defaultFormat; | |||
_defaultInMemory = defaultInMemory; | |||
@@ -108,7 +109,7 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
return _defaultInMemory; | |||
} | |||
protected File getDefaultTempDirectory() { | |||
protected Path getDefaultTempDirectory() { | |||
return _defaultTempDir; | |||
} | |||
@@ -117,27 +118,27 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
* <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 | |||
throws IOException | |||
{ | |||
Object customFile = loadCustomFile(linkerDb, linkeeFileName); | |||
if(customFile != null) { | |||
return createTempDb(customFile, getDefaultFormat(), isDefaultInMemory(), | |||
return createTempDb(customFile, getDefaultFormat(), isDefaultInMemory(), | |||
getDefaultTempDirectory()); | |||
} | |||
return LinkResolver.DEFAULT.resolveLinkedDatabase(linkerDb, linkeeFileName); | |||
@@ -157,28 +158,30 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
* | |||
* @return the temp db for holding the linked table info | |||
*/ | |||
protected Database createTempDb(Object customFile, FileFormat format, | |||
boolean inMemory, File tempDir) | |||
protected Database createTempDb(Object customFile, FileFormat format, | |||
boolean inMemory, Path tempDir) | |||
throws IOException | |||
{ | |||
File dbFile = null; | |||
Path dbFile = null; | |||
FileChannel channel = null; | |||
boolean success = false; | |||
try { | |||
if(inMemory) { | |||
dbFile = new File(MEM_DB_PREFIX + DB_ID.nextLong() + | |||
format.getFileExtension()); | |||
dbFile = Paths.get(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(); | |||
dbFile = ((tempDir != null) ? | |||
Files.createTempFile(tempDir, FILE_DB_PREFIX, | |||
format.getFileExtension()) : | |||
Files.createTempFile(FILE_DB_PREFIX, | |||
format.getFileExtension())); | |||
channel = FileChannel.open(dbFile, DatabaseImpl.RW_CHANNEL_OPTS); | |||
} | |||
TempDatabaseImpl.initDbChannel(channel, format); | |||
TempDatabaseImpl db = new TempDatabaseImpl(this, customFile, dbFile, | |||
TempDatabaseImpl db = new TempDatabaseImpl(this, customFile, dbFile, | |||
channel, format); | |||
success = true; | |||
return db; | |||
@@ -192,9 +195,12 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
} | |||
} | |||
private static void deleteDbFile(File dbFile) { | |||
if((dbFile != null) && (dbFile.getName().startsWith(FILE_DB_PREFIX))) { | |||
dbFile.delete(); | |||
private static void deleteDbFile(Path dbFile) { | |||
if((dbFile != null) && | |||
dbFile.getFileName().toString().startsWith(FILE_DB_PREFIX)) { | |||
try { | |||
Files.deleteIfExists(dbFile); | |||
} catch(IOException ignores) {} | |||
} | |||
} | |||
@@ -203,7 +209,7 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
ByteUtil.closeQuietly((Closeable)customFile); | |||
} | |||
} | |||
/** | |||
* Called by {@link #resolveLinkedDatabase} to determine whether the | |||
* linkeeFileName should be treated as a custom file (thus utiliziing a temp | |||
@@ -252,7 +258,7 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
private final Object _customFile; | |||
protected TempDatabaseImpl(CustomLinkResolver resolver, Object customFile, | |||
File file, FileChannel channel, | |||
Path file, FileChannel channel, | |||
FileFormat fileFormat) | |||
throws IOException | |||
{ | |||
@@ -262,11 +268,11 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
} | |||
@Override | |||
protected TableImpl getTable(String name, boolean includeSystemTables) | |||
throws IOException | |||
protected TableImpl getTable(String name, boolean includeSystemTables) | |||
throws IOException | |||
{ | |||
TableImpl table = super.getTable(name, includeSystemTables); | |||
if((table == null) && | |||
if((table == null) && | |||
_resolver.loadCustomTable(this, _customFile, name)) { | |||
table = super.getTable(name, includeSystemTables); | |||
} | |||
@@ -278,7 +284,7 @@ public abstract class CustomLinkResolver implements LinkResolver | |||
try { | |||
super.close(); | |||
} finally { | |||
deleteDbFile(getFile()); | |||
deleteDbFile(getPath()); | |||
closeCustomFile(_customFile); | |||
} | |||
} |
@@ -29,6 +29,9 @@ import java.nio.channels.FileLock; | |||
import java.nio.channels.NonWritableChannelException; | |||
import java.nio.channels.ReadableByteChannel; | |||
import java.nio.channels.WritableByteChannel; | |||
import java.nio.file.OpenOption; | |||
import java.nio.file.Path; | |||
import java.nio.file.StandardOpenOption; | |||
import com.healthmarketscience.jackcess.Database; | |||
import com.healthmarketscience.jackcess.DatabaseBuilder; | |||
@@ -53,8 +56,13 @@ import com.healthmarketscience.jackcess.impl.DatabaseImpl; | |||
* @author James Ahlborn | |||
* @usage _advanced_class_ | |||
*/ | |||
public class MemFileChannel extends FileChannel | |||
public class MemFileChannel extends FileChannel | |||
{ | |||
/** read-only channel access mode */ | |||
public static final String RO_CHANNEL_MODE = "r"; | |||
/** read/write channel access mode */ | |||
public static final String RW_CHANNEL_MODE = "rw"; | |||
private static final byte[][] EMPTY_DATA = new byte[0][]; | |||
// use largest possible Jet "page size" to ensure that reads/writes will | |||
@@ -68,10 +76,10 @@ public class MemFileChannel extends FileChannel | |||
/** current amount of actual data in the file */ | |||
private long _size; | |||
/** chunks containing the file data. the length of the chunk array is | |||
always a power of 2 and the chunks are always CHUNK_SIZE. */ | |||
always a power of 2 and the chunks are always CHUNK_SIZE. */ | |||
private byte[][] _data; | |||
private MemFileChannel() | |||
private MemFileChannel() | |||
{ | |||
this(0L, 0L, EMPTY_DATA); | |||
} | |||
@@ -95,7 +103,7 @@ public class MemFileChannel extends FileChannel | |||
* affect the original File source. | |||
*/ | |||
public static MemFileChannel newChannel(File file) throws IOException { | |||
return newChannel(file, DatabaseImpl.RW_CHANNEL_MODE); | |||
return newChannel(file, RW_CHANNEL_MODE); | |||
} | |||
/** | |||
@@ -105,25 +113,62 @@ public class MemFileChannel extends FileChannel | |||
* modifications to the returned channel will <i>not</i> affect the original | |||
* File source. | |||
*/ | |||
public static MemFileChannel newChannel(File file, String mode) | |||
throws IOException | |||
public static MemFileChannel newChannel(File file, String mode) | |||
throws IOException | |||
{ | |||
FileChannel in = null; | |||
try { | |||
return newChannel(in = new RandomAccessFile( | |||
file, DatabaseImpl.RO_CHANNEL_MODE).getChannel(), | |||
file, RO_CHANNEL_MODE).getChannel(), | |||
mode); | |||
} finally { | |||
ByteUtil.closeQuietly(in); | |||
} | |||
} | |||
/** | |||
* Creates a new MemFileChannel containing the contents of the | |||
* given Path with the given mode (for mode details see | |||
* {@link RandomAccessFile#RandomAccessFile(File,String)}). Note, | |||
* modifications to the returned channel will <i>not</i> affect the original | |||
* File source. | |||
*/ | |||
public static MemFileChannel newChannel(Path file, OpenOption... opts) | |||
throws IOException | |||
{ | |||
FileChannel in = null; | |||
try { | |||
String mode = RO_CHANNEL_MODE; | |||
if(opts != null) { | |||
for(OpenOption opt : opts) { | |||
if(opt == StandardOpenOption.WRITE) { | |||
mode = RW_CHANNEL_MODE; | |||
break; | |||
} | |||
} | |||
} | |||
return newChannel(in = FileChannel.open(file, StandardOpenOption.READ), | |||
mode); | |||
} finally { | |||
ByteUtil.closeQuietly(in); | |||
} | |||
} | |||
/** | |||
* Creates a new read/write MemFileChannel containing the contents of the | |||
* given Path. Note, modifications to the returned channel will <i>not</i> | |||
* affect the original File source. | |||
*/ | |||
public static MemFileChannel newChannel(Path file) throws IOException { | |||
return newChannel(file, DatabaseImpl.RW_CHANNEL_OPTS); | |||
} | |||
/** | |||
* Creates a new read/write MemFileChannel containing the contents of the | |||
* given InputStream. | |||
*/ | |||
public static MemFileChannel newChannel(InputStream in) throws IOException { | |||
return newChannel(in, DatabaseImpl.RW_CHANNEL_MODE); | |||
return newChannel(in, RW_CHANNEL_MODE); | |||
} | |||
/** | |||
@@ -131,8 +176,8 @@ public class MemFileChannel extends FileChannel | |||
* given InputStream with the given mode (for mode details see | |||
* {@link RandomAccessFile#RandomAccessFile(File,String)}). | |||
*/ | |||
public static MemFileChannel newChannel(InputStream in, String mode) | |||
throws IOException | |||
public static MemFileChannel newChannel(InputStream in, String mode) | |||
throws IOException | |||
{ | |||
return newChannel(Channels.newChannel(in), mode); | |||
} | |||
@@ -141,10 +186,10 @@ public class MemFileChannel extends FileChannel | |||
* Creates a new read/write MemFileChannel containing the contents of the | |||
* given ReadableByteChannel. | |||
*/ | |||
public static MemFileChannel newChannel(ReadableByteChannel in) | |||
public static MemFileChannel newChannel(ReadableByteChannel in) | |||
throws IOException | |||
{ | |||
return newChannel(in, DatabaseImpl.RW_CHANNEL_MODE); | |||
return newChannel(in, RW_CHANNEL_MODE); | |||
} | |||
/** | |||
@@ -152,7 +197,7 @@ public class MemFileChannel extends FileChannel | |||
* given ReadableByteChannel with the given mode (for mode details see | |||
* {@link RandomAccessFile#RandomAccessFile(File,String)}). | |||
*/ | |||
public static MemFileChannel newChannel(ReadableByteChannel in, String mode) | |||
public static MemFileChannel newChannel(ReadableByteChannel in, String mode) | |||
throws IOException | |||
{ | |||
MemFileChannel channel = new MemFileChannel(); | |||
@@ -282,7 +327,7 @@ public class MemFileChannel extends FileChannel | |||
if(position >= _size) { | |||
return 0L; | |||
} | |||
count = Math.min(count, _size - position); | |||
int chunkIndex = getChunkIndex(position); | |||
@@ -304,7 +349,7 @@ public class MemFileChannel extends FileChannel | |||
numBytes += bytesWritten; | |||
count -= bytesWritten; | |||
} while(src.hasRemaining()); | |||
++chunkIndex; | |||
chunkOffset = 0; | |||
} | |||
@@ -360,11 +405,11 @@ public class MemFileChannel extends FileChannel | |||
count -= bytesRead; | |||
_size = Math.max(_size, position + numBytes); | |||
} while(dst.hasRemaining()); | |||
++chunkIndex; | |||
chunkOffset = 0; | |||
chunkOffset = 0; | |||
} | |||
return numBytes; | |||
} | |||
@@ -410,7 +455,7 @@ public class MemFileChannel extends FileChannel | |||
private static int getChunkIndex(long pos) { | |||
return (int)(pos / CHUNK_SIZE); | |||
} | |||
private static int getChunkOffset(long pos) { | |||
return (int)(pos % CHUNK_SIZE); | |||
} | |||
@@ -418,7 +463,7 @@ public class MemFileChannel extends FileChannel | |||
private static int getNumChunks(long size) { | |||
return getChunkIndex(size + CHUNK_SIZE - 1); | |||
} | |||
@Override | |||
public long write(ByteBuffer[] srcs, int offset, int length) | |||
throws IOException | |||
@@ -433,7 +478,7 @@ public class MemFileChannel extends FileChannel | |||
@Override | |||
public long read(ByteBuffer[] dsts, int offset, int length) | |||
throws IOException | |||
{ | |||
{ | |||
long numBytes = 0L; | |||
for(int i = offset; i < offset + length; ++i) { | |||
if(_position >= _size) { | |||
@@ -474,7 +519,7 @@ public class MemFileChannel extends FileChannel | |||
{ | |||
super(channel._position, channel._size, channel._data); | |||
} | |||
@Override | |||
public int write(ByteBuffer src, long position) throws IOException { | |||
throw new NonWritableChannelException(); | |||
@@ -491,6 +536,6 @@ public class MemFileChannel extends FileChannel | |||
throws IOException | |||
{ | |||
throw new NonWritableChannelException(); | |||
} | |||
} | |||
} | |||
} |
@@ -51,7 +51,7 @@ import static com.healthmarketscience.jackcess.TestUtil.*; | |||
/** | |||
* @author Tim McCune | |||
*/ | |||
public class DatabaseTest extends TestCase | |||
public class DatabaseTest extends TestCase | |||
{ | |||
public DatabaseTest(String name) throws Exception { | |||
super(name); | |||
@@ -114,7 +114,7 @@ public class DatabaseTest extends TestCase | |||
db.close(); | |||
} | |||
} | |||
public void testReadDeletedRows() throws Exception { | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.DEL, true)) { | |||
Table table = open(testDB).getTable("Table"); | |||
@@ -122,11 +122,11 @@ public class DatabaseTest extends TestCase | |||
while (table.getNextRow() != null) { | |||
rows++; | |||
} | |||
assertEquals(2, rows); | |||
assertEquals(2, rows); | |||
table.getDatabase().close(); | |||
} | |||
} | |||
public void testGetColumns() throws Exception { | |||
for (final TestDB testDB : SUPPORTED_DBS_TEST_FOR_READ) { | |||
@@ -143,9 +143,9 @@ public class DatabaseTest extends TestCase | |||
checkColumn(columns, 8, "I", DataType.BOOLEAN); | |||
} | |||
} | |||
private static void checkColumn( | |||
List<? extends Column> columns, int columnNumber, String name, | |||
List<? extends Column> columns, int columnNumber, String name, | |||
DataType dataType) | |||
throws Exception | |||
{ | |||
@@ -153,7 +153,7 @@ public class DatabaseTest extends TestCase | |||
assertEquals(name, column.getName()); | |||
assertEquals(dataType, column.getType()); | |||
} | |||
public void testGetNextRow() throws Exception { | |||
for (final TestDB testDB : SUPPORTED_DBS_TEST_FOR_READ) { | |||
final Database db = open(testDB); | |||
@@ -183,7 +183,7 @@ public class DatabaseTest extends TestCase | |||
db.close(); | |||
} | |||
} | |||
public void testDeleteCurrentRow() throws Exception { | |||
// make sure correct row is deleted | |||
@@ -269,7 +269,7 @@ public class DatabaseTest extends TestCase | |||
table.reset(); | |||
List<Row> rows = RowFilterTest.toList(table); | |||
Row r1 = rows.remove(7); | |||
Row r2 = rows.remove(3); | |||
assertEquals(8, rows.size()); | |||
@@ -282,10 +282,10 @@ public class DatabaseTest extends TestCase | |||
table.deleteRow(r2); | |||
table.deleteRow(r1); | |||
assertTable(rows, table); | |||
assertTable(rows, table); | |||
} | |||
} | |||
public void testMissingFile() throws Exception { | |||
File bogusFile = new File("fooby-dooby.mdb"); | |||
assertTrue(!bogusFile.exists()); | |||
@@ -326,7 +326,7 @@ public class DatabaseTest extends TestCase | |||
} | |||
rowNum++; | |||
} | |||
table.getDatabase().close(); | |||
} | |||
} | |||
@@ -508,7 +508,7 @@ public class DatabaseTest extends TestCase | |||
db.close(); | |||
} | |||
} | |||
} | |||
public void testMultiPageTableDef() throws Exception | |||
{ | |||
@@ -580,7 +580,7 @@ public class DatabaseTest extends TestCase | |||
db.close(); | |||
} | |||
} | |||
} | |||
public void testLargeTableDef() throws Exception { | |||
@@ -685,8 +685,8 @@ public class DatabaseTest extends TestCase | |||
TimeZone tz = TimeZone.getTimeZone("America/New_York"); | |||
SimpleDateFormat sdf = DatabaseBuilder.createDateFormat("yyyy-MM-dd"); | |||
sdf.getCalendar().setTimeZone(tz); | |||
List<String> dates = Arrays.asList("1582-10-15", "1582-10-14", | |||
List<String> dates = Arrays.asList("1582-10-15", "1582-10-14", | |||
"1492-01-10", "1392-01-10"); | |||
@@ -703,7 +703,7 @@ public class DatabaseTest extends TestCase | |||
Date d = sdf.parse(dateStr); | |||
table.addRow("row " + dateStr, d); | |||
} | |||
List<String> foundDates = new ArrayList<String>(); | |||
for(Row row : table) { | |||
foundDates.add(sdf.format(row.getDate("date"))); | |||
@@ -741,7 +741,7 @@ public class DatabaseTest extends TestCase | |||
sysTables.addAll( | |||
Arrays.asList("MSysObjects", "MSysQueries", "MSysACES", | |||
"MSysRelationships")); | |||
if (fileFormat == FileFormat.GENERIC_JET4) { | |||
assertNull("file format: " + fileFormat, db.getSystemTable("MSysAccessObjects")); | |||
} else if (fileFormat.ordinal() < FileFormat.V2003.ordinal()) { | |||
@@ -766,11 +766,11 @@ public class DatabaseTest extends TestCase | |||
if(fileFormat.ordinal() >= FileFormat.V2010.ordinal()) { | |||
sysTables.add("f_12D7448B56564D8AAE333BCC9B3718E5_Data"); | |||
sysTables.add("MSysResources"); | |||
} | |||
} | |||
} | |||
assertEquals(sysTables, db.getSystemTableNames()); | |||
assertNotNull(db.getSystemTable("MSysObjects")); | |||
assertNotNull(db.getSystemTable("MSysQueries")); | |||
assertNotNull(db.getSystemTable("MSysACES")); | |||
@@ -782,7 +782,7 @@ public class DatabaseTest extends TestCase | |||
assertEquals("MSysObjects", tmd.getName()); | |||
assertFalse(tmd.isLinked()); | |||
assertTrue(tmd.isSystem()); | |||
db.close(); | |||
} | |||
} | |||
@@ -839,7 +839,7 @@ public class DatabaseTest extends TestCase | |||
"RawData[(12) FF FE 6F 74 68 65 72 20 64 61 74 61]", | |||
null); | |||
List<String> fixVals = Arrays.asList("RawData[(4) 37 00 00 00]", | |||
"RawData[(4) F3 FF FF FF]", | |||
"RawData[(4) F3 FF FF FF]", | |||
"RawData[(4) 02 00 00 00]"); | |||
int idx = 0; | |||
@@ -891,7 +891,8 @@ public class DatabaseTest extends TestCase | |||
Database linkeeDb = db.getLinkedDatabases().get(linkeeDbName); | |||
assertNotNull(linkeeDb); | |||
assertEquals(linkeeFile, linkeeDb.getFile()); | |||
assertEquals("linkeeTest.accdb", ((DatabaseImpl)linkeeDb).getName()); | |||
List<? extends Map<String, Object>> expectedRows = | |||
createExpectedTable( | |||
createExpectedRow( | |||
@@ -900,7 +901,7 @@ public class DatabaseTest extends TestCase | |||
assertTable(expectedRows, t2); | |||
db.createLinkedTable("FooTable", linkeeDbName, "Table2"); | |||
db.createLinkedTable("FooTable", linkeeDbName, "Table2"); | |||
tmd = db.getTableMetaData("FooTable"); | |||
assertEquals("FooTable", tmd.getName()); | |||
@@ -929,7 +930,7 @@ public class DatabaseTest extends TestCase | |||
assertNull(tmd.getLinkedDbName()); | |||
Table t1 = tmd.open(db); | |||
assertFalse(db.isLinkedTable(null)); | |||
assertTrue(db.isLinkedTable(t2)); | |||
assertTrue(db.isLinkedTable(t3)); | |||
@@ -941,21 +942,21 @@ public class DatabaseTest extends TestCase | |||
assertTrue(tables.contains(t2)); | |||
assertTrue(tables.contains(t3)); | |||
assertFalse(tables.contains(((DatabaseImpl)db).getSystemCatalog())); | |||
tables = getTables(db.newIterable().setIncludeNormalTables(false)); | |||
assertEquals(2, tables.size()); | |||
assertFalse(tables.contains(t1)); | |||
assertTrue(tables.contains(t2)); | |||
assertTrue(tables.contains(t3)); | |||
assertFalse(tables.contains(((DatabaseImpl)db).getSystemCatalog())); | |||
tables = getTables(db.newIterable().withLocalUserTablesOnly()); | |||
assertEquals(1, tables.size()); | |||
assertTrue(tables.contains(t1)); | |||
assertFalse(tables.contains(t2)); | |||
assertFalse(tables.contains(t3)); | |||
assertFalse(tables.contains(((DatabaseImpl)db).getSystemCatalog())); | |||
tables = getTables(db.newIterable().withSystemTablesOnly()); | |||
assertTrue(tables.size() > 5); | |||
assertFalse(tables.contains(t1)); | |||
@@ -975,7 +976,7 @@ public class DatabaseTest extends TestCase | |||
} | |||
return tableList; | |||
} | |||
public void testTimeZone() throws Exception | |||
{ | |||
TimeZone tz = TimeZone.getTimeZone("America/New_York"); |
@@ -86,7 +86,7 @@ public class TestUtil | |||
throws Exception | |||
{ | |||
FileChannel channel = (inMem ? MemFileChannel.newChannel( | |||
file, DatabaseImpl.RW_CHANNEL_MODE) | |||
file, MemFileChannel.RW_CHANNEL_MODE) | |||
: null); | |||
final Database db = new DatabaseBuilder(file).setReadOnly(true) | |||
.setAutoSync(getTestAutoSync()).setChannel(channel).open(); |
@@ -98,7 +98,7 @@ public class JetFormatTest extends TestCase { | |||
} | |||
SUPPORTED_FILEFORMATS = supported.toArray(new FileFormat[0]); | |||
SUPPORTED_FILEFORMATS_FOR_READ = | |||
SUPPORTED_FILEFORMATS_FOR_READ = | |||
supportedForRead.toArray(new FileFormat[0]); | |||
} | |||
@@ -110,7 +110,7 @@ public class JetFormatTest extends TestCase { | |||
private final File dbFile; | |||
private final FileFormat expectedFileFormat; | |||
private TestDB(File databaseFile, | |||
private TestDB(File databaseFile, | |||
FileFormat expectedDBFileFormat) { | |||
dbFile = databaseFile; | |||
@@ -119,12 +119,12 @@ public class JetFormatTest extends TestCase { | |||
public final File getFile() { return dbFile; } | |||
public final FileFormat getExpectedFileFormat() { | |||
return expectedFileFormat; | |||
public final FileFormat getExpectedFileFormat() { | |||
return expectedFileFormat; | |||
} | |||
public final JetFormat getExpectedFormat() { | |||
return DatabaseImpl.getFileFormatDetails(expectedFileFormat).getFormat(); | |||
public final JetFormat getExpectedFormat() { | |||
return DatabaseImpl.getFileFormatDetails(expectedFileFormat).getFormat(); | |||
} | |||
@Override | |||
@@ -141,14 +141,14 @@ public class JetFormatTest extends TestCase { | |||
boolean readOnly) { | |||
List<TestDB> supportedTestDBs = new ArrayList<TestDB>(); | |||
for (FileFormat fileFormat : | |||
for (FileFormat fileFormat : | |||
(readOnly ? SUPPORTED_FILEFORMATS_FOR_READ : | |||
SUPPORTED_FILEFORMATS)) { | |||
File testFile = getFileForBasename(basename, fileFormat); | |||
if(!testFile.exists()) { | |||
continue; | |||
} | |||
// verify that the db is the file format expected | |||
try { | |||
Database db = new DatabaseBuilder(testFile).setReadOnly(true).open(); | |||
@@ -170,16 +170,16 @@ public class JetFormatTest extends TestCase { | |||
private static File getFileForBasename( | |||
Basename basename, FileFormat fileFormat) { | |||
return new File(DIR_TEST_DATA, | |||
return new File(DIR_TEST_DATA, | |||
fileFormat.name() + File.separator + | |||
basename + fileFormat.name() + | |||
basename + fileFormat.name() + | |||
fileFormat.getFileExtension()); | |||
} | |||
} | |||
public static final List<TestDB> SUPPORTED_DBS_TEST = | |||
public static final List<TestDB> SUPPORTED_DBS_TEST = | |||
TestDB.getSupportedForBasename(Basename.TEST); | |||
public static final List<TestDB> SUPPORTED_DBS_TEST_FOR_READ = | |||
public static final List<TestDB> SUPPORTED_DBS_TEST_FOR_READ = | |||
TestDB.getSupportedForBasename(Basename.TEST, true); | |||
@@ -193,11 +193,12 @@ public class JetFormatTest extends TestCase { | |||
for (final TestDB testDB : SUPPORTED_DBS_TEST_FOR_READ) { | |||
final FileChannel channel = DatabaseImpl.openChannel(testDB.dbFile, false); | |||
final FileChannel channel = DatabaseImpl.openChannel( | |||
testDB.dbFile.toPath(), false); | |||
try { | |||
JetFormat fmtActual = JetFormat.getFormat(channel); | |||
assertEquals("Unexpected JetFormat for dbFile: " + | |||
assertEquals("Unexpected JetFormat for dbFile: " + | |||
testDB.dbFile.getAbsolutePath(), | |||
testDB.getExpectedFormat(), fmtActual); | |||
@@ -221,7 +222,7 @@ public class JetFormatTest extends TestCase { | |||
PropertyMap props = db.getUserDefinedProperties(); | |||
props.put("foo", "bar"); | |||
props.save(); | |||
} | |||
} | |||
} catch(Exception e) { | |||
failure = e; | |||
@@ -268,7 +269,7 @@ public class JetFormatTest extends TestCase { | |||
} | |||
public void testSqlTypes() throws Exception { | |||
JetFormat v2000 = JetFormat.VERSION_4; | |||
for(DataType dt : DataType.values()) { | |||
if(v2000.isSupportedDataType(dt)) { |
@@ -16,9 +16,9 @@ limitations under the License. | |||
package com.healthmarketscience.jackcess.util; | |||
import java.io.File; | |||
import java.io.FileNotFoundException; | |||
import java.io.IOException; | |||
import java.nio.file.Path; | |||
import com.healthmarketscience.jackcess.ColumnBuilder; | |||
import com.healthmarketscience.jackcess.DataType; | |||
@@ -55,7 +55,7 @@ public class CustomLinkResolverTest extends TestCase | |||
Table t1 = db.getTable("Table1"); | |||
assertNotNull(t1); | |||
assertNotSame(db, t1.getDatabase()); | |||
assertTable(createExpectedTable(createExpectedRow("id", 0, | |||
"data1", "row0"), | |||
createExpectedRow("id", 1, | |||
@@ -101,7 +101,7 @@ public class CustomLinkResolverTest extends TestCase | |||
Database linkerDb, String linkeeFileName) throws IOException | |||
{ | |||
return (("testFile1.txt".equals(linkeeFileName) || | |||
"testFile2.txt".equals(linkeeFileName)) ? | |||
"testFile2.txt".equals(linkeeFileName)) ? | |||
linkeeFileName : null); | |||
} | |||
@@ -121,7 +121,7 @@ public class CustomLinkResolverTest extends TestCase | |||
for(int i = 0; i < 3; ++i) { | |||
t.addRow(i, "row" + i); | |||
} | |||
return true; | |||
} else if("OtherTable2".equals(tableName)) { | |||
@@ -135,7 +135,7 @@ public class CustomLinkResolverTest extends TestCase | |||
for(int i = 3; i < 6; ++i) { | |||
t.addRow(i, "row" + i); | |||
} | |||
return true; | |||
} else if("Table4".equals(tableName)) { | |||
@@ -149,7 +149,7 @@ public class CustomLinkResolverTest extends TestCase | |||
@Override | |||
protected Database createTempDb(Object customFile, FileFormat format, | |||
boolean inMemory, File tempDir) | |||
boolean inMemory, Path tempDir) | |||
throws IOException | |||
{ | |||
inMemory = "testFile1.txt".equals(customFile); |