git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1240 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-2.2.1
@@ -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. |
@@ -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" |
@@ -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> |
@@ -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); | |||
@@ -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_ | |||
*/ |
@@ -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(); |
@@ -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); | |||
} |
@@ -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(); | |||
} | |||
}; | |||
@@ -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); | |||
} | |||
} | |||
} |