diff options
9 files changed, 86 insertions, 35 deletions
diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 0000000..bf7ce69 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,5 @@ +Many of the financial functions have been originally copied from the Apache +POI project (Apache Software Foundation) and the UCanAccess Project. They +have been then modified and adapted so that they are integrated with Jackcess, +in a consistent manner. The Apache POI and UCanAccess projects are licensed +under Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0. diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 74c82ad..3d1160d 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -6,8 +6,26 @@ <body> <release version="2.2.1" date="TBD"> <action dev="jahlborn" type="update"> - Implement IsNumeric function. Add support for hex/oct integer - strings. + Implement the majority of the missing standard functions: + FormatCurrency, FormatDateTime, FormatNumber, FormatPercent, Val, + DateAdd, DateDiff, DatePart, MonthName, WeekdayName, DDB, IPmt, PPmt, + Rate, SLN, SYD, Format, Replace, StrConv. (Note that the internal API + for the expr package has changed in an incompatible way. However, + since the API is still experimental, this was deemed acceptable for a + minor version.). Note that many of the financial functions were + copied and adpated from the Apache POI and UCanAccess projects (which + are both under the Apache License 2.0). + </action> + <action dev="jahlborn" type="update"> + Implement more type coercion methods for expressions. Add support for + hex/oct integer strings. Add support for number strings with commas. + Add support for coercing numeric String to a date/time value. Add + support for date/time values with implicit (current) year. + </action> + <action dev="jahlborn" type="fix" system="SourceForge2" issue="150"> + Ignore column validators for read-only dbs. This will avoid + irrelevant failures when reading databases which have invalid column + properties. </action> </release> <release version="2.2.0" date="2018-09-08" diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java b/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java index 28d9a86..6e4d5ab 100644 --- a/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java +++ b/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java @@ -215,7 +215,7 @@ limitations under the License. * * <table border="1" width="25%" cellpadding="3" cellspacing="0"> * <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr> - * <tr class="TableRowColor"><td>Format[$]</td><td></td></tr> + * <tr class="TableRowColor"><td>Format[$]</td><td>Partial</td></tr> * <tr class="TableRowColor"><td>InStr</td><td>Y</td></tr> * <tr class="TableRowColor"><td>InStrRev</td><td>Y</td></tr> * <tr class="TableRowColor"><td>LCase[$]</td><td>Y</td></tr> diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java index 6d237b4..0016687 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java @@ -491,6 +491,12 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> { } void initColumnValidator() throws IOException { + + if(getDatabase().isReadOnly()) { + // validators are irrelevant for read-only databases + return; + } + // first initialize any "external" (user-defined) validator setColumnValidator(null); diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java index 6831e99..bb74bba 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java @@ -255,6 +255,8 @@ public class DatabaseImpl implements Database private final File _file; /** the simple name of the database */ private final String _name; + /** whether or not this db is read-only */ + private final boolean _readOnly; /** Buffer to hold database pages */ private ByteBuffer _buffer; /** ID of the Tables system object */ @@ -399,7 +401,8 @@ public class DatabaseImpl implements Database } DatabaseImpl db = new DatabaseImpl(mdbFile, channel, closeChannel, autoSync, - null, charset, timeZone, provider); + null, charset, timeZone, provider, + readOnly); success = true; return db; @@ -458,7 +461,8 @@ public class DatabaseImpl implements Database transferDbFrom(channel, getResourceAsStream(details.getEmptyFilePath())); channel.force(true); DatabaseImpl db = new DatabaseImpl(mdbFile, channel, closeChannel, autoSync, - fileFormat, charset, timeZone, null); + fileFormat, charset, timeZone, null, + false); success = true; return db; } finally { @@ -510,11 +514,13 @@ public class DatabaseImpl implements Database */ protected DatabaseImpl(File file, FileChannel channel, boolean closeChannel, boolean autoSync, FileFormat fileFormat, Charset charset, - TimeZone timeZone, CodecProvider provider) + TimeZone timeZone, CodecProvider provider, + boolean readOnly) throws IOException { _file = file; _name = getName(file); + _readOnly = readOnly; _format = JetFormat.getFormat(channel); _charset = ((charset == null) ? getDefaultCharset(_format) : charset); _columnOrder = getDefaultColumnOrder(); @@ -543,6 +549,10 @@ public class DatabaseImpl implements Database return _name; } + public boolean isReadOnly() { + return _readOnly; + } + /** * @usage _advanced_method_ */ diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultTextFunctions.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultTextFunctions.java index 5643766..74856aa 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultTextFunctions.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultTextFunctions.java @@ -425,7 +425,7 @@ public class DefaultTextFunctions } }); - public static final Function FORMAT = registerFunc(new FuncVar("Format", 1, 4) { + public static final Function FORMAT = registerStringFunc(new FuncVar("Format", 1, 4) { @Override protected Value evalVar(EvalContext ctx, Value[] params) { @@ -441,11 +441,11 @@ public class DefaultTextFunctions String fmtStr = params[1].getAsString(ctx); int firstDay = DefaultDateFunctions.getFirstDayParam(ctx, params, 2); int firstWeekType = DefaultDateFunctions.getFirstWeekTypeParam(ctx, params, 3); - + return FormatUtil.format(ctx, expr, fmtStr, firstDay, firstWeekType); } }); - + private static String nchars(int num, char c) { StringBuilder sb = new StringBuilder(num); nchars(sb, num, c); @@ -457,7 +457,7 @@ public class DefaultTextFunctions sb.append(c); } } - + private static String trim(String str, boolean doLeft, boolean doRight) { int start = 0; int end = str.length(); diff --git a/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java b/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java index 7992d71..fce5289 100644 --- a/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java +++ b/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java @@ -117,28 +117,31 @@ 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(), - getDefaultTempDirectory()); + // if linker is read-only, open linkee read-only + boolean readOnly = ((linkerDb instanceof DatabaseImpl) ? + ((DatabaseImpl)linkerDb).isReadOnly() : false); + return createTempDb(customFile, getDefaultFormat(), isDefaultInMemory(), + getDefaultTempDirectory(), readOnly); } return LinkResolver.DEFAULT.resolveLinkedDatabase(linkerDb, linkeeFileName); } @@ -157,8 +160,9 @@ 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, File tempDir, + boolean readOnly) throws IOException { File dbFile = null; @@ -178,8 +182,8 @@ public abstract class CustomLinkResolver implements LinkResolver } TempDatabaseImpl.initDbChannel(channel, format); - TempDatabaseImpl db = new TempDatabaseImpl(this, customFile, dbFile, - channel, format); + TempDatabaseImpl db = new TempDatabaseImpl(this, customFile, dbFile, + channel, format, readOnly); success = true; return db; @@ -203,7 +207,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,21 +256,22 @@ public abstract class CustomLinkResolver implements LinkResolver private final Object _customFile; protected TempDatabaseImpl(CustomLinkResolver resolver, Object customFile, - File file, FileChannel channel, - FileFormat fileFormat) + File file, FileChannel channel, + FileFormat fileFormat, boolean readOnly) throws IOException { - super(file, channel, true, false, fileFormat, null, null, null); + super(file, channel, true, false, fileFormat, null, null, null, + readOnly); _resolver = resolver; _customFile = customFile; } @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); } diff --git a/src/main/java/com/healthmarketscience/jackcess/util/LinkResolver.java b/src/main/java/com/healthmarketscience/jackcess/util/LinkResolver.java index ebb1bbd..5310449 100644 --- a/src/main/java/com/healthmarketscience/jackcess/util/LinkResolver.java +++ b/src/main/java/com/healthmarketscience/jackcess/util/LinkResolver.java @@ -21,6 +21,7 @@ import java.io.IOException; import com.healthmarketscience.jackcess.Database; import com.healthmarketscience.jackcess.DatabaseBuilder; +import com.healthmarketscience.jackcess.impl.DatabaseImpl; /** * Resolver for linked databases. @@ -28,7 +29,7 @@ import com.healthmarketscience.jackcess.DatabaseBuilder; * @author James Ahlborn * @usage _intermediate_class_ */ -public interface LinkResolver +public interface LinkResolver { /** * default link resolver used if none provided @@ -39,7 +40,11 @@ public interface LinkResolver String linkeeFileName) throws IOException { - return DatabaseBuilder.open(new File(linkeeFileName)); + // if linker is read-only, open linkee read-only + boolean readOnly = ((linkerDb instanceof DatabaseImpl) ? + ((DatabaseImpl)linkerDb).isReadOnly() : false); + return new DatabaseBuilder(new File(linkeeFileName)) + .setReadOnly(readOnly).open(); } }; diff --git a/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java b/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java index 31a8853..87f8e6f 100644 --- a/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java @@ -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,11 +149,13 @@ public class CustomLinkResolverTest extends TestCase @Override protected Database createTempDb(Object customFile, FileFormat format, - boolean inMemory, File tempDir) + boolean inMemory, File tempDir, + boolean readOnly) throws IOException { inMemory = "testFile1.txt".equals(customFile); - return super.createTempDb(customFile, format, inMemory, tempDir); + return super.createTempDb(customFile, format, inMemory, tempDir, + readOnly); } } } |