aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Cursor.java18
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/CursorBuilder.java4
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/DataType.java56
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Database.java52
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/DatabaseBuilder.java53
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/DateTimeType.java33
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/IndexCursor.java16
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/InvalidValueException.java4
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/PropertyMap.java8
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Row.java21
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Table.java14
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/TableBuilder.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/complex/Attachment.java28
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/complex/ComplexValueForeignKey.java46
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/complex/Version.java10
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/expr/Identifier.java9
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/expr/LocaleContext.java15
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/expr/TemporalConfig.java55
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/expr/Value.java6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/expr/package-info.java20
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java21
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java421
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/CompoundOleUtil.java14
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/CursorImpl.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/CustomToStringStyle.java20
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java23
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java110
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java348
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/IndexPageCache.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/PropertyMapImpl.java3
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/RowIdImpl.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/RowImpl.java12
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/ZoneContext.java32
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/complex/AttachmentColumnInfoImpl.java34
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/complex/ComplexValueForeignKeyImpl.java123
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/complex/VersionHistoryColumnInfoImpl.java68
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseDelayedValue.java6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseNumericValue.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseValue.java8
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/DateTimeValue.java12
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultDateFunctions.java449
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java15
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultTextFunctions.java6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java183
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java13
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/FormatUtil.java8
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java5
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/ValueSupport.java49
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/query/QueryFormat.java15
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java44
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/MemFileChannel.java95
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/OleBlob.java12
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/ReadOnlyFileChannel.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/RowFilter.java32
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnMatcher.java8
-rw-r--r--src/main/resources/com/healthmarketscience/jackcess/log4j.properties6
-rw-r--r--src/site/javadoc/taglets.properties41
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/ComplexColumnTest.java73
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java65
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/LocalDateTimeTest.java160
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/PropertyExpressionTest.java4
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/TableTest.java22
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/TestUtil.java3
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/impl/DatabaseReadWriteTest.java18
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java33
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java9
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java33
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/query/QueryTest.java57
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java4
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/util/ExportTest.java11
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/util/OleBlobTest.java42
-rw-r--r--src/test/resources/log4j_test.properties6
-rw-r--r--src/test/resources/logging_test.properties1
81 files changed, 1922 insertions, 1359 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/Cursor.java b/src/main/java/com/healthmarketscience/jackcess/Cursor.java
index 7260b79..8503623 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Cursor.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Cursor.java
@@ -31,7 +31,7 @@ import com.healthmarketscience.jackcess.util.IterableBuilder;
* of table modification during traversal (although depending on how the table
* is traversed, row updates may or may not be seen). Multiple cursors may
* traverse the same table simultaneously.
- * <p/>
+ * <br>
* Basic cursors will generally iterate table data in the order it appears in
* the database and searches will require scanning the entire table.
* Additional features are available when utilizing an {@link Index} backed
@@ -135,7 +135,7 @@ public interface Cursor extends Iterable<Row>
* Iterator which will iterate through all the rows of this table. Use of
* the Iterator follows the same restrictions as a call to
* {@link #getNextRow}.
- * <p/>
+ * <br>
* For more flexible iteration see {@link #newIterable}.
* @throws RuntimeIOException if an IOException is thrown by one of the
* operations, the actual exception will be contained within
@@ -151,7 +151,7 @@ public interface Cursor extends Iterable<Row>
/**
* Delete the current row.
- * <p/>
+ * <br>
* Note, re-deleting an already deleted row is allowed (it does nothing).
* @throws IllegalStateException if the current row is not valid (at
* beginning or end of table)
@@ -178,7 +178,7 @@ public interface Cursor extends Iterable<Row>
/**
* Moves to the next row in the table and returns it.
- * @return The next row in this table (Column name -> Column value), or
+ * @return The next row in this table (Column name -&gt; Column value), or
* {@code null} if no next row is found
*/
public Row getNextRow() throws IOException;
@@ -186,7 +186,7 @@ public interface Cursor extends Iterable<Row>
/**
* Moves to the next row in the table and returns it.
* @param columnNames Only column names in this collection will be returned
- * @return The next row in this table (Column name -> Column value), or
+ * @return The next row in this table (Column name -&gt; Column value), or
* {@code null} if no next row is found
*/
public Row getNextRow(Collection<String> columnNames)
@@ -194,7 +194,7 @@ public interface Cursor extends Iterable<Row>
/**
* Moves to the previous row in the table and returns it.
- * @return The previous row in this table (Column name -> Column value), or
+ * @return The previous row in this table (Column name -&gt; Column value), or
* {@code null} if no previous row is found
*/
public Row getPreviousRow() throws IOException;
@@ -202,7 +202,7 @@ public interface Cursor extends Iterable<Row>
/**
* Moves to the previous row in the table and returns it.
* @param columnNames Only column names in this collection will be returned
- * @return The previous row in this table (Column name -> Column value), or
+ * @return The previous row in this table (Column name -&gt; Column value), or
* {@code null} if no previous row is found
*/
public Row getPreviousRow(Collection<String> columnNames)
@@ -325,12 +325,12 @@ public interface Cursor extends Iterable<Row>
public int movePreviousRows(int numRows) throws IOException;
/**
- * Returns the current row in this cursor (Column name -> Column value).
+ * Returns the current row in this cursor (Column name -&gt; Column value).
*/
public Row getCurrentRow() throws IOException;
/**
- * Returns the current row in this cursor (Column name -> Column value).
+ * Returns the current row in this cursor (Column name -&gt; Column value).
* @param columnNames Only column names in this collection will be returned
*/
public Row getCurrentRow(Collection<String> columnNames)
diff --git a/src/main/java/com/healthmarketscience/jackcess/CursorBuilder.java b/src/main/java/com/healthmarketscience/jackcess/CursorBuilder.java
index 831c78f..3ae058b 100644
--- a/src/main/java/com/healthmarketscience/jackcess/CursorBuilder.java
+++ b/src/main/java/com/healthmarketscience/jackcess/CursorBuilder.java
@@ -34,14 +34,14 @@ import com.healthmarketscience.jackcess.util.ColumnMatcher;
* Builder style class for constructing a {@link Cursor}. By default, a
* cursor is created at the beginning of the table, and any start/end rows are
* inclusive.
- * <p/>
+ * <br>
* Simple example traversal:
* <pre>
* for(Row row : table.newCursor().toCursor()) {
* // ... process each row ...
* }
* </pre>
- * <p/>
+ * <br>
* Simple example search:
* <pre>
* Row row = CursorBuilder.findRow(table, Collections.singletonMap(col, "foo"));
diff --git a/src/main/java/com/healthmarketscience/jackcess/DataType.java b/src/main/java/com/healthmarketscience/jackcess/DataType.java
index 6850ab6..11483b1 100644
--- a/src/main/java/com/healthmarketscience/jackcess/DataType.java
+++ b/src/main/java/com/healthmarketscience/jackcess/DataType.java
@@ -24,13 +24,14 @@ import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import java.time.LocalDateTime;
import com.healthmarketscience.jackcess.impl.DatabaseImpl;
import com.healthmarketscience.jackcess.impl.JetFormat;
/**
* Supported access data types.
- *
+ *
* @author Tim McCune
* @usage _general_class_
*/
@@ -87,9 +88,10 @@ public enum DataType {
*/
DOUBLE((byte) 0x07, Types.DOUBLE, 8),
/**
- * Corresponds to a java {@link Date}. Accepts a Date, any {@link Number}
- * (using {@link Number#longValue}), or {@code null}. Equivalent to SQL
- * {@link Types#TIMESTAMP}, {@link Types#DATE}, {@link Types#TIME}.
+ * Corresponds to a java {@link Date} or {@link LocalDateTime}. Accepts a
+ * Date, LocalDateTime (or related types), any {@link Number} (using {@link
+ * Number#longValue}), or {@code null}. Equivalent to SQL {@link
+ * Types#TIMESTAMP}, {@link Types#DATE}, {@link Types#TIME}.
*/
SHORT_DATE_TIME((byte) 0x08, Types.TIMESTAMP, 8),
/**
@@ -104,7 +106,7 @@ public enum DataType {
* null}. Equivalent to SQL {@link Types#VARCHAR}, {@link Types#CHAR}.
*/
TEXT((byte) 0x0A, Types.VARCHAR, null, true, false, 0,
- JetFormat.TEXT_FIELD_MAX_LENGTH, JetFormat.TEXT_FIELD_MAX_LENGTH,
+ JetFormat.TEXT_FIELD_MAX_LENGTH, JetFormat.TEXT_FIELD_MAX_LENGTH,
JetFormat.TEXT_FIELD_UNIT_SIZE),
/**
* Corresponds to a java {@code byte[]} of max length 16777215 bytes.
@@ -151,7 +153,7 @@ public enum DataType {
* Complex type corresponds to a special {@link #LONG} autonumber field
* which is the key for a secondary table which holds the "real" data.
*/
- COMPLEX_TYPE((byte) 0x12, null, 4),
+ COMPLEX_TYPE((byte) 0x12, null, 4),
/**
* Corresponds to a java {@link Long}. Accepts any {@link Number} (using
* {@link Number#longValue}), Boolean as 1 or 0, any Object converted to a
@@ -206,7 +208,7 @@ public enum DataType {
addNewSqlType("TIME_WITH_TIMEZONE", SHORT_DATE_TIME, null);
addNewSqlType("TIMESTAMP_WITH_TIMEZONE", SHORT_DATE_TIME, null);
}
-
+
private static Map<Byte, DataType> DATA_TYPES = new HashMap<Byte, DataType>();
static {
for (DataType type : DataType.values()) {
@@ -249,11 +251,11 @@ public enum DataType {
private final int _maxPrecision;
/** the number of bytes per "unit" for this data type */
private final int _unitSize;
-
+
private DataType(byte value) {
this(value, null, null);
}
-
+
private DataType(byte value, Integer sqlType, Integer fixedSize) {
this(value, sqlType, fixedSize, false, false, 0, 0, 0, 1);
}
@@ -269,7 +271,7 @@ public enum DataType {
minSize, defaultSize, maxSize,
false, 0, 0, 0, 0, 0, 0, unitSize);
}
-
+
private DataType(byte value, Integer sqlType, Integer fixedSize,
boolean variableLength,
boolean longValue,
@@ -301,11 +303,11 @@ public enum DataType {
_maxPrecision = maxPrecision;
_unitSize = unitSize;
}
-
+
public byte getValue() {
return _value;
}
-
+
public boolean isVariableLength() {
return _variableLength;
}
@@ -315,7 +317,7 @@ public enum DataType {
// e.g. NUMERIC
return (isVariableLength() && (getMinSize() != getMaxSize()));
}
-
+
public boolean isLongValue() {
return _longValue;
}
@@ -327,7 +329,7 @@ public enum DataType {
public int getFixedSize() {
return getFixedSize(null);
}
-
+
public int getFixedSize(Short colLength) {
if(_fixedSize != null) {
if(colLength != null) {
@@ -338,7 +340,7 @@ public enum DataType {
if(colLength != null) {
return colLength;
}
- throw new IllegalArgumentException("Unexpected fixed length column " +
+ throw new IllegalArgumentException("Unexpected fixed length column " +
this);
}
@@ -353,7 +355,7 @@ public enum DataType {
public int getMaxSize() {
return _maxSize;
}
-
+
public int getSQLType() throws SQLException {
if (_sqlType != null) {
return _sqlType;
@@ -368,19 +370,19 @@ public enum DataType {
public int getDefaultScale() {
return _defaultScale;
}
-
+
public int getMaxScale() {
return _maxScale;
}
-
+
public int getMinPrecision() {
return _minPrecision;
}
-
+
public int getDefaultPrecision() {
return _defaultPrecision;
}
-
+
public int getMaxPrecision() {
return _maxPrecision;
}
@@ -414,7 +416,7 @@ public enum DataType {
private static boolean isWithinRange(int value, int minValue, int maxValue) {
return((value >= minValue) && (value <= maxValue));
}
-
+
public int toValidSize(int size) {
return toValidRange(size, getMinSize(), getMaxSize());
}
@@ -442,12 +444,12 @@ public enum DataType {
public boolean isUnsupported() {
return((this == UNSUPPORTED_FIXEDLEN) || (this == UNSUPPORTED_VARLEN));
}
-
+
private static int toValidRange(int value, int minValue, int maxValue) {
return((value > maxValue) ? maxValue :
((value < minValue) ? minValue : value));
}
-
+
public static DataType fromByte(byte b) throws IOException {
DataType rtn = DATA_TYPES.get(b);
if (rtn != null) {
@@ -455,13 +457,13 @@ public enum DataType {
}
throw new IOException("Unrecognized data type: " + b);
}
-
+
public static DataType fromSQLType(int sqlType)
throws SQLException
{
return fromSQLType(sqlType, 0, null);
}
-
+
public static DataType fromSQLType(int sqlType, int lengthInUnits)
throws SQLException
{
@@ -504,7 +506,7 @@ public enum DataType {
rtn = altRtn;
}
}
-
+
return rtn;
}
@@ -512,7 +514,7 @@ public enum DataType {
* Adds mappings for a sql type which was added after jdk 1.5 (using
* reflection).
*/
- private static void addNewSqlType(String typeName, DataType type,
+ private static void addNewSqlType(String typeName, DataType type,
DataType altType)
{
try {
diff --git a/src/main/java/com/healthmarketscience/jackcess/Database.java b/src/main/java/com/healthmarketscience/jackcess/Database.java
index d853fe8..3a7b65a 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Database.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Database.java
@@ -21,6 +21,8 @@ import java.io.File;
import java.io.Flushable;
import java.io.IOException;
import java.nio.charset.Charset;
+import java.nio.file.Path;
+import java.time.ZoneId;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
@@ -29,8 +31,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;
@@ -44,11 +46,11 @@ import com.healthmarketscience.jackcess.util.TableIterableBuilder;
* Database has been opened, you can interact with the data via the relevant
* {@link Table}. When a Database instance is no longer useful, it should
* <b>always</b> be closed ({@link #close}) to avoid corruption.
- * <p/>
+ * <br>
* Database instances (and all the related objects) are <i>not</i>
* thread-safe. However, separate Database instances (and their respective
* objects) can be used by separate threads without a problem.
- * <p/>
+ * <br>
* Database instances do not implement any "transactional" support, and
* therefore concurrent editing of the same database file by multiple Database
* instances (or with outside programs such as MS Access) <i>will generally
@@ -179,6 +181,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_
*/
@@ -201,6 +208,7 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
* database while an Iterator is in use.
* @usage _general_method_
*/
+ @Override
public Iterator<Table> iterator();
/**
@@ -240,7 +248,7 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
/**
* Finds all the relationships in the database in <i>non-system</i> tables.
- * </p>
+ * <br>
* Warning, this may load <i>all</i> the Tables (metadata, not data) in the
* database which could cause memory issues.
* @usage _intermediate_method_
@@ -250,7 +258,7 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
/**
* Finds <i>all</i> the relationships in the database, <i>including system
* tables</i>.
- * </p>
+ * <br>
* Warning, this may load <i>all</i> the Tables (metadata, not data) in the
* database which could cause memory issues.
* @usage _intermediate_method_
@@ -319,6 +327,7 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
* databases) to disk.
* @usage _general_method_
*/
+ @Override
public void flush() throws IOException;
/**
@@ -329,6 +338,7 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
* OutputStream or jdbc Connection).
* @usage _general_method_
*/
+ @Override
public void close() throws IOException;
/**
@@ -377,18 +387,34 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
public boolean isLinkedTable(Table table) throws IOException;
/**
- * Gets currently configured TimeZone (always non-{@code null}).
+ * Gets currently configured TimeZone (always non-{@code null} and aligned
+ * with the ZoneId).
* @usage _intermediate_method_
*/
public TimeZone getTimeZone();
/**
- * Sets a new TimeZone. If {@code null}, resets to the default value.
+ * Sets a new TimeZone. If {@code null}, resets to the default value. Note
+ * that setting the TimeZone will alter the ZoneId as well.
* @usage _intermediate_method_
*/
public void setTimeZone(TimeZone newTimeZone);
/**
+ * Gets currently configured ZoneId (always non-{@code null} and aligned
+ * with the TimeZone).
+ * @usage _intermediate_method_
+ */
+ public ZoneId getZoneId();
+
+ /**
+ * Sets a new ZoneId. If {@code null}, resets to the default value. Note
+ * that setting the ZoneId will alter the TimeZone as well.
+ * @usage _intermediate_method_
+ */
+ public void setZoneId(ZoneId newZoneId);
+
+ /**
* Gets currently configured Charset (always non-{@code null}).
* @usage _intermediate_method_
*/
@@ -492,4 +518,16 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
* Returns the EvalConfig for configuring expression evaluation.
*/
public EvalConfig getEvalConfig();
+
+ /**
+ * Gets the currently configured DateTimeType.
+ * @usage _general_method_
+ */
+ public DateTimeType getDateTimeType();
+
+ /**
+ * Sets the DateTimeType. If {@code null}, resets to the default value.
+ * @usage _general_method_
+ */
+ public void setDateTimeType(DateTimeType dateTimeType);
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/DatabaseBuilder.java b/src/main/java/com/healthmarketscience/jackcess/DatabaseBuilder.java
index c5e0252..dce1901 100644
--- a/src/main/java/com/healthmarketscience/jackcess/DatabaseBuilder.java
+++ b/src/main/java/com/healthmarketscience/jackcess/DatabaseBuilder.java
@@ -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;
@@ -35,12 +36,12 @@ import com.healthmarketscience.jackcess.util.MemFileChannel;
/**
* Builder style class for opening/creating a {@link Database}.
- * <p/>
+ * <br>
* Simple example usage:
* <pre>
* Database db = DatabaseBuilder.open(new File("test.mdb"));
* </pre>
- * <p/>
+ * <br>
* Advanced example usage:
* <pre>
* Database db = new DatabaseBuilder(new File("test.mdb"))
@@ -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);
+ }
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/DateTimeType.java b/src/main/java/com/healthmarketscience/jackcess/DateTimeType.java
new file mode 100644
index 0000000..8704350
--- /dev/null
+++ b/src/main/java/com/healthmarketscience/jackcess/DateTimeType.java
@@ -0,0 +1,33 @@
+/*
+Copyright (c) 2018 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;
+
+/**
+ * Enum for selecting how a Database returns date/time types. Prefer using
+ * {@link DateTimeType#LOCAL_DATE_TIME} as using Date is being phased out and
+ * will eventually be removed.
+ *
+ * @author James Ahlborn
+ */
+public enum DateTimeType
+{
+ /** use legacy {@link java.util.Date} objects. To maintain backwards
+ compatibility, this is the default type. */
+ DATE,
+ /** use jdk8+ {@link java.time.LocalDateTime} objects */
+ LOCAL_DATE_TIME;
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/IndexCursor.java b/src/main/java/com/healthmarketscience/jackcess/IndexCursor.java
index 1e4aa34..a48a2ce 100644
--- a/src/main/java/com/healthmarketscience/jackcess/IndexCursor.java
+++ b/src/main/java/com/healthmarketscience/jackcess/IndexCursor.java
@@ -41,9 +41,9 @@ public interface IndexCursor extends Cursor
* @param entryValues the column values for the index's columns.
* @return the matching row or {@code null} if a match could not be found.
*/
- public Row findRowByEntry(Object... entryValues)
+ public Row findRowByEntry(Object... entryValues)
throws IOException;
-
+
/**
* Moves to the first row (as defined by the cursor) where the index entries
* match the given values. If a match is not found (or an exception is
@@ -56,32 +56,32 @@ public interface IndexCursor extends Cursor
* @return {@code true} if a valid row was found with the given values,
* {@code false} if no row was found
*/
- public boolean findFirstRowByEntry(Object... entryValues)
+ public boolean findFirstRowByEntry(Object... entryValues)
throws IOException;
/**
* Moves to the first row (as defined by the cursor) where the index entries
- * are >= the given values. If a an exception is thrown, the cursor is
+ * are &gt;= the given values. If a an exception is thrown, the cursor is
* restored to its previous state.
*
* @param entryValues the column values for the index's columns.
*/
- public void findClosestRowByEntry(Object... entryValues)
+ public void findClosestRowByEntry(Object... entryValues)
throws IOException;
/**
* Returns {@code true} if the current row matches the given index entries.
- *
+ *
* @param entryValues the column values for the index's columns.
*/
- public boolean currentRowMatchesEntry(Object... entryValues)
+ public boolean currentRowMatchesEntry(Object... entryValues)
throws IOException;
/**
* Convenience method for constructing a new EntryIterableBuilder for this
* cursor. An EntryIterableBuilder provides a variety of options for more
* flexible iteration based on a specific index entry.
- *
+ *
* @param entryValues the column values for the index's columns.
*/
public EntryIterableBuilder newEntryIterable(Object... entryValues);
diff --git a/src/main/java/com/healthmarketscience/jackcess/InvalidValueException.java b/src/main/java/com/healthmarketscience/jackcess/InvalidValueException.java
index adffc0f..2e161d2 100644
--- a/src/main/java/com/healthmarketscience/jackcess/InvalidValueException.java
+++ b/src/main/java/com/healthmarketscience/jackcess/InvalidValueException.java
@@ -29,4 +29,8 @@ public class InvalidValueException extends JackcessException
public InvalidValueException(String msg) {
super(msg);
}
+
+ public InvalidValueException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/PropertyMap.java b/src/main/java/com/healthmarketscience/jackcess/PropertyMap.java
index 516a098..c8c8703 100644
--- a/src/main/java/com/healthmarketscience/jackcess/PropertyMap.java
+++ b/src/main/java/com/healthmarketscience/jackcess/PropertyMap.java
@@ -80,7 +80,7 @@ public interface PropertyMap extends Iterable<PropertyMap.Property>
* determine the type of the property based on the name and value (the
* property names listed above have their types builtin, otherwise the type
* of the value is used).
- * <p/>
+ * <br>
* Note, this change will not be persisted until the {@link #save} method
* has been called.
*
@@ -92,7 +92,7 @@ public interface PropertyMap extends Iterable<PropertyMap.Property>
/**
* Creates a new (or updates an existing) property in the map.
- * <p/>
+ * <br>
* Note, this change will not be persisted until the {@link #save} method
* has been called.
*
@@ -102,7 +102,7 @@ public interface PropertyMap extends Iterable<PropertyMap.Property>
/**
* Creates a new (or updates an existing) property in the map.
- * <p/>
+ * <br>
* Note, this change will not be persisted until the {@link #save} method
* has been called.
*
@@ -151,7 +151,7 @@ public interface PropertyMap extends Iterable<PropertyMap.Property>
/**
* Sets the new value for this property.
- * <p/>
+ * <br>
* Note, this change will not be persisted until the {@link
* PropertyMap#save} method has been called.
*/
diff --git a/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java b/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java
index 3ca3e85..d183de0 100644
--- a/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java
+++ b/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java
@@ -32,7 +32,7 @@ import com.healthmarketscience.jackcess.impl.RelationshipImpl;
* if integrity enforcement is enabled, there must already be a unique index
* on the "from" Table for the relevant columns (same requirement as MS
* Access).
- * <p/>
+ * <br>
* Example:
* <pre>
* Relationship rel = new RelationshipBuilder("FromTable", "ToTable")
diff --git a/src/main/java/com/healthmarketscience/jackcess/Row.java b/src/main/java/com/healthmarketscience/jackcess/Row.java
index a599c20..8fcaf67 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Row.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Row.java
@@ -20,13 +20,14 @@ import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.math.BigDecimal;
+import java.time.LocalDateTime;
import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
import com.healthmarketscience.jackcess.util.OleBlob;
/**
- * A row of data as column name->value pairs. Values are strongly typed, and
+ * A row of data as column name-&gt;value pairs. Values are strongly typed, and
* column names are case sensitive.
*
* @author James Ahlborn
@@ -35,7 +36,7 @@ import com.healthmarketscience.jackcess.util.OleBlob;
public interface Row extends Map<String,Object>
{
/**
- * @return the id of this row
+ * @return the id of this row
*/
public RowId getId();
@@ -90,11 +91,25 @@ public interface Row extends Map<String,Object>
/**
* Convenience method which gets the value for the row with the given name,
* casting it to a Date (DataType SHORT_DATE_TIME).
+ * @deprecated this is only valid for Database instances configured for the
+ * legacy {@link DateTimeType#DATE}. Prefer using
+ * {@link DateTimeType#LOCAL_DATE_TIME} and the corresponding
+ * {@link #getLocalDateTime} method. Using Date is being phased
+ * out and will eventually be removed.
*/
+ @Deprecated
public Date getDate(String name);
/**
* Convenience method which gets the value for the row with the given name,
+ * casting it to a LocalDateTime (DataType SHORT_DATE_TIME). This method
+ * will only work for Database instances configured for
+ * {@link DateTimeType#LOCAL_DATE_TIME}.
+ */
+ public LocalDateTime getLocalDateTime(String name);
+
+ /**
+ * Convenience method which gets the value for the row with the given name,
* casting it to a byte[] (DataTypes BINARY, OLE).
*/
public byte[] getBytes(String name);
@@ -108,7 +123,7 @@ public interface Row extends Map<String,Object>
/**
* Convenience method which gets the value for the row with the given name,
* converting it to an {@link OleBlob} (DataTypes OLE).
- * </p>
+ * <br>
* Note, <i>the OleBlob should be closed after use</i>.
*/
public OleBlob getBlob(String name) throws IOException;
diff --git a/src/main/java/com/healthmarketscience/jackcess/Table.java b/src/main/java/com/healthmarketscience/jackcess/Table.java
index bbb6885..496f2ea 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Table.java
@@ -33,7 +33,7 @@ import com.healthmarketscience.jackcess.util.ErrorHandler;
* {@link TableBuilder}. The {@link com.healthmarketscience.jackcess.util.Joiner} utility can be used to traverse
* table relationships (e.g. find rows in another table based on a foreign-key
* relationship).
- * <p/>
+ * <br>
* A Table instance is not thread-safe (see {@link Database} for more
* thread-safety details).
*
@@ -160,14 +160,14 @@ public interface Table extends Iterable<Row>
public Index getForeignKeyIndex(Table otherTable);
/**
- * Converts a map of columnName -> columnValue to an array of row values
+ * Converts a map of columnName -&gt; columnValue to an array of row values
* appropriate for a call to {@link #addRow(Object...)}.
* @usage _general_method_
*/
public Object[] asRow(Map<String,?> rowMap);
/**
- * Converts a map of columnName -> columnValue to an array of row values
+ * Converts a map of columnName -&gt; columnValue to an array of row values
* appropriate for a call to {@link Cursor#updateCurrentRow(Object...)}.
* @usage _general_method_
*/
@@ -203,7 +203,7 @@ public interface Table extends Iterable<Row>
/**
* Calls {@link #asRow} on the given row map and passes the result to {@link
* #addRow}.
- * <p/>
+ * <br>
* Note, if this table has an auto-number column, the value generated will be
* put back into the given row map.
* @return the given row map, which will contain any autonumbers generated
@@ -242,7 +242,7 @@ public interface Table extends Iterable<Row>
/**
* Calls {@link #asRow} on the given row maps and passes the results to
* {@link #addRows}.
- * <p/>
+ * <br>
* Note, if this table has an auto-number column, the values generated will
* be put back into the appropriate row maps.
* <p>
@@ -278,7 +278,7 @@ public interface Table extends Iterable<Row>
* Iterator which will iterate through all the rows of this table. Use of
* the Iterator follows the same restrictions as a call to
* {@link #getNextRow}.
- * <p/>
+ * <br>
* For more advanced iteration, use the {@link #getDefaultCursor default
* cursor} directly.
* @throws RuntimeIOException if an IOException is thrown by one of the
@@ -296,7 +296,7 @@ public interface Table extends Iterable<Row>
public void reset();
/**
- * @return The next row in this table (Column name -> Column value) (uses
+ * @return The next row in this table (Column name -&gt; Column value) (uses
* the {@link #getDefaultCursor default cursor})
* @usage _general_method_
*/
diff --git a/src/main/java/com/healthmarketscience/jackcess/TableBuilder.java b/src/main/java/com/healthmarketscience/jackcess/TableBuilder.java
index 31aa3a0..eaf6b1a 100644
--- a/src/main/java/com/healthmarketscience/jackcess/TableBuilder.java
+++ b/src/main/java/com/healthmarketscience/jackcess/TableBuilder.java
@@ -32,7 +32,7 @@ import com.healthmarketscience.jackcess.impl.TableCreator;
/**
* Builder style class for constructing a {@link Table}.
- * <p/>
+ * <br>
* Example:
* <pre>
* Table table = new TableBuilder("Test")
diff --git a/src/main/java/com/healthmarketscience/jackcess/complex/Attachment.java b/src/main/java/com/healthmarketscience/jackcess/complex/Attachment.java
index d35559e..0047719 100644
--- a/src/main/java/com/healthmarketscience/jackcess/complex/Attachment.java
+++ b/src/main/java/com/healthmarketscience/jackcess/complex/Attachment.java
@@ -17,14 +17,16 @@ limitations under the License.
package com.healthmarketscience.jackcess.complex;
import java.io.IOException;
+import java.time.LocalDateTime;
import java.util.Date;
+import com.healthmarketscience.jackcess.DateTimeType;
/**
* Complex value corresponding to an attachment.
*
* @author James Ahlborn
*/
-public interface Attachment extends ComplexValue
+public interface Attachment extends ComplexValue
{
public byte[] getFileData() throws IOException;
@@ -37,20 +39,34 @@ public interface Attachment extends ComplexValue
public String getFileName();
public void setFileName(String fileName);
-
+
public String getFileUrl();
public void setFileUrl(String fileUrl);
-
+
public String getFileType();
public void setFileType(String fileType);
-
+
+ /**
+ * @deprecated see {@link DateTimeType} for details
+ */
+ @Deprecated
public Date getFileTimeStamp();
+ /**
+ * @deprecated see {@link DateTimeType} for details
+ */
+ @Deprecated
public void setFileTimeStamp(Date fileTimeStamp);
-
+
+ public LocalDateTime getFileLocalTimeStamp();
+
+ public void setFileLocalTimeStamp(LocalDateTime fileTimeStamp);
+
+ public Object getFileTimeStampObject();
+
public Integer getFileFlags();
- public void setFileFlags(Integer fileFlags);
+ public void setFileFlags(Integer fileFlags);
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/complex/ComplexValueForeignKey.java b/src/main/java/com/healthmarketscience/jackcess/complex/ComplexValueForeignKey.java
index 150dd07..0e0bc13 100644
--- a/src/main/java/com/healthmarketscience/jackcess/complex/ComplexValueForeignKey.java
+++ b/src/main/java/com/healthmarketscience/jackcess/complex/ComplexValueForeignKey.java
@@ -18,10 +18,13 @@ package com.healthmarketscience.jackcess.complex;
import java.io.IOException;
import java.io.ObjectStreamException;
+import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
+
import com.healthmarketscience.jackcess.Column;
+import com.healthmarketscience.jackcess.DateTimeType;
/**
@@ -40,33 +43,33 @@ import com.healthmarketscience.jackcess.Column;
*/
public abstract class ComplexValueForeignKey extends Number
{
- private static final long serialVersionUID = 20130319L;
+ private static final long serialVersionUID = 20130319L;
@Override
public byte byteValue() {
return (byte)get();
}
-
+
@Override
public short shortValue() {
return (short)get();
}
-
+
@Override
public int intValue() {
return get();
}
-
+
@Override
public long longValue() {
return get();
}
-
+
@Override
public float floatValue() {
return get();
}
-
+
@Override
public double doubleValue() {
return get();
@@ -78,12 +81,12 @@ public abstract class ComplexValueForeignKey extends Number
// of jackcess)
return Integer.valueOf(get());
}
-
+
@Override
public int hashCode() {
return get();
}
-
+
@Override
public boolean equals(Object o) {
return ((this == o) ||
@@ -94,7 +97,7 @@ public abstract class ComplexValueForeignKey extends Number
@Override
public String toString() {
return String.valueOf(get());
- }
+ }
public abstract int get();
@@ -122,25 +125,50 @@ public abstract class ComplexValueForeignKey extends Number
public abstract Version addVersion(String value)
throws IOException;
+ /**
+ * @deprecated see {@link DateTimeType} for details
+ */
+ @Deprecated
public abstract Version addVersion(String value, Date modifiedDate)
throws IOException;
+ public abstract Version addVersion(String value, LocalDateTime modifiedDate)
+ throws IOException;
+
public abstract Attachment addAttachment(byte[] data)
throws IOException;
+ /**
+ * @deprecated see {@link DateTimeType} for details
+ */
+ @Deprecated
public abstract Attachment addAttachment(
String url, String name, String type, byte[] data,
Date timeStamp, Integer flags)
throws IOException;
+ public abstract Attachment addAttachment(
+ String url, String name, String type, byte[] data,
+ LocalDateTime timeStamp, Integer flags)
+ throws IOException;
+
public abstract Attachment addEncodedAttachment(byte[] encodedData)
throws IOException;
+ /**
+ * @deprecated see {@link DateTimeType} for details
+ */
+ @Deprecated
public abstract Attachment addEncodedAttachment(
String url, String name, String type, byte[] encodedData,
Date timeStamp, Integer flags)
throws IOException;
+ public abstract Attachment addEncodedAttachment(
+ String url, String name, String type, byte[] encodedData,
+ LocalDateTime timeStamp, Integer flags)
+ throws IOException;
+
public abstract Attachment updateAttachment(Attachment attachment)
throws IOException;
diff --git a/src/main/java/com/healthmarketscience/jackcess/complex/Version.java b/src/main/java/com/healthmarketscience/jackcess/complex/Version.java
index a1ace1b..374e047 100644
--- a/src/main/java/com/healthmarketscience/jackcess/complex/Version.java
+++ b/src/main/java/com/healthmarketscience/jackcess/complex/Version.java
@@ -16,7 +16,9 @@ limitations under the License.
package com.healthmarketscience.jackcess.complex;
+import java.time.LocalDateTime;
import java.util.Date;
+import com.healthmarketscience.jackcess.DateTimeType;
/**
* Complex value corresponding to a version of a memo column.
@@ -27,5 +29,13 @@ public interface Version extends ComplexValue, Comparable<Version>
{
public String getValue();
+ /**
+ * @deprecated see {@link DateTimeType} for details
+ */
+ @Deprecated
public Date getModifiedDate();
+
+ public LocalDateTime getModifiedLocalDate();
+
+ public Object getModifiedDateObject();
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/Identifier.java b/src/main/java/com/healthmarketscience/jackcess/expr/Identifier.java
index 45db1ad..709a7cd 100644
--- a/src/main/java/com/healthmarketscience/jackcess/expr/Identifier.java
+++ b/src/main/java/com/healthmarketscience/jackcess/expr/Identifier.java
@@ -16,7 +16,8 @@ limitations under the License.
package com.healthmarketscience.jackcess.expr;
-import org.apache.commons.lang.ObjectUtils;
+import java.util.Objects;
+
/**
* identifies a database entity (e.g. the name of a database field). An
@@ -71,9 +72,9 @@ public class Identifier
Identifier oi = (Identifier)o;
- return (ObjectUtils.equals(_objectName, oi._objectName) &&
- ObjectUtils.equals(_collectionName, oi._collectionName) &&
- ObjectUtils.equals(_propertyName, oi._propertyName));
+ return (Objects.equals(_objectName, oi._objectName) &&
+ Objects.equals(_collectionName, oi._collectionName) &&
+ Objects.equals(_propertyName, oi._propertyName));
}
@Override
diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/LocaleContext.java b/src/main/java/com/healthmarketscience/jackcess/expr/LocaleContext.java
index a90a80b..7b7a306 100644
--- a/src/main/java/com/healthmarketscience/jackcess/expr/LocaleContext.java
+++ b/src/main/java/com/healthmarketscience/jackcess/expr/LocaleContext.java
@@ -17,8 +17,8 @@ limitations under the License.
package com.healthmarketscience.jackcess.expr;
import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
/**
* LocaleContext encapsulates all shared localization state for expression
@@ -35,16 +35,15 @@ public interface LocaleContext
public TemporalConfig getTemporalConfig();
/**
- * @return an appropriately configured (i.e. TimeZone and other date/time
- * flags) SimpleDateFormat for the given format.
+ * @return an appropriately configured (i.e. locale) DateTimeFormatter for
+ * the given format.
*/
- public SimpleDateFormat createDateFormat(String formatStr);
+ public DateTimeFormatter createDateFormatter(String formatStr);
/**
- * @return an appropriately configured (i.e. TimeZone and other date/time
- * flags) Calendar.
+ * @return the currently configured ZoneId
*/
- public Calendar getCalendar();
+ public ZoneId getZoneId();
/**
* @return the currently configured NumericConfig (from the
diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/TemporalConfig.java b/src/main/java/com/healthmarketscience/jackcess/expr/TemporalConfig.java
index db7806f..cfe08e1 100644
--- a/src/main/java/com/healthmarketscience/jackcess/expr/TemporalConfig.java
+++ b/src/main/java/com/healthmarketscience/jackcess/expr/TemporalConfig.java
@@ -30,19 +30,18 @@ import java.util.Locale;
*/
public class TemporalConfig
{
- public static final String US_DATE_FORMAT = "M/d/yyyy";
- public static final String US_DATE_IMPLICIT_YEAR_FORMAT = "M/d";
+ public static final String US_DATE_FORMAT = "M/d[/uuuu]";
public static final String US_TIME_FORMAT_12_FORMAT = "h:mm:ss a";
public static final String US_TIME_FORMAT_24_FORMAT = "H:mm:ss";
- public static final String US_LONG_DATE_FORMAT = "EEEE, MMMM dd, yyyy";
+ public static final String US_LONG_DATE_FORMAT = "EEEE, MMMM dd, uuuu";
- public static final String MEDIUM_DATE_FORMAT = "dd-MMM-yy";
+ public static final String MEDIUM_DATE_FORMAT = "dd-MMM-uu";
public static final String MEDIUM_TIME_FORMAT = "hh:mm a";
public static final String SHORT_TIME_FORMAT = "HH:mm";
/** default implementation which is configured for the US locale */
public static final TemporalConfig US_TEMPORAL_CONFIG = new TemporalConfig(
- US_DATE_FORMAT, US_DATE_IMPLICIT_YEAR_FORMAT, US_LONG_DATE_FORMAT,
+ US_DATE_FORMAT, US_LONG_DATE_FORMAT,
US_TIME_FORMAT_12_FORMAT, US_TIME_FORMAT_24_FORMAT, '/', ':', Locale.US);
public enum Type {
@@ -133,8 +132,8 @@ public class TemporalConfig
}
}
+ private final Locale _locale;
private final String _dateFormat;
- private final String _dateImplicitYearFormat;
private final String _longDateFormat;
private final String _timeFormat12;
private final String _timeFormat24;
@@ -142,16 +141,15 @@ public class TemporalConfig
private final char _timeSeparator;
private final String _dateTimeFormat12;
private final String _dateTimeFormat24;
- private final DateFormatSymbols _symbols;
+ private final String[] _amPmStrings;
/**
* Instantiates a new TemporalConfig with the given configuration. Note
* that the date/time format variants will be created by concatenating the
- * relevant date and time formats, separated by a single space, e.g. "<date>
- * <time>".
+ * relevant date and time formats, separated by a single space,
+ * e.g. "&lt;date&gt; &lt;time&gt;".
*
* @param dateFormat the date (no time) format
- * @param dateImplicitYearFormat the date (no time) with no year format
* @param timeFormat12 the 12 hour time format
* @param timeFormat24 the 24 hour time format
* @param dateSeparator the primary separator used to separate elements in
@@ -163,21 +161,26 @@ public class TemporalConfig
* string. This value should differ from the
* dateSeparator.
*/
- public TemporalConfig(String dateFormat, String dateImplicitYearFormat,
- String longDateFormat,
+ public TemporalConfig(String dateFormat, String longDateFormat,
String timeFormat12, String timeFormat24,
char dateSeparator, char timeSeparator, Locale locale)
{
+ _locale = locale;
_dateFormat = dateFormat;
- _dateImplicitYearFormat = dateImplicitYearFormat;
_longDateFormat = longDateFormat;
_timeFormat12 = timeFormat12;
_timeFormat24 = timeFormat24;
_dateSeparator = dateSeparator;
_timeSeparator = timeSeparator;
- _dateTimeFormat12 = _dateFormat + " " + _timeFormat12;
- _dateTimeFormat24 = _dateFormat + " " + _timeFormat24;
- _symbols = DateFormatSymbols.getInstance(locale);
+ _dateTimeFormat12 = toDateTimeFormat(_dateFormat, _timeFormat12);
+ _dateTimeFormat24 = toDateTimeFormat(_dateFormat, _timeFormat24);
+ // there doesn't seem to be a good/easy way to get this in new jave.time
+ // api, so just use old api
+ _amPmStrings = DateFormatSymbols.getInstance(locale).getAmPmStrings();
+ }
+
+ public Locale getLocale() {
+ return _locale;
}
public String getDateFormat() {
@@ -252,24 +255,8 @@ public class TemporalConfig
}
}
- public String getImplicitYearDateTimeFormat(Type type) {
- switch(type) {
- case DATE:
- return _dateImplicitYearFormat;
- case DATE_TIME:
- return toDateTimeFormat(_dateImplicitYearFormat, getDefaultTimeFormat());
- case DATE_TIME_12:
- return toDateTimeFormat(_dateImplicitYearFormat, getTimeFormat12());
- case DATE_TIME_24:
- return toDateTimeFormat(_dateImplicitYearFormat, getTimeFormat24());
- default:
- throw new IllegalArgumentException(
- "the given format does not include a date " + type);
- }
- }
-
- public DateFormatSymbols getDateFormatSymbols() {
- return _symbols;
+ public String[] getAmPmStrings() {
+ return _amPmStrings;
}
private static String toDateTimeFormat(String dateFormat, String timeFormat) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/Value.java b/src/main/java/com/healthmarketscience/jackcess/expr/Value.java
index 118215e..ded758b 100644
--- a/src/main/java/com/healthmarketscience/jackcess/expr/Value.java
+++ b/src/main/java/com/healthmarketscience/jackcess/expr/Value.java
@@ -17,7 +17,7 @@ limitations under the License.
package com.healthmarketscience.jackcess.expr;
import java.math.BigDecimal;
-import java.util.Date;
+import java.time.LocalDateTime;
/**
* Wrapper for a typed primitive value used within the expression evaluation
@@ -97,9 +97,9 @@ public interface Value
public String getAsString(LocaleContext ctx);
/**
- * @return this primitive value converted to a Date
+ * @return this primitive value converted to a LocalDateTime
*/
- public Date getAsDateTime(LocaleContext ctx);
+ public LocalDateTime getAsLocalDateTime(LocaleContext ctx);
/**
* Since date/time values have different types, it may be more convenient to
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 6e4d5ab..99b9162 100644
--- a/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java
+++ b/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java
@@ -20,13 +20,13 @@ limitations under the License.
* but can be globally enabled via the system property
* "com.healthmarketscience.jackcess.enableExpressionEvaluation" or
* selectively enabled on a per database basis using {@link com.healthmarketscience.jackcess.Database#setEvaluateExpressions(Boolean)}.
- * <p/>
+ * <br>
* The expression evaluation engine implementation does its best to follow all
* the warts and idiosyncracies of Access expression evaluation (both those
* that are documented as well as those discovered through experimentation).
* These include such things as value conversions, "Null" handling, rounding
* rules, and implicit interpretations of expression in certain contexts.
- * <p/>
+ * <br>
* Expressions can be used in a number of different places within an Access
* database. When enabled, Jackcess supports the following usage:
* <ul>
@@ -50,14 +50,14 @@ limitations under the License.
* record validation rules will be run for the entire record before
* update. Failures are handled in a similar manner.</li>
* </ul>
- * <p/>
+ * <br>
* <h2>Supporting Classes</h2>
- * <p/>
+ * <br>
* The classes in this package make up the public api for expression handling
* in Jackcess. They generally fall into two categories:
- * <p/>
+ * <br>
* <h3>General Use Classes</h3>
- * <p/>
+ * <br>
* <ul>
* <li>{@link com.healthmarketscience.jackcess.expr.EvalConfig} allows for customization of the expression
* evaluation context for a given {@link com.healthmarketscience.jackcess.Database} instance.</li>
@@ -72,9 +72,9 @@ limitations under the License.
* <li>{@link com.healthmarketscience.jackcess.expr.ParseException} wrapper exception thrown for failures which
* occur during expression parsing.</li>
* </ul>
- * <p/>
+ * <br>
* <h3>Advanced Use Classes</h3>
- * <p/>
+ * <br>
* <ul>
* <li>{@link com.healthmarketscience.jackcess.expr.EvalContext} encapsulates all shared state for expression
* parsing and evaluation.</li>
@@ -86,9 +86,9 @@ limitations under the License.
* database field).</li>
* <li>{@link com.healthmarketscience.jackcess.expr.Value} represents a typed primitive value.</li>
* </ul>
- * <p/>
+ * <br>
* <h2>Function Support</h2>
- * <p/>
+ * <br>
* Jackcess supports many of the standard Access functions. The following
* tables list the (hopefully) current status of support built into Jackcess.
*
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java
index 0e52fa4..28785d1 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java
@@ -19,8 +19,9 @@ package com.healthmarketscience.jackcess.impl;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Date;
import java.util.EnumMap;
@@ -28,6 +29,7 @@ import java.util.Map;
import javax.script.Bindings;
import com.healthmarketscience.jackcess.DataType;
+import com.healthmarketscience.jackcess.DateTimeType;
import com.healthmarketscience.jackcess.JackcessException;
import com.healthmarketscience.jackcess.expr.EvalContext;
import com.healthmarketscience.jackcess.expr.EvalException;
@@ -82,12 +84,12 @@ public abstract class BaseEvalContext implements EvalContext
return _dbCtx.getTemporalConfig();
}
- public SimpleDateFormat createDateFormat(String formatStr) {
- return _dbCtx.createDateFormat(formatStr);
+ public DateTimeFormatter createDateFormatter(String formatStr) {
+ return _dbCtx.createDateFormatter(formatStr);
}
- public Calendar getCalendar() {
- return _dbCtx.getCalendar();
+ public ZoneId getZoneId() {
+ return _dbCtx.getZoneId();
}
public NumericConfig getNumericConfig() {
@@ -146,7 +148,10 @@ public abstract class BaseEvalContext implements EvalContext
protected Value toValue(Object val, DataType dType) {
try {
- val = ColumnImpl.toInternalValue(dType, val, getDatabase());
+ // expression engine always uses LocalDateTime, so force that date/time
+ // type
+ val = ColumnImpl.toInternalValue(dType, val, getDatabase(),
+ ColumnImpl.LDT_DATE_TIME_FACTORY);
if(val == null) {
return ValueSupport.NULL_VAL;
}
@@ -158,7 +163,7 @@ public abstract class BaseEvalContext implements EvalContext
case DATE:
case TIME:
case DATE_TIME:
- return ValueSupport.toValue(vType, (Date)val);
+ return ValueSupport.toValue(vType, (LocalDateTime)val);
case LONG:
Integer i = ((val instanceof Integer) ? (Integer)val :
((Number)val).intValue());
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java b/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java
index fb76ad7..141f47f 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java
@@ -26,7 +26,7 @@ import com.healthmarketscience.jackcess.InvalidValueException;
/**
* Utility code for dealing with calculated columns.
- * <p/>
+ * <br>
* These are the currently possible calculated types: FLOAT, DOUBLE, INT,
* LONG, BIG_INT, GUID, SHORT_DATE_TIME, MONEY, BOOLEAN, NUMERIC, TEXT, MEMO.
*
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
index 0016687..6ab88ab 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
@@ -32,11 +32,22 @@ import java.nio.charset.Charset;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalQueries;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
+import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -44,6 +55,7 @@ import java.util.regex.Pattern;
import com.healthmarketscience.jackcess.Column;
import com.healthmarketscience.jackcess.ColumnBuilder;
import com.healthmarketscience.jackcess.DataType;
+import com.healthmarketscience.jackcess.DateTimeType;
import com.healthmarketscience.jackcess.InvalidValueException;
import com.healthmarketscience.jackcess.PropertyMap;
import com.healthmarketscience.jackcess.Table;
@@ -52,9 +64,10 @@ import com.healthmarketscience.jackcess.complex.ComplexValue;
import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
import com.healthmarketscience.jackcess.expr.Identifier;
import com.healthmarketscience.jackcess.impl.complex.ComplexValueForeignKeyImpl;
+import com.healthmarketscience.jackcess.impl.expr.NumberFormatter;
import com.healthmarketscience.jackcess.util.ColumnValidator;
import com.healthmarketscience.jackcess.util.SimpleColumnValidator;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -63,7 +76,8 @@ import org.apache.commons.logging.LogFactory;
* @author Tim McCune
* @usage _intermediate_class_
*/
-public class ColumnImpl implements Column, Comparable<ColumnImpl> {
+public class ColumnImpl implements Column, Comparable<ColumnImpl>, ZoneContext
+{
protected static final Log LOG = LogFactory.getLog(ColumnImpl.class);
@@ -79,8 +93,11 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
/**
* Access stores numeric dates in days. Java stores them in milliseconds.
*/
- private static final long MILLISECONDS_PER_DAY =
- (24L * 60L * 60L * 1000L);
+ private static final long MILLISECONDS_PER_DAY = (24L * 60L * 60L * 1000L);
+ private static final long SECONDS_PER_DAY = (24L * 60L * 60L);
+ private static final long NANOS_PER_SECOND = 1_000_000_000L;
+ private static final long NANOS_PER_MILLI = 1_000_000L;
+ private static final long MILLIS_PER_SECOND = 1000L;
/**
* Access starts counting dates at Dec 30, 1899 (note, this strange date
@@ -91,6 +108,16 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
static final long MILLIS_BETWEEN_EPOCH_AND_1900 =
25569L * MILLISECONDS_PER_DAY;
+ public static final LocalDate BASE_LD = LocalDate.of(1899, 12, 30);
+ public static final LocalTime BASE_LT = LocalTime.of(0, 0);
+ public static final LocalDateTime BASE_LDT = LocalDateTime.of(BASE_LD, BASE_LT);
+
+ private static final DateTimeFactory DEF_DATE_TIME_FACTORY =
+ new DefaultDateTimeFactory();
+
+ static final DateTimeFactory LDT_DATE_TIME_FACTORY =
+ new LDTDateTimeFactory();
+
/**
* mask for the fixed len bit
* @usage _advanced_field_
@@ -332,10 +359,12 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
// base does nothing
}
+ @Override
public TableImpl getTable() {
return _table;
}
+ @Override
public DatabaseImpl getDatabase() {
return getTable().getDatabase();
}
@@ -354,14 +383,17 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return getDatabase().getPageChannel();
}
+ @Override
public String getName() {
return _name;
}
+ @Override
public boolean isVariableLength() {
return _variableLength;
}
+ @Override
public boolean isAutoNumber() {
return _autoNumber;
}
@@ -373,6 +405,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return _columnNumber;
}
+ @Override
public int getColumnIndex() {
return _columnIndex;
}
@@ -391,22 +424,27 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return _displayIndex;
}
+ @Override
public DataType getType() {
return _type;
}
+ @Override
public int getSQLType() throws SQLException {
return _type.getSQLType();
}
+ @Override
public boolean isCompressedUnicode() {
return false;
}
+ @Override
public byte getPrecision() {
return (byte)getType().getDefaultPrecision();
}
+ @Override
public byte getScale() {
return (byte)getType().getDefaultScale();
}
@@ -425,14 +463,17 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return 0;
}
+ @Override
public short getLength() {
return _columnLength;
}
+ @Override
public short getLengthInUnits() {
return (short)getType().toUnitSize(getLength());
}
+ @Override
public boolean isCalculated() {
return _calculated;
}
@@ -455,14 +496,26 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return getDatabase().getCharset();
}
- protected Calendar getCalendar() {
- return getDatabase().getCalendar();
+ @Override
+ public TimeZone getTimeZone() {
+ return getDatabase().getTimeZone();
+ }
+
+ @Override
+ public ZoneId getZoneId() {
+ return getDatabase().getZoneId();
+ }
+
+ protected DateTimeFactory getDateTimeFactory() {
+ return getDatabase().getDateTimeFactory();
}
+ @Override
public boolean isAppendOnly() {
return (getVersionHistoryColumn() != null);
}
+ @Override
public ColumnImpl getVersionHistoryColumn() {
return null;
}
@@ -482,10 +535,12 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
throw new UnsupportedOperationException();
}
+ @Override
public boolean isHyperlink() {
return false;
}
+ @Override
public ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
return null;
}
@@ -582,12 +637,14 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
reloadPropertiesValidators();
}
+ @Override
public ColumnValidator getColumnValidator() {
// unwrap any "internal" validator
return ((_validator instanceof InternalColumnValidator) ?
((InternalColumnValidator)_validator).getExternal() : _validator);
}
+ @Override
public void setColumnValidator(ColumnValidator newValidator) {
if(isAutoNumber()) {
@@ -648,6 +705,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return _autoNumberGenerator;
}
+ @Override
public PropertyMap getProperties() throws IOException {
if(_props == null) {
_props = getTable().getPropertyMaps().get(getName());
@@ -655,20 +713,24 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return _props;
}
+ @Override
public Object setRowValue(Object[] rowArray, Object value) {
rowArray[_columnIndex] = value;
return value;
}
+ @Override
public Object setRowValue(Map<String,Object> rowMap, Object value) {
rowMap.put(_name, value);
return value;
}
+ @Override
public Object getRowValue(Object[] rowArray) {
return rowArray[_columnIndex];
}
+ @Override
public Object getRowValue(Map<String,?> rowMap) {
return rowMap.get(_name);
}
@@ -887,45 +949,25 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
/**
* Decodes a date value.
*/
- private Date readDateValue(ByteBuffer buffer)
- {
- // seems access stores dates in the local timezone. guess you just hope
- // you read it in the same timezone in which it was written!
+ private Object readDateValue(ByteBuffer buffer) {
long dateBits = buffer.getLong();
- long time = fromDateDouble(Double.longBitsToDouble(dateBits));
- return new DateExt(time, dateBits);
+ return getDateTimeFactory().fromDateBits(this, dateBits);
}
/**
* Returns a java long time value converted from an access date double.
* @usage _advanced_method_
*/
- public long fromDateDouble(double value)
- {
- return fromDateDouble(value, getCalendar());
+ public long fromDateDouble(double value) {
+ return fromDateDouble(value, getTimeZone());
}
- /**
- * Returns a java long time value converted from an access date double.
- * @usage _advanced_method_
- */
- public static long fromDateDouble(double value, DatabaseImpl db)
- {
- return fromDateDouble(value, db.getCalendar());
- }
-
- /**
- * Returns a java long time value converted from an access date double.
- * @usage _advanced_method_
- */
- public static long fromDateDouble(double value, Calendar c)
- {
+ private static long fromDateDouble(double value, TimeZone tz) {
long localTime = fromLocalDateDouble(value);
- return localTime - getFromLocalTimeZoneOffset(localTime, c);
+ return localTime - getFromLocalTimeZoneOffset(localTime, tz);
}
- static long fromLocalDateDouble(double value)
- {
+ static long fromLocalDateDouble(double value) {
long datePart = ((long)value) * MILLISECONDS_PER_DAY;
// the fractional part of the double represents the time. it is always
@@ -933,29 +975,49 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
// _not_ the time distance from zero (as one would expect with "normal"
// numbers). therefore, we need to do a little number logic to convert
// the absolute time fraction into a normal distance from zero number.
- long timePart = Math.round((Math.abs(value) % 1.0) *
- (double)MILLISECONDS_PER_DAY);
+ long timePart = Math.round((Math.abs(value) % 1.0d) *
+ MILLISECONDS_PER_DAY);
long time = datePart + timePart;
- time -= MILLIS_BETWEEN_EPOCH_AND_1900;
- return time;
+ return time - MILLIS_BETWEEN_EPOCH_AND_1900;
+ }
+
+ public static LocalDateTime ldtFromLocalDateDouble(double value) {
+ Duration dateTimeOffset = durationFromLocalDateDouble(value);
+ return BASE_LDT.plus(dateTimeOffset);
+ }
+
+ private static Duration durationFromLocalDateDouble(double value) {
+ long dateSeconds = ((long)value) * SECONDS_PER_DAY;
+
+ // the fractional part of the double represents the time. it is always
+ // a positive fraction of the day (even if the double is negative),
+ // _not_ the time distance from zero (as one would expect with "normal"
+ // numbers). therefore, we need to do a little number logic to convert
+ // the absolute time fraction into a normal distance from zero number.
+
+ double secondsDouble = (Math.abs(value) % 1.0d) * SECONDS_PER_DAY;
+ long timeSeconds = (long)secondsDouble;
+ long timeMillis = (long)(roundToMillis(secondsDouble % 1.0d) *
+ MILLIS_PER_SECOND);
+
+ return Duration.ofSeconds(dateSeconds + timeSeconds,
+ timeMillis * NANOS_PER_MILLI);
}
/**
* Writes a date value.
*/
private void writeDateValue(ByteBuffer buffer, Object value)
+ throws InvalidValueException
{
if(value == null) {
buffer.putDouble(0d);
} else if(value instanceof DateExt) {
-
// this is a Date value previously read from readDateValue(). use the
// original bits to store the value so we don't lose any precision
buffer.putLong(((DateExt)value).getDateBits());
-
} else {
-
buffer.putDouble(toDateDouble(value));
}
}
@@ -966,36 +1028,86 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
* @usage _advanced_method_
*/
public double toDateDouble(Object value)
+ throws InvalidValueException
{
- return toDateDouble(value, getCalendar());
+ try {
+ return toDateDouble(value, this);
+ } catch(IllegalArgumentException iae) {
+ throw new InvalidValueException(withErrorContext(iae.getMessage()), iae);
+ }
}
/**
- * Returns an access date double converted from a java Date/Calendar/Number
- * time value.
+ * Returns an access date double converted from a java
+ * Date/Calendar/Number/Temporal time value.
* @usage _advanced_method_
*/
- public static double toDateDouble(Object value, DatabaseImpl db)
+ private static double toDateDouble(Object value, ZoneContext zc)
{
- return toDateDouble(value, db.getCalendar());
- }
+ if(value instanceof TemporalAccessor) {
+ return toDateDouble(toLocalDateTime((TemporalAccessor)value, zc));
+ }
- /**
- * Returns an access date double converted from a java Date/Calendar/Number
- * time value.
- * @usage _advanced_method_
- */
- public static double toDateDouble(Object value, Calendar c)
- {
// seems access stores dates in the local timezone. guess you just
// hope you read it in the same timezone in which it was written!
long time = toDateLong(value);
- time += getToLocalTimeZoneOffset(time, c);
+ time += getToLocalTimeZoneOffset(time, zc.getTimeZone());
return toLocalDateDouble(time);
}
- static double toLocalDateDouble(long time)
- {
+ private static LocalDateTime toLocalDateTime(
+ TemporalAccessor value, ZoneContext zc) {
+
+ // handle some common Temporal types
+ if(value instanceof LocalDateTime) {
+ return (LocalDateTime)value;
+ }
+ if(value instanceof ZonedDateTime) {
+ // if the temporal value has a timezone, convert it to this db's timezone
+ return ((ZonedDateTime)value).withZoneSameInstant(
+ zc.getZoneId()).toLocalDateTime();
+ }
+ if(value instanceof Instant) {
+ return LocalDateTime.ofInstant((Instant)value, zc.getZoneId());
+ }
+ if(value instanceof LocalDate) {
+ return ((LocalDate)value).atTime(BASE_LT);
+ }
+ if(value instanceof LocalTime) {
+ return ((LocalTime)value).atDate(BASE_LD);
+ }
+
+ // generic handling for many other Temporal types
+ try {
+
+ LocalDate ld = value.query(TemporalQueries.localDate());
+ if(ld == null) {
+ ld = BASE_LD;
+ }
+ LocalTime lt = value.query(TemporalQueries.localTime());
+ if(lt == null) {
+ lt = BASE_LT;
+ }
+ ZoneId zone = value.query(TemporalQueries.zone());
+ if(zone != null) {
+ // the Temporal has a zone, see if it is the right zone. if not,
+ // adjust it
+ ZoneId zoneId = zc.getZoneId();
+ if(!zoneId.equals(zone)) {
+ return ZonedDateTime.of(ld, lt, zone).withZoneSameInstant(zoneId)
+ .toLocalDateTime();
+ }
+ }
+
+ return LocalDateTime.of(ld, lt);
+
+ } catch(DateTimeException | ArithmeticException e) {
+ throw new IllegalArgumentException(
+ "Unsupported temporal type " + value.getClass(), e);
+ }
+ }
+
+ static double toLocalDateDouble(long time) {
time += MILLIS_BETWEEN_EPOCH_AND_1900;
if(time < 0L) {
@@ -1009,11 +1121,63 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return time / (double)MILLISECONDS_PER_DAY;
}
+ public static double toDateDouble(LocalDateTime ldt) {
+ Duration dateTimeOffset = Duration.between(BASE_LDT, ldt);
+ return toLocalDateDouble(dateTimeOffset);
+ }
+
+ private static double toLocalDateDouble(Duration time) {
+ long dateTimeSeconds = time.getSeconds();
+ long timeSeconds = dateTimeSeconds % SECONDS_PER_DAY;
+ if(timeSeconds < 0) {
+ timeSeconds += SECONDS_PER_DAY;
+ }
+ long dateSeconds = dateTimeSeconds - timeSeconds;
+ long timeNanos = time.getNano();
+
+ // we have a difficult choice to make here between keeping a value which
+ // most accurately represents the bits saved and rounding to a value that
+ // would match what the user would expect too see. since we do a double
+ // to long conversion, we end up in a situation where the value might be
+ // 19.9999 seconds. access will display this as 20 seconds (access seems
+ // to only record times to second precision). if we return 19.9999, then
+ // when the value is written back out it will be exactly the same double
+ // (good), but will display as 19 seconds (bad because it looks wrong to
+ // the user). on the flip side, if we round, the value will display
+ // "correctly" to the user, but if the value is written back out, it will
+ // be a slightly different double value. this may not be a problem for
+ // most situations, but may result in incorrect index based lookups. in
+ // the old date time handling we use DateExt to store the original bits.
+ // in jdk8, we cannot extend LocalDateTime. for now, we will try
+ // returning the value rounded to milliseconds (technically still more
+ // precision than access uses but more likely to round trip to the same
+ // value).
+ double timeDouble = ((roundToMillis((double)timeNanos / NANOS_PER_SECOND) +
+ timeSeconds) / SECONDS_PER_DAY);
+
+ double dateDouble = ((double)dateSeconds / SECONDS_PER_DAY);
+
+ if(dateSeconds < 0) {
+ timeDouble = -timeDouble;
+ }
+
+ return dateDouble + timeDouble;
+ }
+
+ /**
+ * Rounds the given decimal to milliseconds (3 decimal places) using the
+ * standard access rounding mode.
+ */
+ private static double roundToMillis(double dbl) {
+ return ((dbl == 0d) ? dbl :
+ new BigDecimal(dbl).setScale(3, NumberFormatter.ROUND_MODE)
+ .doubleValue());
+ }
+
/**
* @return an appropriate Date long value for the given object
*/
- private static long toDateLong(Object value)
- {
+ private static long toDateLong(Object value) {
return ((value instanceof Date) ?
((Date)value).getTime() :
((value instanceof Calendar) ?
@@ -1025,24 +1189,19 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
* Gets the timezone offset from UTC to local time for the given time
* (including DST).
*/
- private static long getToLocalTimeZoneOffset(long time, Calendar c)
- {
- c.setTimeInMillis(time);
- return ((long)c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET));
+ private static long getToLocalTimeZoneOffset(long time, TimeZone tz) {
+ return tz.getOffset(time);
}
/**
* Gets the timezone offset from local time to UTC for the given time
* (including DST).
*/
- private static long getFromLocalTimeZoneOffset(long time, Calendar c)
- {
+ private static long getFromLocalTimeZoneOffset(long time, TimeZone tz) {
// getting from local time back to UTC is a little wonky (and not
- // guaranteed to get you back to where you started)
- c.setTimeInMillis(time);
- // apply the zone offset first to get us closer to the original time
- c.setTimeInMillis(time - c.get(Calendar.ZONE_OFFSET));
- return ((long)c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET));
+ // guaranteed to get you back to where you started). apply the zone
+ // offset first to get us closer to the original time
+ return tz.getOffset(time - tz.getRawOffset());
}
/**
@@ -1552,6 +1711,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
* Orders Columns by column number.
* @usage _general_method_
*/
+ @Override
public int compareTo(ColumnImpl other) {
if (_columnNumber > other.getColumnNumber()) {
return 1;
@@ -1607,6 +1767,8 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return ((Boolean)value) ? BigDecimal.valueOf(-1) : BigDecimal.ZERO;
} else if(value instanceof Date) {
return new BigDecimal(toDateDouble(value, db));
+ } else if(value instanceof LocalDateTime) {
+ return new BigDecimal(toDateDouble((LocalDateTime)value));
}
return new BigDecimal(value.toString());
}
@@ -1637,6 +1799,8 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return ((Boolean)value) ? -1 : 0;
} else if(value instanceof Date) {
return toDateDouble(value, db);
+ } else if(value instanceof LocalDateTime) {
+ return toDateDouble((LocalDateTime)value);
}
return Double.valueOf(value.toString());
}
@@ -1972,6 +2136,14 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
DatabaseImpl db)
throws IOException
{
+ return toInternalValue(dataType, value, db, null);
+ }
+
+ static Object toInternalValue(DataType dataType, Object value,
+ DatabaseImpl db,
+ ColumnImpl.DateTimeFactory factory)
+ throws IOException
+ {
if(value == null) {
return null;
}
@@ -1996,8 +2168,10 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return ((value instanceof Double) ? value :
toNumber(value, db).doubleValue());
case SHORT_DATE_TIME:
- return ((value instanceof Date) ? value :
- new Date(toDateLong(value)));
+ if(factory == null) {
+ factory = db.getDateTimeFactory();
+ }
+ return factory.toInternalValue(db, value);
case TEXT:
case MEMO:
case GUID:
@@ -2017,6 +2191,11 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
}
}
+ static DateTimeFactory getDateTimeFactory(DateTimeType type) {
+ return ((type == DateTimeType.LOCAL_DATE_TIME) ?
+ LDT_DATE_TIME_FACTORY : DEF_DATE_TIME_FACTORY);
+ }
+
String withErrorContext(String msg) {
return withErrorContext(msg, getDatabase(), getTable().getName(), getName());
}
@@ -2034,8 +2213,10 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
/**
* Date subclass which stashes the original date bits, in case we attempt to
- * re-write the value (will not lose precision).
+ * re-write the value (will not lose precision). Also, this implementation
+ * is immutable.
*/
+ @SuppressWarnings("deprecation")
private static final class DateExt extends Date
{
private static final long serialVersionUID = 0L;
@@ -2052,6 +2233,41 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return _dateBits;
}
+ @Override
+ public void setDate(int time) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setHours(int time) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setMinutes(int time) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setMonth(int time) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setSeconds(int time) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setYear(int time) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setTime(long time) {
+ throw new UnsupportedOperationException();
+ }
+
private Object writeReplace() throws ObjectStreamException {
// if we are going to serialize this Date, convert it back to a normal
// Date (in case it is restored outside of the context of jackcess)
@@ -2456,4 +2672,65 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
sb.append("allowZeroLength=false");
}
}
+
+ /**
+ * Factory which handles date/time values appropriately for a DateTimeType.
+ */
+ static abstract class DateTimeFactory
+ {
+ public abstract DateTimeType getType();
+
+ public abstract Object fromDateBits(ColumnImpl col, long dateBits);
+
+ public abstract Object toInternalValue(DatabaseImpl db, Object value);
+ }
+
+ /**
+ * Factory impl for legacy Date handling.
+ */
+ static final class DefaultDateTimeFactory extends DateTimeFactory
+ {
+ @Override
+ public DateTimeType getType() {
+ return DateTimeType.DATE;
+ }
+
+ @Override
+ public Object fromDateBits(ColumnImpl col, long dateBits) {
+ long time = col.fromDateDouble(
+ Double.longBitsToDouble(dateBits));
+ return new DateExt(time, dateBits);
+ }
+
+ @Override
+ public Object toInternalValue(DatabaseImpl db, Object value) {
+ return ((value instanceof Date) ? value :
+ new Date(toDateLong(value)));
+ }
+ }
+
+ /**
+ * Factory impl for LocalDateTime handling.
+ */
+ static final class LDTDateTimeFactory extends DateTimeFactory
+ {
+ @Override
+ public DateTimeType getType() {
+ return DateTimeType.LOCAL_DATE_TIME;
+ }
+
+ @Override
+ public Object fromDateBits(ColumnImpl col, long dateBits) {
+ return ldtFromLocalDateDouble(Double.longBitsToDouble(dateBits));
+ }
+
+ @Override
+ public Object toInternalValue(DatabaseImpl db, Object value) {
+ if(value instanceof TemporalAccessor) {
+ return toLocalDateTime((TemporalAccessor)value, db);
+ }
+ Instant inst = Instant.ofEpochMilli(toDateLong(value));
+ return LocalDateTime.ofInstant(inst, db.getZoneId());
+ }
+ }
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/CompoundOleUtil.java b/src/main/java/com/healthmarketscience/jackcess/impl/CompoundOleUtil.java
index 8440f81..9858426 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/CompoundOleUtil.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/CompoundOleUtil.java
@@ -32,16 +32,16 @@ import com.healthmarketscience.jackcess.RuntimeIOException;
import static com.healthmarketscience.jackcess.impl.OleUtil.*;
import com.healthmarketscience.jackcess.util.MemFileChannel;
import static com.healthmarketscience.jackcess.util.OleBlob.*;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
* Utility code for working with OLE data which is in the compound storage
* format. This functionality relies on the optional POI library.
- * <p/>
+ * <br>
* Note that all POI usage is restricted to this file so that the basic ole
* support in OleUtil can be utilized without requiring POI.
*
@@ -57,7 +57,7 @@ public class CompoundOleUtil implements CompoundPackageFactory
static {
// force a poi class to be loaded to ensure that when this class is
// loaded, we know that the poi classes are available
- NPOIFSFileSystem.class.getName();
+ POIFSFileSystem.class.getName();
}
public CompoundOleUtil()
@@ -139,7 +139,7 @@ public class CompoundOleUtil implements CompoundPackageFactory
extends EmbeddedPackageContentImpl
implements CompoundContent
{
- private NPOIFSFileSystem _fs;
+ private POIFSFileSystem _fs;
private CompoundContentImpl(
OleBlobImpl blob, String prettyName, String className,
@@ -152,9 +152,9 @@ public class CompoundOleUtil implements CompoundPackageFactory
return ContentType.COMPOUND_STORAGE;
}
- private NPOIFSFileSystem getFileSystem() throws IOException {
+ private POIFSFileSystem getFileSystem() throws IOException {
if(_fs == null) {
- _fs = new NPOIFSFileSystem(MemFileChannel.newChannel(getStream(), "r"));
+ _fs = new POIFSFileSystem(MemFileChannel.newChannel(getStream(), "r"));
}
return _fs;
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/CursorImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/CursorImpl.java
index 85a53dd..bc24e46 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/CursorImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/CursorImpl.java
@@ -299,7 +299,7 @@ public abstract class CursorImpl implements Cursor
* Moves to another row in the table based on the given direction and
* returns it.
* @param columnNames Only column names in this collection will be returned
- * @return another row in this table (Column name -> Column value), where
+ * @return another row in this table (Column name -&gt; Column value), where
* "next" may be backwards if moveForward is {@code false}, or
* {@code null} if there is not another row in the given direction.
*/
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/CustomToStringStyle.java b/src/main/java/com/healthmarketscience/jackcess/impl/CustomToStringStyle.java
index 707e163..89ef061 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/CustomToStringStyle.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/CustomToStringStyle.java
@@ -21,9 +21,8 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
-import org.apache.commons.lang.SystemUtils;
-import org.apache.commons.lang.builder.StandardToStringStyle;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.StandardToStringStyle;
+import org.apache.commons.lang3.builder.ToStringBuilder;
/**
* Custom ToStringStyle for use with ToStringBuilder.
@@ -34,7 +33,7 @@ public class CustomToStringStyle extends StandardToStringStyle
{
private static final long serialVersionUID = 0L;
- private static final String ML_FIELD_SEP = SystemUtils.LINE_SEPARATOR + " ";
+ private static final String ML_FIELD_SEP = System.lineSeparator() + " ";
private static final String IMPL_SUFFIX = "Impl";
private static final int MAX_BYTE_DETAIL_LEN = 20;
private static final Object IGNORE_ME = new Object();
@@ -47,7 +46,7 @@ public class CustomToStringStyle extends StandardToStringStyle
setFieldSeparatorAtStart(true);
setFieldNameValueSeparator(": ");
setArraySeparator("," + ML_FIELD_SEP);
- setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ setContentEnd(System.lineSeparator() + "]");
setUseShortClassName(true);
}
};
@@ -91,7 +90,7 @@ public class CustomToStringStyle extends StandardToStringStyle
}
@Override
- protected String getShortClassName(Class clss) {
+ protected String getShortClassName(Class<?> clss) {
String shortName = super.getShortClassName(clss);
if(shortName.endsWith(IMPL_SUFFIX)) {
shortName = shortName.substring(0,
@@ -116,7 +115,7 @@ public class CustomToStringStyle extends StandardToStringStyle
@Override
protected void appendDetail(StringBuffer buffer, String fieldName,
- Collection value) {
+ Collection<?> value) {
buffer.append("[");
// gather contents of list in a new StringBuffer
@@ -145,13 +144,12 @@ public class CustomToStringStyle extends StandardToStringStyle
@Override
protected void appendDetail(StringBuffer buffer, String fieldName,
- Map value) {
+ Map<?,?> value) {
buffer.append("{");
// gather contents of map in a new StringBuffer
StringBuffer sb = new StringBuffer();
- @SuppressWarnings("unchecked")
- Iterator<Map.Entry<?,?>> iter = value.entrySet().iterator();
+ Iterator<? extends Map.Entry<?,?>> iter = value.entrySet().iterator();
if(iter.hasNext()) {
if(isFieldSeparatorAtStart()) {
appendFieldSeparator(sb);
@@ -203,7 +201,7 @@ public class CustomToStringStyle extends StandardToStringStyle
private static String indent(Object obj) {
return ((obj != null) ? obj.toString().replaceAll(
- SystemUtils.LINE_SEPARATOR, ML_FIELD_SEP) : null);
+ System.lineSeparator(), ML_FIELD_SEP) : null);
}
public static Object ignoreNull(Object obj) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java
index 7f50f68..2fbec97 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java
@@ -17,8 +17,8 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl;
import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.Map;
import javax.script.Bindings;
import javax.script.SimpleBindings;
@@ -42,7 +42,7 @@ public class DBEvalContext implements Expressionator.ParseContext, EvalConfig
private final DatabaseImpl _db;
private FunctionLookup _funcs = DefaultFunctions.LOOKUP;
- private Map<String,SimpleDateFormat> _sdfs;
+ private Map<String,DateTimeFormatter> _sdfs;
private Map<String,DecimalFormat> _dfs;
private TemporalConfig _temporal = TemporalConfig.US_TEMPORAL_CONFIG;
private NumericConfig _numeric = NumericConfig.US_NUMERIC_CONFIG;
@@ -68,8 +68,8 @@ public class DBEvalContext implements Expressionator.ParseContext, EvalConfig
}
}
- public Calendar getCalendar() {
- return _db.getCalendar();
+ public ZoneId getZoneId() {
+ return _db.getZoneId();
}
public NumericConfig getNumericConfig() {
@@ -99,14 +99,13 @@ public class DBEvalContext implements Expressionator.ParseContext, EvalConfig
_bindings = bindings;
}
- public SimpleDateFormat createDateFormat(String formatStr) {
+ public DateTimeFormatter createDateFormatter(String formatStr) {
if(_sdfs == null) {
- _sdfs = new SimpleCache<String,SimpleDateFormat>(MAX_CACHE_SIZE);
+ _sdfs = new SimpleCache<String,DateTimeFormatter>(MAX_CACHE_SIZE);
}
- SimpleDateFormat sdf = _sdfs.get(formatStr);
+ DateTimeFormatter sdf = _sdfs.get(formatStr);
if(sdf == null) {
- sdf = _db.createDateFormat(formatStr);
- sdf.setDateFormatSymbols(_temporal.getDateFormatSymbols());
+ sdf = DateTimeFormatter.ofPattern(formatStr, _temporal.getLocale());
_sdfs.put(formatStr, sdf);
}
return sdf;
@@ -128,8 +127,4 @@ public class DBEvalContext implements Expressionator.ParseContext, EvalConfig
public float getRandom(Integer seed) {
return _rndCtx.getRandom(seed);
}
-
- void resetDateTimeConfig() {
- _sdfs = null;
- }
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
index bb74bba..dcd4e18 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
@@ -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,7 +27,12 @@ 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.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -53,6 +57,7 @@ import com.healthmarketscience.jackcess.CursorBuilder;
import com.healthmarketscience.jackcess.DataType;
import com.healthmarketscience.jackcess.Database;
import com.healthmarketscience.jackcess.DatabaseBuilder;
+import com.healthmarketscience.jackcess.DateTimeType;
import com.healthmarketscience.jackcess.Index;
import com.healthmarketscience.jackcess.IndexBuilder;
import com.healthmarketscience.jackcess.IndexCursor;
@@ -73,8 +78,8 @@ import com.healthmarketscience.jackcess.util.LinkResolver;
import com.healthmarketscience.jackcess.util.ReadOnlyFileChannel;
import com.healthmarketscience.jackcess.util.SimpleColumnValidatorFactory;
import com.healthmarketscience.jackcess.util.TableIterableBuilder;
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -84,7 +89,7 @@ import org.apache.commons.logging.LogFactory;
* @author Tim McCune
* @usage _intermediate_class_
*/
-public class DatabaseImpl implements Database
+public class DatabaseImpl implements Database, ZoneContext
{
private static final Log LOG = LogFactory.getLog(DatabaseImpl.class);
@@ -198,9 +203,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 +259,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;
/** whether or not this db is read-only */
@@ -300,6 +307,8 @@ public class DatabaseImpl implements Database
private Charset _charset;
/** timezone to use when handling dates */
private TimeZone _timeZone;
+ /** zoneId to use when handling dates */
+ private ZoneId _zoneId;
/** language sort order to be used for textual columns */
private ColumnImpl.SortOrder _defaultSortOrder;
/** default code page to be used for textual columns (in some dbs) */
@@ -335,10 +344,11 @@ public class DatabaseImpl implements Database
/** shared state used when enforcing foreign keys */
private final FKEnforcer.SharedState _fkEnforcerSharedState =
FKEnforcer.initSharedState();
- /** Calendar for use interpreting dates/times in Columns */
- private Calendar _calendar;
/** shared context for evaluating expressions */
private DBEvalContext _evalCtx;
+ /** factory for the appropriate date/time type */
+ private ColumnImpl.DateTimeFactory _dtf =
+ ColumnImpl.getDateTimeFactory(DateTimeType.DATE);
/**
* Open an existing Database. If the existing file is not writeable or the
@@ -364,20 +374,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);
@@ -434,7 +444,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
@@ -486,11 +496,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);
}
/**
@@ -512,7 +522,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,
boolean readOnly)
@@ -529,7 +539,7 @@ public class DatabaseImpl implements Database
_evaluateExpressions = getDefaultEvaluateExpressions();
_fileFormat = fileFormat;
_pageChannel = new PageChannel(channel, closeChannel, _format, autoSync);
- _timeZone = ((timeZone == null) ? getDefaultTimeZone() : timeZone);
+ setZoneInfo(timeZone, null);
if(provider == null) {
provider = DefaultCodecProvider.INSTANCE;
}
@@ -542,6 +552,10 @@ public class DatabaseImpl implements Database
}
public File getFile() {
+ return ((_file != null) ? _file.toFile() : null);
+ }
+
+ public Path getPath() {
return _file;
}
@@ -645,20 +659,51 @@ public class DatabaseImpl implements Database
(_linkedDbs.get(linkedDbName) == table.getDatabase()));
}
+ @Override
public TimeZone getTimeZone() {
return _timeZone;
}
+ @Override
public void setTimeZone(TimeZone newTimeZone) {
- if(newTimeZone == null) {
+ setZoneInfo(newTimeZone, null);
+ }
+
+ @Override
+ public ZoneId getZoneId() {
+ return _zoneId;
+ }
+
+ public void setZoneId(ZoneId newZoneId) {
+ setZoneInfo(null, newZoneId);
+ }
+
+ private void setZoneInfo(TimeZone newTimeZone, ZoneId newZoneId) {
+ if(newTimeZone != null) {
+ newZoneId = newTimeZone.toZoneId();
+ } else if(newZoneId != null) {
+ newTimeZone = TimeZone.getTimeZone(newZoneId);
+ } else {
newTimeZone = getDefaultTimeZone();
+ newZoneId = newTimeZone.toZoneId();
}
+
_timeZone = newTimeZone;
- // clear cached calendar(s) when timezone is changed
- _calendar = null;
- if(_evalCtx != null) {
- _evalCtx.resetDateTimeConfig();
- }
+ _zoneId = newZoneId;
+ }
+
+ @Override
+ public DateTimeType getDateTimeType() {
+ return _dtf.getType();
+ }
+
+ @Override
+ public void setDateTimeType(DateTimeType dateTimeType) {
+ _dtf = ColumnImpl.getDateTimeFactory(dateTimeType);
+ }
+
+ protected ColumnImpl.DateTimeFactory getDateTimeFactory() {
+ return _dtf;
}
public Charset getCharset()
@@ -735,17 +780,6 @@ public class DatabaseImpl implements Database
return _fkEnforcerSharedState;
}
- /**
- * @usage _advanced_method_
- */
- Calendar getCalendar() {
- if(_calendar == null) {
- _calendar = DatabaseBuilder.toCompatibleCalendar(
- Calendar.getInstance(_timeZone));
- }
- return _calendar;
- }
-
public EvalConfig getEvalConfig() {
return getEvalContext();
}
@@ -2106,11 +2140,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) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
index e2b2dd5..f210a0a 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
@@ -33,7 +33,7 @@ import com.healthmarketscience.jackcess.IndexBuilder;
import com.healthmarketscience.jackcess.RuntimeIOException;
import static com.healthmarketscience.jackcess.impl.ByteUtil.ByteStream;
import static com.healthmarketscience.jackcess.impl.IndexCodes.*;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -41,17 +41,17 @@ import org.apache.commons.logging.LogFactory;
* Access table index data. This is the actual data which backs a logical
* Index, where one or more logical indexes can be backed by the same index
* data.
- *
+ *
* @author Tim McCune
*/
public class IndexData {
-
+
protected static final Log LOG = LogFactory.getLog(Index.class);
/** special entry which is less than any other entry */
public static final Entry FIRST_ENTRY =
createSpecialEntry(RowIdImpl.FIRST_ROW_ID);
-
+
/** special entry which is greater than any other entry */
public static final Entry LAST_ENTRY =
createSpecialEntry(RowIdImpl.LAST_ROW_ID);
@@ -65,12 +65,12 @@ public class IndexData {
public static final Object MIN_VALUE = new Object();
private static final DataPage NEW_ROOT_DATA_PAGE = new RootDataPage();
-
- protected static final int INVALID_INDEX_PAGE_NUMBER = 0;
-
+
+ protected static final int INVALID_INDEX_PAGE_NUMBER = 0;
+
/** Max number of columns in an index */
public static final int MAX_COLUMNS = 10;
-
+
protected static final byte[] EMPTY_PREFIX = new byte[0];
static final short COLUMN_UNUSED = -1;
@@ -85,7 +85,7 @@ public class IndexData {
private static final int MAGIC_INDEX_NUMBER = 1923;
private static final ByteOrder ENTRY_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
-
+
/** type attributes for Entries which simplify comparisons */
public enum EntryType {
/** comparable type indicating this Entry should always compare less than
@@ -104,7 +104,7 @@ public class IndexData {
than valid RowIds */
ALWAYS_LAST;
}
-
+
public static final Comparator<byte[]> BYTE_CODE_COMPARATOR =
new Comparator<byte[]>() {
public int compare(byte[] left, byte[] right) {
@@ -131,8 +131,8 @@ public class IndexData {
((left.length > right.length) ? 1 : 0));
}
};
-
-
+
+
/** name, generated on demand */
private String _name;
/** owning table */
@@ -175,7 +175,7 @@ public class IndexData {
private String _unsupportedReason;
/** Cache which manages the index pages */
private final IndexPageCache _pageCache;
-
+
protected IndexData(TableImpl table, int number, int uniqueEntryCount,
int uniqueEntryCountOffset)
{
@@ -216,14 +216,14 @@ public class IndexData {
} else {
_name = String.valueOf(_number);
}
- }
+ }
return _name;
}
public TableImpl getTable() {
return _table;
}
-
+
public JetFormat getFormat() {
return getTable().getFormat();
}
@@ -280,7 +280,7 @@ public class IndexData {
public int getIndexDataNumber() {
return _number;
}
-
+
public int getUniqueEntryCount() {
return _uniqueEntryCount;
}
@@ -315,7 +315,7 @@ public class IndexData {
public boolean isUnique() {
return(isBackingPrimaryKey() || ((_indexFlags & UNIQUE_INDEX_FLAG) != 0));
}
-
+
/**
* Whether or not values are required in the columns.
*/
@@ -345,7 +345,7 @@ public class IndexData {
return _rootPageNumber;
}
- private void setUnsupportedReason(String reason, ColumnImpl col) {
+ private void setUnsupportedReason(String reason, ColumnImpl col) {
_unsupportedReason = withErrorContext(reason);
if(!col.getTable().isSystem()) {
LOG.warn(_unsupportedReason + ", making read-only");
@@ -371,7 +371,7 @@ public class IndexData {
public int getOwnedPageCount() {
return _ownedPages.getPageCount();
}
-
+
void addOwnedPage(int pageNumber) throws IOException {
_ownedPages.addPageNumber(pageNumber);
}
@@ -379,7 +379,7 @@ public class IndexData {
void collectUsageMapPages(Collection<Integer> pages) {
pages.add(_ownedPages.getTablePageNumber());
}
-
+
/**
* Used by unit tests to validate the internal status of the index.
* @usage _advanced_method_
@@ -407,7 +407,7 @@ public class IndexData {
}
return count;
}
-
+
/**
* Forces initialization of this index (actual parsing of index pages).
* normally, the index will not be initialized until the entries are
@@ -429,7 +429,7 @@ public class IndexData {
{
// make sure we've parsed the entries
initialize();
-
+
if(_unsupportedReason != null) {
throw new UnsupportedOperationException(
"Cannot write indexes of this type due to " + _unsupportedReason);
@@ -470,7 +470,7 @@ public class IndexData {
}
_ownedPages = UsageMap.read(getTable().getDatabase(), tableBuffer);
-
+
_rootPageNumber = tableBuffer.getInt();
ByteUtil.forward(tableBuffer, getFormat().SKIP_BEFORE_INDEX_FLAGS); //Forward past Unknown
@@ -525,7 +525,7 @@ public class IndexData {
* @param buffer Buffer to write to
*/
protected static void writeDefinition(
- TableMutator creator, ByteBuffer buffer,
+ TableMutator creator, ByteBuffer buffer,
TableMutator.IndexDataState idxDataState, ByteBuffer rootPageBuffer)
throws IOException
{
@@ -559,7 +559,7 @@ public class IndexData {
creator.getDatabase(), creator.getTableName(), idx.getName()));
}
}
-
+
buffer.putShort(columnNumber); // table column number
buffer.put(flags); // column flags (e.g. ordering)
}
@@ -568,7 +568,7 @@ public class IndexData {
ByteUtil.put3ByteInt(buffer, idxDataState.getUmapPageNumber()); // umap page
// write empty root index page
- creator.getPageChannel().writePage(rootPageBuffer,
+ creator.getPageChannel().writePage(rootPageBuffer,
idxDataState.getRootPageNumber());
buffer.putInt(idxDataState.getRootPageNumber());
@@ -577,11 +577,11 @@ public class IndexData {
ByteUtil.forward(buffer, 5); // unknown
}
- private static ByteBuffer createRootPageBuffer(TableMutator creator)
+ private static ByteBuffer createRootPageBuffer(TableMutator creator)
throws IOException
{
ByteBuffer rootPageBuffer = creator.getPageChannel().createPageBuffer();
- writeDataPage(rootPageBuffer, NEW_ROOT_DATA_PAGE,
+ writeDataPage(rootPageBuffer, NEW_ROOT_DATA_PAGE,
creator.getTdefPageNumber(), creator.getFormat());
return rootPageBuffer;
}
@@ -591,7 +591,7 @@ public class IndexData {
* this method returns.
* <p>
* Forces index initialization.
- *
+ *
* @param row Row to add
* @param rowId rowId of the row to be added
*
@@ -603,7 +603,7 @@ public class IndexData {
{
return prepareAddRow(row, rowId, new AddRowPendingChange(nextChange));
}
-
+
private PendingChange prepareAddRow(Object[] row, RowIdImpl rowId,
AddRowPendingChange change)
throws IOException
@@ -619,7 +619,7 @@ public class IndexData {
"Null value found in row " + Arrays.asList(row) +
" for primary key or required index"));
}
-
+
// make sure we've parsed the entries
initialize();
@@ -637,14 +637,14 @@ public class IndexData {
DataPage dataPage = findDataPage(newEntry);
int idx = dataPage.findEntry(newEntry);
if(idx < 0) {
-
+
// this is a new entry
idx = missingIndexToInsertionPoint(idx);
Position newPos = new Position(dataPage, idx, newEntry, true);
Position nextPos = getNextPosition(newPos);
Position prevPos = getPreviousPosition(newPos);
-
+
// determine if the addition of this entry would break the uniqueness
// constraint. See isUnique() for some notes about uniqueness as
// defined by Access.
@@ -691,7 +691,7 @@ public class IndexData {
* before this method returns.
* <p>
* Forces index initialization.
- *
+ *
* @param oldRow Row to be removed
* @param newRow Row to be added
* @param rowId rowId of the row to be updated
@@ -699,7 +699,7 @@ public class IndexData {
* @return a PendingChange which can complete the update or roll it back
*/
public PendingChange prepareUpdateRow(Object[] oldRow, RowIdImpl rowId,
- Object[] newRow,
+ Object[] newRow,
PendingChange nextChange)
throws IOException
{
@@ -715,12 +715,12 @@ public class IndexData {
throw e;
}
}
-
+
/**
* Removes a row from this index
* <p>
* Forces index initialization.
- *
+ *
* @param row Row to remove
* @param rowId rowId of the row to be removed
*/
@@ -729,7 +729,7 @@ public class IndexData {
{
deleteRowImpl(row, rowId);
}
-
+
private Entry deleteRowImpl(Object[] row, RowIdImpl rowId)
throws IOException
{
@@ -738,7 +738,7 @@ public class IndexData {
// nothing to do
return null;
}
-
+
// make sure we've parsed the entries
initialize();
@@ -748,7 +748,7 @@ public class IndexData {
++_modCount;
} else {
LOG.warn(withErrorContext(
- "Failed removing index entry " + oldEntry + " for row: " +
+ "Failed removing index entry " + oldEntry + " for row: " +
Arrays.asList(row)));
}
return removedEntry;
@@ -773,7 +773,7 @@ public class IndexData {
dataPage.addEntry(missingIndexToInsertionPoint(idx), removedEntry);
}
}
-
+
/**
* Removes an entry from the relevant index dataPage, maintaining the order.
* Will search by RowId if entry is not found (in case a partial entry was
@@ -810,10 +810,10 @@ public class IndexData {
// found it!
removedEntry = dataPage.removeEntry(idx);
}
-
+
return removedEntry;
}
-
+
public static void commitAll(PendingChange change) throws IOException {
while(change != null) {
change.commit();
@@ -827,7 +827,7 @@ public class IndexData {
change = change.getNext();
}
}
-
+
/**
* Gets a new cursor for this index.
* <p>
@@ -838,13 +838,13 @@ public class IndexData {
{
return cursor(null, true, null, true);
}
-
+
/**
* Gets a new cursor for this index, narrowed to the range defined by the
* given startRow and endRow.
* <p>
* Forces index initialization.
- *
+ *
* @param startRow the first row of data for the cursor, or {@code null} for
* the first entry
* @param startInclusive whether or not startRow is inclusive or exclusive
@@ -1008,13 +1008,13 @@ public class IndexData {
int valIdx = 0;
Object[] idxRow = new Object[getTable().getColumnCount()];
for(ColumnDescriptor col : _columns) {
- idxRow[col.getColumnIndex()] =
+ idxRow[col.getColumnIndex()] =
((valIdx < values.length) ? values[valIdx] : filler);
++valIdx;
}
return idxRow;
}
-
+
/**
* Constructs an array of values appropriate for this index from the given
* column value.
@@ -1057,7 +1057,7 @@ public class IndexData {
idxRow[col.getColumnIndex()] = row.get(col.getName());
}
return idxRow;
- }
+ }
/**
* Constructs an array of values appropriate for this index from the given
@@ -1088,7 +1088,7 @@ public class IndexData {
Object[] idxRow = new Object[getTable().getColumnCount()];
int valIdx = 0;
for(ColumnDescriptor col : _columns) {
- idxRow[col.getColumnIndex()] =
+ idxRow[col.getColumnIndex()] =
((valIdx < numCols) ? row.get(col.getName()) : filler);
++valIdx;
}
@@ -1116,7 +1116,7 @@ public class IndexData {
sb.append("pageCache", _pageCache);
return sb.toString();
}
-
+
/**
* Write the given index page out to a buffer
*/
@@ -1126,7 +1126,7 @@ public class IndexData {
if(dataPage.getCompressedEntrySize() > _maxPageEntrySize) {
throw new IllegalStateException(withErrorContext("data page is too large"));
}
-
+
ByteBuffer buffer = _indexBufferH.getPageBuffer(getPageChannel());
writeDataPage(buffer, dataPage, getTable().getTableDefPageNumber(),
@@ -1170,7 +1170,7 @@ public class IndexData {
// first entry includes the prefix
buffer.put(entryPrefix);
-
+
for(Entry entry : dataPage.getEntries()) {
entry.write(buffer, entryPrefix);
}
@@ -1232,7 +1232,7 @@ public class IndexData {
"Unexpected order in index entries, " +
prevEntry + " >= " + entry));
}
-
+
entries.add(entry);
if((entries.size() == 1) && (entryPrefixLength > 0)) {
@@ -1251,7 +1251,7 @@ public class IndexData {
dataPage.setEntryPrefix(entryPrefix != null ? entryPrefix : EMPTY_PREFIX);
dataPage.setEntries(entries);
dataPage.setTotalEntrySize(totalEntrySize);
-
+
int prevPageNumber = buffer.getInt(getFormat().OFFSET_PREV_INDEX_PAGE);
int nextPageNumber = buffer.getInt(getFormat().OFFSET_NEXT_INDEX_PAGE);
int childTailPageNumber =
@@ -1265,7 +1265,7 @@ public class IndexData {
/**
* Returns a new Entry of the correct type for the given data and page type.
*/
- private static Entry newEntry(ByteBuffer buffer, int entryLength,
+ private static Entry newEntry(ByteBuffer buffer, int entryLength,
boolean isLeaf)
throws IOException
{
@@ -1291,10 +1291,10 @@ public class IndexData {
tmpEntryBuffer.put(valuePrefix);
tmpEntryBuffer.put(indexPage.array(), indexPage.position(), entryLen);
tmpEntryBuffer.flip();
-
+
return tmpEntryBuffer;
}
-
+
/**
* Determines if the given index page is a leaf or node page.
*/
@@ -1309,7 +1309,7 @@ public class IndexData {
}
throw new IOException(withErrorContext("Unexpected page type " + pageType));
}
-
+
/**
* Determines the number of {@code null} values for this index from the
* given row.
@@ -1319,7 +1319,7 @@ public class IndexData {
if(values == null) {
return _columns.size();
}
-
+
// annoyingly, the values array could come from different sources, one
// of which will make it a different size than the other. we need to
// handle both situations.
@@ -1330,7 +1330,7 @@ public class IndexData {
++nullCount;
}
}
-
+
return nullCount;
}
@@ -1342,12 +1342,12 @@ public class IndexData {
if(values == null) {
return null;
}
-
+
if(_entryBuffer == null) {
_entryBuffer = new ByteStream();
}
_entryBuffer.reset();
-
+
for(ColumnDescriptor col : _columns) {
Object value = values[col.getColumnIndex()];
@@ -1373,9 +1373,9 @@ public class IndexData {
col.writeValue(value, _entryBuffer);
}
-
+
return _entryBuffer.toByteArray();
- }
+ }
/**
* Finds the data page for the given entry.
@@ -1385,7 +1385,7 @@ public class IndexData {
{
return _pageCache.findCacheDataPage(entry);
}
-
+
/**
* Gets the data page for the pageNumber.
*/
@@ -1394,7 +1394,7 @@ public class IndexData {
{
return _pageCache.getCacheDataPage(pageNumber);
}
-
+
/**
* Flips the first bit in the byte at the given index.
*/
@@ -1418,7 +1418,7 @@ public class IndexData {
static byte[] flipBytes(byte[] value, int offset, int length) {
for(int i = offset; i < (offset + length); ++i) {
value[i] = (byte)(~value[i]);
- }
+ }
return value;
}
@@ -1430,7 +1430,7 @@ public class IndexData {
{
// always write in big endian order
return column.write(value, 0, ENTRY_BYTE_ORDER).array();
- }
+ }
/**
* Writes a binary value using the general binary entry encoding rules.
@@ -1452,7 +1452,7 @@ public class IndexData {
// bit twiddling rules:
// - isAsc => nothing
- // - !isAsc => flipBytes, _but keep intermediate 09 unflipped_!
+ // - !isAsc => flipBytes, _but keep intermediate 09 unflipped_!
// first, write any intermediate segements
int segmentLen = dataLen;
@@ -1549,7 +1549,7 @@ public class IndexData {
default:
// we can't modify this index at this point in time
- setUnsupportedReason("unsupported data type " + col.getType() +
+ setUnsupportedReason("unsupported data type " + col.getType() +
" for index", col);
return new ReadOnlyColumnDescriptor(col, flags);
}
@@ -1598,7 +1598,7 @@ public class IndexData {
return msg + " (Db=" + db.getName() + ";Table=" + tableName +
";Index=" + idxName + ")";
}
-
+
/**
* Information about the columns in an index. Also encodes new index
* values.
@@ -1626,11 +1626,11 @@ public class IndexData {
public boolean isAscending() {
return((getFlags() & ASCENDING_COLUMN_FLAG) != 0);
}
-
+
public int getColumnIndex() {
return getColumn().getColumnIndex();
}
-
+
public String getName() {
return getColumn().getName();
}
@@ -1638,7 +1638,7 @@ public class IndexData {
protected boolean isNullValue(Object value) {
return (value == null);
}
-
+
protected final void writeValue(Object value, ByteStream bout)
throws IOException
{
@@ -1647,7 +1647,7 @@ public class IndexData {
bout.write(getNullEntryFlag(isAscending()));
return;
}
-
+
// write the start flag
bout.write(getStartEntryFlag(isAscending()));
// write the rest of the value
@@ -1655,8 +1655,8 @@ public class IndexData {
}
protected abstract void writeNonNullValue(Object value, ByteStream bout)
- throws IOException;
-
+ throws IOException;
+
@Override
public String toString() {
return CustomToStringStyle.builder(this)
@@ -1676,26 +1676,26 @@ public class IndexData {
{
super(column, flags);
}
-
+
@Override
protected void writeNonNullValue(Object value, ByteStream bout)
throws IOException
{
byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
-
+
// bit twiddling rules:
// - isAsc => flipFirstBit
// - !isAsc => flipFirstBit, flipBytes
-
+
flipFirstBitInByte(valueBytes, 0);
if(!isAscending()) {
flipBytes(valueBytes);
}
-
+
bout.write(valueBytes);
- }
+ }
}
-
+
/**
* ColumnDescriptor for floating point based columns.
*/
@@ -1707,13 +1707,13 @@ public class IndexData {
{
super(column, flags);
}
-
+
@Override
protected void writeNonNullValue(Object value, ByteStream bout)
throws IOException
{
byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
-
+
// determine if the number is negative by testing if the first bit is
// set
boolean isNegative = ((valueBytes[0] & 0x80) != 0);
@@ -1723,18 +1723,18 @@ public class IndexData {
// isAsc && isNeg => flipBytes
// !isAsc && !isNeg => flipFirstBit, flipBytes
// !isAsc && isNeg => nothing
-
+
if(!isNegative) {
flipFirstBitInByte(valueBytes, 0);
}
if(isNegative == isAscending()) {
flipBytes(valueBytes);
}
-
+
bout.write(valueBytes);
- }
+ }
}
-
+
/**
* ColumnDescriptor for fixed point based columns (legacy sort order).
*/
@@ -1755,15 +1755,15 @@ public class IndexData {
}
// reverse the sign byte (after any previous byte flipping)
- valueBytes[0] = (isNegative ? (byte)0x00 : (byte)0xFF);
+ valueBytes[0] = (isNegative ? (byte)0x00 : (byte)0xFF);
}
-
+
@Override
protected void writeNonNullValue(Object value, ByteStream bout)
throws IOException
{
byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
-
+
// determine if the number is negative by testing if the first bit is
// set
boolean isNegative = ((valueBytes[0] & 0x80) != 0);
@@ -1773,7 +1773,7 @@ public class IndexData {
// isAsc && isNeg => flipBytes, setReverseSignByte => 00 FF FF ...
// !isAsc && !isNeg => flipBytes, setReverseSignByte => FF FF FF ...
// !isAsc && isNeg => setReverseSignByte => 00 00 00 ...
-
+
// v2007 bit twiddling rules (old ordering was a bug, MS kb 837148):
// isAsc && !isNeg => setSignByte 0xFF => FF 00 00 ...
// isAsc && isNeg => setSignByte 0xFF, flipBytes => 00 FF FF ...
@@ -1782,9 +1782,9 @@ public class IndexData {
handleNegationAndOrder(isNegative, valueBytes);
bout.write(valueBytes);
- }
+ }
}
-
+
/**
* ColumnDescriptor for new-style fixed point based columns.
*/
@@ -1796,7 +1796,7 @@ public class IndexData {
{
super(column, flags);
}
-
+
@Override
protected void handleNegationAndOrder(boolean isNegative,
byte[] valueBytes)
@@ -1809,9 +1809,9 @@ public class IndexData {
if(isNegative == isAscending()) {
flipBytes(valueBytes);
}
- }
+ }
}
-
+
/**
* ColumnDescriptor for byte based columns.
*/
@@ -1822,24 +1822,24 @@ public class IndexData {
{
super(column, flags);
}
-
+
@Override
protected void writeNonNullValue(Object value, ByteStream bout)
throws IOException
{
byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
-
+
// bit twiddling rules:
// - isAsc => nothing
// - !isAsc => flipBytes
if(!isAscending()) {
flipBytes(valueBytes);
}
-
+
bout.write(valueBytes);
- }
+ }
}
-
+
/**
* ColumnDescriptor for boolean columns.
*/
@@ -1856,7 +1856,7 @@ public class IndexData {
// null values are handled as booleans
return false;
}
-
+
@Override
protected void writeNonNullValue(Object value, ByteStream bout)
throws IOException
@@ -1867,11 +1867,11 @@ public class IndexData {
(isAscending() ? ASC_BOOLEAN_FALSE : DESC_BOOLEAN_FALSE));
}
}
-
+
/**
* ColumnDescriptor for "general legacy" sort order text based columns.
*/
- private static final class GenLegTextColumnDescriptor
+ private static final class GenLegTextColumnDescriptor
extends ColumnDescriptor
{
private GenLegTextColumnDescriptor(ColumnImpl column, byte flags)
@@ -1879,14 +1879,14 @@ public class IndexData {
{
super(column, flags);
}
-
+
@Override
protected void writeNonNullValue(Object value, ByteStream bout)
throws IOException
{
GeneralLegacyIndexCodes.GEN_LEG_INSTANCE.writeNonNullIndexTextValue(
value, bout, isAscending());
- }
+ }
}
/**
@@ -1899,14 +1899,14 @@ public class IndexData {
{
super(column, flags);
}
-
+
@Override
protected void writeNonNullValue(Object value, ByteStream bout)
throws IOException
{
GeneralIndexCodes.GEN_INSTANCE.writeNonNullIndexTextValue(
value, bout, isAscending());
- }
+ }
}
/**
@@ -1919,7 +1919,7 @@ public class IndexData {
{
super(column, flags);
}
-
+
@Override
protected void writeNonNullValue(Object value, ByteStream bout)
throws IOException
@@ -1929,7 +1929,7 @@ public class IndexData {
bout);
}
}
-
+
/**
* ColumnDescriptor for BINARY columns.
@@ -1950,8 +1950,8 @@ public class IndexData {
ColumnImpl.toByteArray(value), isAscending(), bout);
}
}
-
-
+
+
/**
* ColumnDescriptor for columns which we cannot currently write.
*/
@@ -1971,7 +1971,7 @@ public class IndexData {
"Cannot write indexes of this type due to " + _unsupportedReason);
}
}
-
+
/**
* A single leaf entry in an index (points to a single row)
*/
@@ -1983,7 +1983,7 @@ public class IndexData {
private final byte[] _entryBytes;
/** comparable type for the entry */
private final EntryType _type;
-
+
/**
* Create a new entry
* @param entryBytes encoded bytes for this index entry
@@ -1995,7 +1995,7 @@ public class IndexData {
_entryBytes = entryBytes;
_type = type;
}
-
+
/**
* Create a new entry
* @param entryBytes encoded bytes for this index entry
@@ -2014,7 +2014,7 @@ public class IndexData {
{
this(buffer, entryLen, 0);
}
-
+
/**
* Read an existing entry in from a buffer
*/
@@ -2031,11 +2031,11 @@ public class IndexData {
// read the rowId
int page = ByteUtil.get3ByteInt(buffer, ENTRY_BYTE_ORDER);
int row = ByteUtil.getUnsignedByte(buffer);
-
+
_rowId = new RowIdImpl(page, row);
_type = EntryType.NORMAL;
}
-
+
public RowIdImpl getRowId() {
return _rowId;
}
@@ -2047,11 +2047,11 @@ public class IndexData {
public Integer getSubPageNumber() {
throw new UnsupportedOperationException();
}
-
+
public boolean isLeafEntry() {
return true;
}
-
+
public boolean isValid() {
return(_entryBytes != null);
}
@@ -2059,7 +2059,7 @@ public class IndexData {
protected final byte[] getEntryBytes() {
return _entryBytes;
}
-
+
/**
* Size of this entry in the db.
*/
@@ -2067,7 +2067,7 @@ public class IndexData {
// need 4 trailing bytes for the rowId
return _entryBytes.length + 4;
}
-
+
/**
* Write this entry into a buffer
*/
@@ -2076,15 +2076,15 @@ public class IndexData {
throws IOException
{
if(prefix.length <= _entryBytes.length) {
-
+
// write entry bytes, not including prefix
buffer.put(_entryBytes, prefix.length,
(_entryBytes.length - prefix.length));
ByteUtil.put3ByteInt(buffer, getRowId().getPageNumber(),
ENTRY_BYTE_ORDER);
-
+
} else if(prefix.length <= (_entryBytes.length + 3)) {
-
+
// the prefix includes part of the page number, write to temp buffer
// and copy last bytes to output buffer
ByteBuffer tmp = ByteBuffer.allocate(3);
@@ -2093,16 +2093,16 @@ public class IndexData {
tmp.flip();
tmp.position(prefix.length - _entryBytes.length);
buffer.put(tmp);
-
+
} else {
-
+
// since the row number would never be the same if the page number is
// the same, nothing past the page number should ever be included in
// the prefix.
// FIXME, this could happen if page has only one row...
throw new IllegalStateException("prefix should never be this long");
}
-
+
buffer.put((byte)getRowId().getRowNumber());
}
@@ -2113,7 +2113,7 @@ public class IndexData {
}
return sb;
}
-
+
@Override
public String toString() {
return entryBytesToStringBuilder(
@@ -2141,7 +2141,7 @@ public class IndexData {
public boolean equalsEntryBytes(Entry o) {
return(BYTE_CODE_COMPARATOR.compare(_entryBytes, o._entryBytes) == 0);
}
-
+
public int compareTo(Entry other) {
if (this == other) {
return 0;
@@ -2165,7 +2165,7 @@ public class IndexData {
return typeCmp;
}
}
-
+
// at this point we let the RowId decide the final result
return _rowId.compareTo(other.getRowId());
}
@@ -2177,7 +2177,7 @@ public class IndexData {
protected Entry asNodeEntry(Integer subPageNumber) {
return new NodeEntry(_entryBytes, _rowId, _type, subPageNumber);
}
-
+
}
/**
@@ -2200,7 +2200,7 @@ public class IndexData {
super(entryBytes, rowId, type);
_subPageNumber = subPageNumber;
}
-
+
/**
* Read an existing node entry in from a buffer
*/
@@ -2222,19 +2222,19 @@ public class IndexData {
public boolean isLeafEntry() {
return false;
}
-
+
@Override
protected int size() {
// need 4 trailing bytes for the sub-page number
return super.size() + 4;
}
-
+
@Override
protected void write(ByteBuffer buffer, byte[] prefix) throws IOException {
super.write(buffer, prefix);
ByteUtil.putInt(buffer, _subPageNumber, ENTRY_BYTE_ORDER);
}
-
+
@Override
public boolean equals(Object o) {
return((this == o) ||
@@ -2250,7 +2250,7 @@ public class IndexData {
.append("rowId", getRowId())
.append("subPage", _subPageNumber))
.toString();
- }
+ }
}
/**
@@ -2298,14 +2298,14 @@ public class IndexData {
private int getIndexModCount() {
return IndexData.this._modCount;
}
-
+
/**
* Returns the first entry (exclusive) as defined by this cursor.
*/
public Entry getFirstEntry() {
return _firstPos.getEntry();
}
-
+
/**
* Returns the last entry (exclusive) as defined by this cursor.
*/
@@ -2320,7 +2320,7 @@ public class IndexData {
public boolean isUpToDate() {
return(getIndexModCount() == _lastModCount);
}
-
+
public void reset() {
beforeFirst();
}
@@ -2341,26 +2341,26 @@ public class IndexData {
/**
* Repositions the cursor so that the next row will be the first entry
- * >= the given row.
+ * &gt;= the given row.
*/
public void beforeEntry(Object[] row)
throws IOException
{
- restorePosition(new Entry(IndexData.this.createEntryBytes(row),
+ restorePosition(new Entry(IndexData.this.createEntryBytes(row),
RowIdImpl.FIRST_ROW_ID));
}
-
+
/**
* Repositions the cursor so that the previous row will be the first
- * entry <= the given row.
+ * entry &lt;= the given row.
*/
public void afterEntry(Object[] row)
throws IOException
{
- restorePosition(new Entry(IndexData.this.createEntryBytes(row),
+ restorePosition(new Entry(IndexData.this.createEntryBytes(row),
RowIdImpl.LAST_ROW_ID));
}
-
+
/**
* @return valid entry if there was a next entry,
* {@code #getLastEntry} otherwise
@@ -2386,7 +2386,7 @@ public class IndexData {
{
restorePosition(curEntry, _curPos.getEntry());
}
-
+
/**
* Restores a current and previous position for the cursor.
*/
@@ -2406,7 +2406,7 @@ public class IndexData {
checkForModification();
}
}
-
+
/**
* Gets another entry in the given direction, returning the new entry.
*/
@@ -2462,7 +2462,7 @@ public class IndexData {
withErrorContext("Invalid entry given " + entry));
}
}
-
+
Position pos = findEntryPosition(entry);
if(pos.compareTo(_lastPos) >= 0) {
return _lastPos;
@@ -2471,7 +2471,7 @@ public class IndexData {
}
return pos;
}
-
+
/**
* Updates any the boundary info (_firstPos/_lastPos).
*/
@@ -2481,7 +2481,7 @@ public class IndexData {
_firstPos = findEntryPosition(_firstPos.getEntry());
_lastPos = findEntryPosition(_lastPos.getEntry());
}
-
+
@Override
public String toString() {
return CustomToStringStyle.valueBuilder(this)
@@ -2489,7 +2489,7 @@ public class IndexData {
.append("prevPosition", _prevPos)
.toString();
}
-
+
/**
* Handles moving the cursor in a given direction. Separates cursor
* logic from value storage.
@@ -2500,7 +2500,7 @@ public class IndexData {
public abstract Position getBeginningPosition();
public abstract Position getEndPosition();
}
-
+
/**
* Handles moving the cursor forward.
*/
@@ -2524,7 +2524,7 @@ public class IndexData {
return _lastPos;
}
}
-
+
/**
* Handles moving the cursor backward.
*/
@@ -2569,7 +2569,7 @@ public class IndexData {
{
this(dataPage, idx, dataPage.getEntries().get(idx), false);
}
-
+
private Position(DataPage dataPage, int idx, Entry entry, boolean between)
{
_dataPage = dataPage;
@@ -2581,7 +2581,7 @@ public class IndexData {
public DataPage getDataPage() {
return _dataPage;
}
-
+
public int getIndex() {
return _idx;
}
@@ -2598,7 +2598,7 @@ public class IndexData {
// non-between case
return(_idx - 1);
}
-
+
public Entry getEntry() {
return _entry;
}
@@ -2610,7 +2610,7 @@ public class IndexData {
public boolean equalsEntry(Entry entry) {
return _entry.equals(entry);
}
-
+
public int compareTo(Position other)
{
if(this == other) {
@@ -2627,16 +2627,16 @@ public class IndexData {
return idxCmp;
}
}
-
+
// compare the entries.
return _entry.compareTo(other._entry);
}
-
+
@Override
public int hashCode() {
return _entry.hashCode();
}
-
+
@Override
public boolean equals(Object o) {
return((this == o) ||
@@ -2661,7 +2661,7 @@ public class IndexData {
protected static abstract class DataPage {
public abstract int getPageNumber();
-
+
public abstract boolean isLeaf();
public abstract void setLeaf(boolean isLeaf);
@@ -2671,7 +2671,7 @@ public class IndexData {
public abstract void setNextPageNumber(int pageNumber);
public abstract int getChildTailPageNumber();
public abstract void setChildTailPageNumber(int pageNumber);
-
+
public abstract int getTotalEntrySize();
public abstract void setTotalEntrySize(int totalSize);
public abstract byte[] getEntryPrefix();
@@ -2688,7 +2688,7 @@ public class IndexData {
public final boolean isEmpty() {
return getEntries().isEmpty();
}
-
+
public final int getCompressedEntrySize() {
// when written to the index page, the entryPrefix bytes will only be
// written for the first entry, so we subtract the entry prefix size
@@ -2717,7 +2717,7 @@ public class IndexData {
public final String toString() {
List<Entry> entries = getEntries();
- String objName =
+ String objName =
(isLeaf() ? "Leaf" : "Node") + "DataPage[" + getPageNumber() +
"] " + getPrevPageNumber() + ", " + getNextPageNumber() + ", (" +
getChildTailPageNumber() + ")";
@@ -2740,7 +2740,7 @@ public class IndexData {
@Override
public int getPageNumber() { return 0; }
-
+
@Override
public boolean isLeaf() { return true; }
@Override
@@ -2760,7 +2760,7 @@ public class IndexData {
public int getChildTailPageNumber() { return 0; }
@Override
public void setChildTailPageNumber(int pageNumber) { }
-
+
@Override
public int getTotalEntrySize() { return 0; }
@Override
@@ -2772,7 +2772,7 @@ public class IndexData {
public void setEntryPrefix(byte[] entryPrefix) { }
@Override
- public List<Entry> getEntries() { return Collections.emptyList(); }
+ public List<Entry> getEntries() { return Collections.emptyList(); }
@Override
public void setEntries(List<Entry> entries) { }
@Override
@@ -2800,7 +2800,7 @@ public class IndexData {
public PendingChange getNext() {
return _next;
}
-
+
/**
* Completes the pending change.
*/
@@ -2827,7 +2827,7 @@ public class IndexData {
super(next);
}
- public void setAddRow(Entry addEntry, DataPage dataPage, int idx,
+ public void setAddRow(Entry addEntry, DataPage dataPage, int idx,
boolean isDupe) {
_addEntry = addEntry;
_addDataPage = dataPage;
@@ -2843,7 +2843,7 @@ public class IndexData {
public void commit() throws IOException {
commitAddRow(_addEntry, _addDataPage, _addIdx, _isDupe, _oldEntry);
}
-
+
@Override
public void rollback() throws IOException {
_addEntry = null;
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
index 0fbd231..5332432 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
@@ -25,7 +25,7 @@ import java.util.Map;
import com.healthmarketscience.jackcess.CursorBuilder;
import com.healthmarketscience.jackcess.Index;
import com.healthmarketscience.jackcess.IndexBuilder;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexPageCache.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexPageCache.java
index 4ee1882..d594c1c 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexPageCache.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexPageCache.java
@@ -29,7 +29,7 @@ import java.util.Map;
import java.util.RandomAccess;
import static com.healthmarketscience.jackcess.impl.IndexData.*;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
/**
* Manager of the index pages for a IndexData.
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java b/src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java
index d8a2336..466b6a4 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java
@@ -35,7 +35,7 @@ import java.util.regex.Pattern;
import com.healthmarketscience.jackcess.DataType;
import com.healthmarketscience.jackcess.util.OleBlob;
import static com.healthmarketscience.jackcess.util.OleBlob.*;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
/**
* Utility code for working with OLE data.
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/PropertyMapImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/PropertyMapImpl.java
index be3a249..dfcbb9d 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/PropertyMapImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/PropertyMapImpl.java
@@ -17,6 +17,7 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl;
import java.io.IOException;
+import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
@@ -208,7 +209,7 @@ public class PropertyMapImpl implements PropertyMap
type = DataType.FLOAT;
} else if(value instanceof Double) {
type = DataType.DOUBLE;
- } else if(value instanceof Date) {
+ } else if((value instanceof Date) || (value instanceof LocalDateTime)) {
type = DataType.SHORT_DATE_TIME;
} else if(value instanceof byte[]) {
type = DataType.OLE;
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/RowIdImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/RowIdImpl.java
index 7bfab4a..5ce8214 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/RowIdImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/RowIdImpl.java
@@ -19,7 +19,7 @@ package com.healthmarketscience.jackcess.impl;
import java.io.Serializable;
import com.healthmarketscience.jackcess.RowId;
-import org.apache.commons.lang.builder.CompareToBuilder;
+import org.apache.commons.lang3.builder.CompareToBuilder;
/**
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/RowImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/RowImpl.java
index ee088f4..9b9ee7a 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/RowImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/RowImpl.java
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Date;
import java.math.BigDecimal;
+import java.time.LocalDateTime;
import com.healthmarketscience.jackcess.Row;
import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
@@ -27,8 +28,8 @@ import com.healthmarketscience.jackcess.util.OleBlob;
/**
- * A row of data as column->value pairs.
- * </p>
+ * A row of data as column-&gt;value pairs.
+ * <br>
* Note that the {@link #equals} and {@link #hashCode} methods work on the row
* contents <i>only</i> (i.e. they ignore the id).
*
@@ -36,7 +37,7 @@ import com.healthmarketscience.jackcess.util.OleBlob;
*/
public class RowImpl extends LinkedHashMap<String,Object> implements Row
{
- private static final long serialVersionUID = 20130314L;
+ private static final long serialVersionUID = 20130314L;
private final RowIdImpl _id;
@@ -90,10 +91,15 @@ public class RowImpl extends LinkedHashMap<String,Object> implements Row
return (Double)get(name);
}
+ @SuppressWarnings("deprecation")
public Date getDate(String name) {
return (Date)get(name);
}
+ public LocalDateTime getLocalDateTime(String name) {
+ return (LocalDateTime)get(name);
+ }
+
public byte[] getBytes(String name) {
return (byte[])get(name);
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
index 15a0c8c..9f6ae49 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
@@ -52,7 +52,7 @@ import com.healthmarketscience.jackcess.Table;
import com.healthmarketscience.jackcess.expr.Identifier;
import com.healthmarketscience.jackcess.util.ErrorHandler;
import com.healthmarketscience.jackcess.util.ExportUtil;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -2086,7 +2086,7 @@ public class TableImpl implements Table, PropertyMaps.Owner
}
/**
- * Converts a map of columnName -> columnValue to an array of row values
+ * Converts a map of columnName -&gt; columnValue to an array of row values
* appropriate for a call to {@link #addRow(Object...)}, where the generated
* RowId will be an extra value at the end of the array.
* @see ColumnImpl#RETURN_ROW_ID
@@ -2110,7 +2110,7 @@ public class TableImpl implements Table, PropertyMaps.Owner
}
/**
- * Converts a map of columnName -> columnValue to an array of row values.
+ * Converts a map of columnName -&gt; columnValue to an array of row values.
*/
private Object[] asRow(Map<String,?> rowMap, Object defaultValue,
boolean returnRowId)
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ZoneContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/ZoneContext.java
new file mode 100644
index 0000000..0134e1f
--- /dev/null
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/ZoneContext.java
@@ -0,0 +1,32 @@
+/*
+Copyright (c) 2018 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.impl;
+
+import java.time.ZoneId;
+import java.util.TimeZone;
+
+/**
+ * Provider of zone related info for date/time conversions.
+ *
+ * @author James Ahlborn
+ */
+interface ZoneContext
+{
+ public ZoneId getZoneId();
+
+ public TimeZone getTimeZone();
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/complex/AttachmentColumnInfoImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/complex/AttachmentColumnInfoImpl.java
index 6642a69..1789e92 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/complex/AttachmentColumnInfoImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/complex/AttachmentColumnInfoImpl.java
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
@@ -166,7 +167,7 @@ public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl<Attachment>
String name = (String)getFileNameColumn().getRowValue(rawValue);
String type = (String)getFileTypeColumn().getRowValue(rawValue);
Integer flags = (Integer)getFileFlagsColumn().getRowValue(rawValue);
- Date ts = (Date)getFileTimeStampColumn().getRowValue(rawValue);
+ Object ts = getFileTimeStampColumn().getRowValue(rawValue);
byte[] data = (byte[])getFileDataColumn().getRowValue(rawValue);
return new AttachmentImpl(id, complexValueFk, url, name, type, null,
@@ -182,7 +183,7 @@ public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl<Attachment>
getFileNameColumn().setRowValue(row, attachment.getFileName());
getFileTypeColumn().setRowValue(row, attachment.getFileType());
getFileFlagsColumn().setRowValue(row, attachment.getFileFlags());
- getFileTimeStampColumn().setRowValue(row, attachment.getFileTimeStamp());
+ getFileTimeStampColumn().setRowValue(row, attachment.getFileTimeStampObject());
getFileDataColumn().setRowValue(row, attachment.getEncodedFileData());
return row;
}
@@ -198,7 +199,7 @@ public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl<Attachment>
public static Attachment newAttachment(
String url, String name, String type, byte[] data,
- Date timeStamp, Integer flags)
+ Object timeStamp, Integer flags)
{
return newAttachment(INVALID_FK, url, name, type, data,
timeStamp, flags);
@@ -206,7 +207,7 @@ public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl<Attachment>
public static Attachment newAttachment(
ComplexValueForeignKey complexValueFk, String url, String name,
- String type, byte[] data, Date timeStamp, Integer flags)
+ String type, byte[] data, Object timeStamp, Integer flags)
{
return new AttachmentImpl(INVALID_ID, complexValueFk, url, name, type,
data, timeStamp, flags, null);
@@ -224,7 +225,7 @@ public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl<Attachment>
public static Attachment newEncodedAttachment(
String url, String name, String type, byte[] encodedData,
- Date timeStamp, Integer flags)
+ Object timeStamp, Integer flags)
{
return newEncodedAttachment(INVALID_FK, url, name, type,
encodedData, timeStamp, flags);
@@ -232,13 +233,14 @@ public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl<Attachment>
public static Attachment newEncodedAttachment(
ComplexValueForeignKey complexValueFk, String url, String name,
- String type, byte[] encodedData, Date timeStamp, Integer flags)
+ String type, byte[] encodedData, Object timeStamp, Integer flags)
{
return new AttachmentImpl(INVALID_ID, complexValueFk, url, name, type,
null, timeStamp, flags, encodedData);
}
+ @SuppressWarnings("deprecation")
private static class AttachmentImpl extends ComplexValueImpl
implements Attachment
{
@@ -246,13 +248,13 @@ public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl<Attachment>
private String _name;
private String _type;
private byte[] _data;
- private Date _timeStamp;
+ private Object _timeStamp;
private Integer _flags;
private byte[] _encodedData;
private AttachmentImpl(Id id, ComplexValueForeignKey complexValueFk,
String url, String name, String type, byte[] data,
- Date timeStamp, Integer flags, byte[] encodedData)
+ Object timeStamp, Integer flags, byte[] encodedData)
{
super(id, complexValueFk);
_url = url;
@@ -313,13 +315,25 @@ public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl<Attachment>
}
public Date getFileTimeStamp() {
- return _timeStamp;
+ return (Date)_timeStamp;
}
public void setFileTimeStamp(Date fileTimeStamp) {
_timeStamp = fileTimeStamp;
}
+ public LocalDateTime getFileLocalTimeStamp() {
+ return (LocalDateTime)_timeStamp;
+ }
+
+ public void setFileLocalTimeStamp(LocalDateTime fileTimeStamp) {
+ _timeStamp = fileTimeStamp;
+ }
+
+ public Object getFileTimeStampObject() {
+ return _timeStamp;
+ }
+
public Integer getFileFlags() {
return _flags;
}
@@ -348,7 +362,7 @@ public class AttachmentColumnInfoImpl extends ComplexColumnInfoImpl<Attachment>
return "Attachment(" + getComplexValueForeignKey() + "," + getId() +
") " + getFileUrl() + ", " + getFileName() + ", " + getFileType()
- + ", " + getFileTimeStamp() + ", " + getFileFlags() + ", " +
+ + ", " + getFileTimeStampObject() + ", " + getFileFlags() + ", " +
dataStr;
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/complex/ComplexValueForeignKeyImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/complex/ComplexValueForeignKeyImpl.java
index a73d3ed..06c0cd7 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/complex/ComplexValueForeignKeyImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/complex/ComplexValueForeignKeyImpl.java
@@ -17,11 +17,14 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.complex;
import java.io.IOException;
+import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.healthmarketscience.jackcess.Column;
+import com.healthmarketscience.jackcess.Database;
+import com.healthmarketscience.jackcess.DateTimeType;
import com.healthmarketscience.jackcess.Row;
import com.healthmarketscience.jackcess.complex.Attachment;
import com.healthmarketscience.jackcess.complex.AttachmentColumnInfo;
@@ -50,14 +53,15 @@ import com.healthmarketscience.jackcess.complex.VersionHistoryColumnInfo;
*
* @author James Ahlborn
*/
+@SuppressWarnings("deprecation")
public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
{
- private static final long serialVersionUID = 20110805L;
-
+ private static final long serialVersionUID = 20110805L;
+
private transient final Column _column;
private final int _value;
private transient List<? extends ComplexValue> _values;
-
+
public ComplexValueForeignKeyImpl(Column column, int value) {
_column = column;
_value = value;
@@ -72,12 +76,12 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
public Column getColumn() {
return _column;
}
-
+
@Override
public ComplexDataType getComplexType() {
return getComplexInfo().getType();
}
-
+
protected ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
return _column.getComplexInfo();
}
@@ -85,7 +89,7 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
protected VersionHistoryColumnInfo getVersionInfo() {
return (VersionHistoryColumnInfo)getComplexInfo();
}
-
+
protected AttachmentColumnInfo getAttachmentInfo() {
return (AttachmentColumnInfo)getComplexInfo();
}
@@ -93,27 +97,27 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
protected MultiValueColumnInfo getMultiValueInfo() {
return (MultiValueColumnInfo)getComplexInfo();
}
-
+
protected UnsupportedColumnInfo getUnsupportedInfo() {
return (UnsupportedColumnInfo)getComplexInfo();
}
-
+
@Override
public int countValues() throws IOException {
return getComplexInfo().countValues(get());
}
-
+
public List<Row> getRawValues() throws IOException {
return getComplexInfo().getRawValues(get());
- }
-
+ }
+
@Override
public List<? extends ComplexValue> getValues() throws IOException {
if(_values == null) {
_values = getComplexInfo().getValues(this);
}
return _values;
- }
+ }
@Override
@SuppressWarnings("unchecked")
@@ -123,7 +127,7 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
}
return (List<Version>)getValues();
}
-
+
@Override
@SuppressWarnings("unchecked")
public List<Attachment> getAttachments() throws IOException {
@@ -132,7 +136,7 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
}
return (List<Attachment>)getValues();
}
-
+
@Override
@SuppressWarnings("unchecked")
public List<SingleValue> getMultiValues() throws IOException {
@@ -141,7 +145,7 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
}
return (List<SingleValue>)getValues();
}
-
+
@Override
@SuppressWarnings("unchecked")
public List<UnsupportedValue> getUnsupportedValues() throws IOException {
@@ -150,20 +154,29 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
}
return (List<UnsupportedValue>)getValues();
}
-
+
@Override
public void reset() {
// discard any cached values
_values = null;
}
-
+
@Override
public Version addVersion(String value) throws IOException {
- return addVersion(value, new Date());
+ return addVersionImpl(value, now());
}
-
+
@Override
public Version addVersion(String value, Date modifiedDate) throws IOException {
+ return addVersionImpl(value, modifiedDate);
+ }
+
+ @Override
+ public Version addVersion(String value, LocalDateTime modifiedDate) throws IOException {
+ return addVersionImpl(value, modifiedDate);
+ }
+
+ private Version addVersionImpl(String value, Object modifiedDate) throws IOException {
reset();
Version v = VersionHistoryColumnInfoImpl.newVersion(this, value, modifiedDate);
getVersionInfo().addValue(v);
@@ -172,15 +185,32 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
@Override
public Attachment addAttachment(byte[] data) throws IOException {
- return addAttachment(null, null, null, data, null, null);
+ return addAttachmentImpl(null, null, null, data, null, null);
}
-
+
@Override
public Attachment addAttachment(
String url, String name, String type, byte[] data,
Date timeStamp, Integer flags)
throws IOException
{
+ return addAttachmentImpl(url, name, type, data, timeStamp, flags);
+ }
+
+ @Override
+ public Attachment addAttachment(
+ String url, String name, String type, byte[] data,
+ LocalDateTime timeStamp, Integer flags)
+ throws IOException
+ {
+ return addAttachmentImpl(url, name, type, data, timeStamp, flags);
+ }
+
+ private Attachment addAttachmentImpl(
+ String url, String name, String type, byte[] data,
+ Object timeStamp, Integer flags)
+ throws IOException
+ {
reset();
Attachment a = AttachmentColumnInfoImpl.newAttachment(
this, url, name, type, data, timeStamp, flags);
@@ -192,36 +222,55 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
public Attachment addEncodedAttachment(byte[] encodedData)
throws IOException
{
- return addEncodedAttachment(null, null, null, encodedData, null, null);
+ return addEncodedAttachmentImpl(null, null, null, encodedData, null, null);
}
-
+
@Override
public Attachment addEncodedAttachment(
String url, String name, String type, byte[] encodedData,
Date timeStamp, Integer flags)
throws IOException
{
+ return addEncodedAttachmentImpl(url, name, type, encodedData, timeStamp,
+ flags);
+ }
+
+ @Override
+ public Attachment addEncodedAttachment(
+ String url, String name, String type, byte[] encodedData,
+ LocalDateTime timeStamp, Integer flags)
+ throws IOException
+ {
+ return addEncodedAttachmentImpl(url, name, type, encodedData, timeStamp,
+ flags);
+ }
+
+ private Attachment addEncodedAttachmentImpl(
+ String url, String name, String type, byte[] encodedData,
+ Object timeStamp, Integer flags)
+ throws IOException
+ {
reset();
Attachment a = AttachmentColumnInfoImpl.newEncodedAttachment(
this, url, name, type, encodedData, timeStamp, flags);
getAttachmentInfo().addValue(a);
return a;
}
-
+
@Override
public Attachment updateAttachment(Attachment attachment) throws IOException {
reset();
getAttachmentInfo().updateValue(attachment);
return attachment;
}
-
+
@Override
public Attachment deleteAttachment(Attachment attachment) throws IOException {
reset();
getAttachmentInfo().deleteValue(attachment);
return attachment;
}
-
+
@Override
public SingleValue addMultiValue(Object value) throws IOException {
reset();
@@ -229,21 +278,21 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
getMultiValueInfo().addValue(v);
return v;
}
-
+
@Override
public SingleValue updateMultiValue(SingleValue value) throws IOException {
reset();
getMultiValueInfo().updateValue(value);
return value;
}
-
+
@Override
public SingleValue deleteMultiValue(SingleValue value) throws IOException {
reset();
getMultiValueInfo().deleteValue(value);
return value;
}
-
+
@Override
public UnsupportedValue addUnsupportedValue(Map<String,?> values)
throws IOException
@@ -253,7 +302,7 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
getUnsupportedInfo().addValue(v);
return v;
}
-
+
@Override
public UnsupportedValue updateUnsupportedValue(UnsupportedValue value)
throws IOException
@@ -262,7 +311,7 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
getUnsupportedInfo().updateValue(value);
return value;
}
-
+
@Override
public UnsupportedValue deleteUnsupportedValue(UnsupportedValue value)
throws IOException
@@ -271,16 +320,24 @@ public class ComplexValueForeignKeyImpl extends ComplexValueForeignKey
getUnsupportedInfo().deleteValue(value);
return value;
}
-
+
@Override
public void deleteAllValues() throws IOException {
reset();
getComplexInfo().deleteAllValues(this);
}
-
+
@Override
public boolean equals(Object o) {
return(super.equals(o) &&
(_column == ((ComplexValueForeignKeyImpl)o)._column));
}
+
+ private Object now() {
+ Database db = getColumn().getDatabase();
+ if(db.getDateTimeType() == DateTimeType.DATE) {
+ return new Date();
+ }
+ return LocalDateTime.now(db.getZoneId());
+ }
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/complex/VersionHistoryColumnInfoImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/complex/VersionHistoryColumnInfoImpl.java
index a64788f..14667d0 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/complex/VersionHistoryColumnInfoImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/complex/VersionHistoryColumnInfoImpl.java
@@ -17,6 +17,7 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.complex;
import java.io.IOException;
+import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -42,14 +43,14 @@ import com.healthmarketscience.jackcess.impl.ColumnImpl;
*
* @author James Ahlborn
*/
-public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
+public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
implements VersionHistoryColumnInfo
{
private final Column _valueCol;
private final Column _modifiedCol;
-
+
public VersionHistoryColumnInfoImpl(Column column, int complexId,
- Table typeObjTable, Table flatTable)
+ Table typeObjTable, Table flatTable)
throws IOException
{
super(column, complexId, typeObjTable, flatTable);
@@ -83,7 +84,7 @@ public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
getValueColumn().getName());
((ColumnImpl)versionedCol).setVersionHistoryColumn((ColumnImpl)getColumn());
}
-
+
public Column getValueColumn() {
return _valueCol;
}
@@ -91,7 +92,7 @@ public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
public Column getModifiedDateColumn() {
return _modifiedCol;
}
-
+
@Override
public ComplexDataType getType() {
return ComplexDataType.VERSION_HISTORY;
@@ -124,7 +125,7 @@ public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
// order versions newest to oldest
Collections.sort(versions);
-
+
return versions;
}
@@ -133,7 +134,7 @@ public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
Row rawValue) {
ComplexValue.Id id = getValueId(rawValue);
String value = (String)getValueColumn().getRowValue(rawValue);
- Date modifiedDate = (Date)getModifiedDateColumn().getRowValue(rawValue);
+ Object modifiedDate = getModifiedDateColumn().getRowValue(rawValue);
return new VersionImpl(id, complexValueFk, value, modifiedDate);
}
@@ -142,47 +143,55 @@ public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
protected Object[] asRow(Object[] row, Version version) throws IOException {
super.asRow(row, version);
getValueColumn().setRowValue(row, version.getValue());
- getModifiedDateColumn().setRowValue(row, version.getModifiedDate());
+ getModifiedDateColumn().setRowValue(row, version.getModifiedDateObject());
return row;
}
-
- public static Version newVersion(String value, Date modifiedDate) {
+
+ public static Version newVersion(String value, Object modifiedDate) {
return newVersion(INVALID_FK, value, modifiedDate);
}
-
+
public static Version newVersion(ComplexValueForeignKey complexValueFk,
- String value, Date modifiedDate) {
+ String value, Object modifiedDate) {
return new VersionImpl(INVALID_ID, complexValueFk, value, modifiedDate);
}
-
+ @SuppressWarnings("deprecation")
private static class VersionImpl extends ComplexValueImpl implements Version
{
private final String _value;
- private final Date _modifiedDate;
+ private final Object _modifiedDate;
private VersionImpl(Id id, ComplexValueForeignKey complexValueFk,
- String value, Date modifiedDate)
+ String value, Object modifiedDate)
{
super(id, complexValueFk);
_value = value;
_modifiedDate = modifiedDate;
}
-
+
public String getValue() {
return _value;
}
public Date getModifiedDate() {
+ return (Date)_modifiedDate;
+ }
+
+ public LocalDateTime getModifiedLocalDate() {
+ return (LocalDateTime)_modifiedDate;
+ }
+
+ public Object getModifiedDateObject() {
return _modifiedDate;
- }
-
+ }
+
public int compareTo(Version o) {
- Date d1 = getModifiedDate();
- Date d2 = o.getModifiedDate();
+ Object d1 = getModifiedDateObject();
+ Object d2 = o.getModifiedDateObject();
// sort by descending date (newest/greatest first)
- int cmp = d2.compareTo(d1);
+ int cmp = compare(d2, d1);
if(cmp != 0) {
return cmp;
}
@@ -200,11 +209,20 @@ public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
((id1 < id2) ? 1 : 0));
}
+ @SuppressWarnings("unchecked")
+ private static <C extends Comparable<C>> int compare(Object o1, Object o2) {
+ // each date/time type (Date, LocalDateTime) is mutually Comparable, so
+ // just silence the compiler
+ C c1 = (C)o1;
+ C c2 = (C)o2;
+ return c1.compareTo(c2);
+ }
+
public void update() throws IOException {
throw new UnsupportedOperationException(
"This column does not support value updates");
}
-
+
public void delete() throws IOException {
throw new UnsupportedOperationException(
"This column does not support value deletes");
@@ -214,8 +232,8 @@ public class VersionHistoryColumnInfoImpl extends ComplexColumnInfoImpl<Version>
public String toString()
{
return "Version(" + getComplexValueForeignKey() + "," + getId() + ") " +
- getModifiedDate() + ", " + getValue();
- }
+ getModifiedDateObject() + ", " + getValue();
+ }
}
-
+
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseDelayedValue.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseDelayedValue.java
index d527c69..1e99e64 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseDelayedValue.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseDelayedValue.java
@@ -17,7 +17,7 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
-import java.util.Date;
+import java.time.LocalDateTime;
import com.healthmarketscience.jackcess.expr.LocaleContext;
import com.healthmarketscience.jackcess.expr.Value;
@@ -60,8 +60,8 @@ public abstract class BaseDelayedValue implements Value
return getDelegate().getAsString(ctx);
}
- public Date getAsDateTime(LocaleContext ctx) {
- return getDelegate().getAsDateTime(ctx);
+ public LocalDateTime getAsLocalDateTime(LocaleContext ctx) {
+ return getDelegate().getAsLocalDateTime(ctx);
}
public Value getAsDateTimeValue(LocaleContext ctx) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseNumericValue.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseNumericValue.java
index 299cd2a..cae689d 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseNumericValue.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseNumericValue.java
@@ -44,7 +44,7 @@ public abstract class BaseNumericValue extends BaseValue
@Override
public Value getAsDateTimeValue(LocaleContext ctx) {
Value dateValue = DefaultDateFunctions.numberToDateValue(
- ctx, getNumber().doubleValue());
+ getNumber().doubleValue());
if(dateValue == null) {
throw invalidConversion(Value.Type.DATE_TIME);
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseValue.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseValue.java
index 2b172d3..35e6ccf 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseValue.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseValue.java
@@ -17,11 +17,11 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
-import java.util.Date;
+import java.time.LocalDateTime;
-import com.healthmarketscience.jackcess.expr.Value;
import com.healthmarketscience.jackcess.expr.EvalException;
import com.healthmarketscience.jackcess.expr.LocaleContext;
+import com.healthmarketscience.jackcess.expr.Value;
/**
*
@@ -41,8 +41,8 @@ public abstract class BaseValue implements Value
throw invalidConversion(Type.STRING);
}
- public Date getAsDateTime(LocaleContext ctx) {
- return (Date)getAsDateTimeValue(ctx).get();
+ public LocalDateTime getAsLocalDateTime(LocaleContext ctx) {
+ return (LocalDateTime)getAsDateTimeValue(ctx).get();
}
public Value getAsDateTimeValue(LocaleContext ctx) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java
index 5131a93..5f63dad 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java
@@ -76,7 +76,7 @@ public class BuiltinOperators
case DATE_TIME:
// dates/times get converted to date doubles for arithmetic
double result = -param1.getAsDouble(ctx);
- return toDateValue(ctx, mathType, result);
+ return toDateValueIfPossible(mathType, result);
case LONG:
return toValue(-param1.getAsLongInt(ctx));
case DOUBLE:
@@ -108,7 +108,7 @@ public class BuiltinOperators
case DATE_TIME:
// dates/times get converted to date doubles for arithmetic
double result = param1.getAsDouble(ctx) + param2.getAsDouble(ctx);
- return toDateValue(ctx, mathType, result);
+ return toDateValueIfPossible(mathType, result);
case LONG:
return toValue(param1.getAsLongInt(ctx) + param2.getAsLongInt(ctx));
case DOUBLE:
@@ -138,7 +138,7 @@ public class BuiltinOperators
case DATE_TIME:
// dates/times get converted to date doubles for arithmetic
double result = param1.getAsDouble(ctx) - param2.getAsDouble(ctx);
- return toDateValue(ctx, mathType, result);
+ return toDateValueIfPossible(mathType, result);
case LONG:
return toValue(param1.getAsLongInt(ctx) - param2.getAsLongInt(ctx));
case DOUBLE:
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DateTimeValue.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DateTimeValue.java
index e2de36d..f9d1e7b 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DateTimeValue.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DateTimeValue.java
@@ -17,11 +17,11 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
-import java.util.Date;
+import java.time.LocalDateTime;
-import com.healthmarketscience.jackcess.impl.ColumnImpl;
import com.healthmarketscience.jackcess.expr.LocaleContext;
import com.healthmarketscience.jackcess.expr.Value;
+import com.healthmarketscience.jackcess.impl.ColumnImpl;
/**
*
@@ -30,9 +30,9 @@ import com.healthmarketscience.jackcess.expr.Value;
public class DateTimeValue extends BaseValue
{
private final Type _type;
- private final Date _val;
+ private final LocalDateTime _val;
- public DateTimeValue(Type type, Date val) {
+ public DateTimeValue(Type type, LocalDateTime val) {
if(!type.isTemporal()) {
throw new IllegalArgumentException("invalid date/time type");
}
@@ -49,7 +49,7 @@ public class DateTimeValue extends BaseValue
}
protected Double getNumber(LocaleContext ctx) {
- return ColumnImpl.toDateDouble(_val, ctx.getCalendar());
+ return ColumnImpl.toDateDouble(_val);
}
@Override
@@ -64,7 +64,7 @@ public class DateTimeValue extends BaseValue
}
@Override
- public Date getAsDateTime(LocaleContext ctx) {
+ public LocalDateTime getAsLocalDateTime(LocaleContext ctx) {
return _val;
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultDateFunctions.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultDateFunctions.java
index a19ab0a..2ac67d2 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultDateFunctions.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultDateFunctions.java
@@ -17,11 +17,20 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.expr;
-import java.math.BigDecimal;
-import java.text.DateFormat;
-import java.text.DateFormatSymbols;
-import java.util.Calendar;
-import java.util.Date;
+import java.time.DateTimeException;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.MonthDay;
+import java.time.Year;
+import java.time.format.DateTimeFormatter;
+import java.time.format.TextStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.WeekFields;
import com.healthmarketscience.jackcess.expr.EvalContext;
import com.healthmarketscience.jackcess.expr.EvalException;
@@ -44,12 +53,6 @@ public class DefaultDateFunctions
// max, valid, recognizable date: December 31, 9999 A.D. 23:59:59
private static final double MAX_DATE = 2958465.999988426d;
- private static final long SECONDS_PER_DAY = 24L * 60L * 60L;
- private static final double DSECONDS_PER_DAY = SECONDS_PER_DAY;
-
- private static final long SECONDS_PER_HOUR = 60L * 60L;
- private static final long SECONDS_PER_MINUTE = 60L;
-
private static final String INTV_YEAR = "yyyy";
private static final String INTV_QUARTER = "q";
private static final String INTV_MONTH = "m";
@@ -61,7 +64,8 @@ public class DefaultDateFunctions
private static final String INTV_MINUTE = "n";
private static final String INTV_SECOND = "s";
- private enum WeekOpType { GET_WEEK, GET_NUM_WEEKS }
+ private static final WeekFields SUNDAY_FIRST =
+ WeekFields.of(DayOfWeek.SUNDAY, 1);
private DefaultDateFunctions() {}
@@ -72,8 +76,7 @@ public class DefaultDateFunctions
public static final Function DATE = registerFunc(new Func0("Date") {
@Override
protected Value eval0(EvalContext ctx) {
- double dd = dateOnly(currentTimeDouble(ctx));
- return ValueSupport.toDateValue(ctx, Value.Type.DATE, dd);
+ return ValueSupport.toValue(LocalDate.now());
}
});
@@ -84,8 +87,7 @@ public class DefaultDateFunctions
if(dv.getType() == Value.Type.DATE) {
return dv;
}
- double dd = dateOnly(dv.getAsDouble(ctx));
- return ValueSupport.toDateValue(ctx, Value.Type.DATE, dd);
+ return ValueSupport.toValue(dv.getAsLocalDateTime(ctx).toLocalDate());
}
});
@@ -101,15 +103,11 @@ public class DefaultDateFunctions
year += ((year <= 29) ? 2000 : 1900);
}
- Calendar cal = ctx.getCalendar();
- cal.clear();
+ // we have to construct incrementatlly to handle out of range values
+ LocalDate ld = LocalDate.of(year,1,1).plusMonths(month - 1)
+ .plusDays(day - 1);
- cal.set(Calendar.YEAR, year);
- // convert to 0 based value
- cal.set(Calendar.MONTH, month - 1);
- cal.set(Calendar.DAY_OF_MONTH, day);
-
- return ValueSupport.toValue(Value.Type.DATE, cal.getTime());
+ return ValueSupport.toValue(ld);
}
});
@@ -127,27 +125,27 @@ public class DefaultDateFunctions
String intv = params[0].getAsString(ctx).trim();
int result = -1;
if(intv.equalsIgnoreCase(INTV_YEAR)) {
- result = nonNullToCalendarField(ctx, param2, Calendar.YEAR);
+ result = param2.getAsLocalDateTime(ctx).getYear();
} else if(intv.equalsIgnoreCase(INTV_QUARTER)) {
- result = getQuarter(nonNullToCalendar(ctx, param2));
+ result = getQuarter(param2.getAsLocalDateTime(ctx));
} else if(intv.equalsIgnoreCase(INTV_MONTH)) {
- // convert from 0 based to 1 based value
- result = nonNullToCalendarField(ctx, param2, Calendar.MONTH) + 1;
+ result = param2.getAsLocalDateTime(ctx).getMonthValue();
} else if(intv.equalsIgnoreCase(INTV_DAY_OF_YEAR)) {
- result = nonNullToCalendarField(ctx, param2, Calendar.DAY_OF_YEAR);
+ result = param2.getAsLocalDateTime(ctx).getDayOfYear();
} else if(intv.equalsIgnoreCase(INTV_DAY)) {
- result = nonNullToCalendarField(ctx, param2, Calendar.DAY_OF_MONTH);
+ result = param2.getAsLocalDateTime(ctx).getDayOfMonth();
} else if(intv.equalsIgnoreCase(INTV_WEEKDAY)) {
- int dayOfWeek = nonNullToCalendarField(ctx, param2, Calendar.DAY_OF_WEEK);
+ int dayOfWeek = param2.getAsLocalDateTime(ctx)
+ .get(SUNDAY_FIRST.dayOfWeek());
result = dayOfWeekToWeekDay(dayOfWeek, firstDay);
} else if(intv.equalsIgnoreCase(INTV_WEEK)) {
result = weekOfYear(ctx, param2, firstDay, firstWeekType);
} else if(intv.equalsIgnoreCase(INTV_HOUR)) {
- result = nonNullToCalendarField(ctx, param2, Calendar.HOUR_OF_DAY);
+ result = param2.getAsLocalDateTime(ctx).getHour();
} else if(intv.equalsIgnoreCase(INTV_MINUTE)) {
- result = nonNullToCalendarField(ctx, param2, Calendar.MINUTE);
+ result = param2.getAsLocalDateTime(ctx).getMinute();
} else if(intv.equalsIgnoreCase(INTV_SECOND)) {
- result = nonNullToCalendarField(ctx, param2, Calendar.SECOND);
+ result = param2.getAsLocalDateTime(ctx).getSecond();
} else {
throw new EvalException("Invalid interval " + intv);
}
@@ -167,33 +165,31 @@ public class DefaultDateFunctions
String intv = param1.getAsString(ctx).trim();
int val = param2.getAsLongInt(ctx);
- Calendar cal = nonNullToCalendar(ctx, param3);
+ LocalDateTime ldt = param3.getAsLocalDateTime(ctx);
if(intv.equalsIgnoreCase(INTV_YEAR)) {
- cal.add(Calendar.YEAR, val);
+ ldt = ldt.plus(val, ChronoUnit.YEARS);
} else if(intv.equalsIgnoreCase(INTV_QUARTER)) {
- cal.add(Calendar.MONTH, val * 3);
+ ldt = ldt.plus(val * 3, ChronoUnit.MONTHS);
} else if(intv.equalsIgnoreCase(INTV_MONTH)) {
- cal.add(Calendar.MONTH, val);
- } else if(intv.equalsIgnoreCase(INTV_DAY_OF_YEAR)) {
- cal.add(Calendar.DAY_OF_YEAR, val);
- } else if(intv.equalsIgnoreCase(INTV_DAY)) {
- cal.add(Calendar.DAY_OF_YEAR, val);
- } else if(intv.equalsIgnoreCase(INTV_WEEKDAY)) {
- cal.add(Calendar.DAY_OF_WEEK, val);
+ ldt = ldt.plus(val, ChronoUnit.MONTHS);
+ } else if(intv.equalsIgnoreCase(INTV_DAY_OF_YEAR) ||
+ intv.equalsIgnoreCase(INTV_DAY) ||
+ intv.equalsIgnoreCase(INTV_WEEKDAY)) {
+ ldt = ldt.plus(val, ChronoUnit.DAYS);
} else if(intv.equalsIgnoreCase(INTV_WEEK)) {
- cal.add(Calendar.WEEK_OF_YEAR, val);
+ ldt = ldt.plus(val, ChronoUnit.WEEKS);
} else if(intv.equalsIgnoreCase(INTV_HOUR)) {
- cal.add(Calendar.HOUR, val);
+ ldt = ldt.plus(val, ChronoUnit.HOURS);
} else if(intv.equalsIgnoreCase(INTV_MINUTE)) {
- cal.add(Calendar.MINUTE, val);
+ ldt = ldt.plus(val, ChronoUnit.MINUTES);
} else if(intv.equalsIgnoreCase(INTV_SECOND)) {
- cal.add(Calendar.SECOND, val);
+ ldt = ldt.plus(val, ChronoUnit.SECONDS);
} else {
throw new EvalException("Invalid interval " + intv);
}
- return ValueSupport.toValue(cal);
+ return ValueSupport.toValue(ldt);
}
});
@@ -212,14 +208,14 @@ public class DefaultDateFunctions
String intv = params[0].getAsString(ctx).trim();
- Calendar cal1 = nonNullToCalendar(ctx, param2);
- Calendar cal2 = nonNullToCalendar(ctx, param3);
+ LocalDateTime ldt1 = param2.getAsLocalDateTime(ctx);
+ LocalDateTime ldt2 = param3.getAsLocalDateTime(ctx);
int sign = 1;
- if(cal1.after(cal2)) {
- Calendar tmp = cal1;
- cal1 = cal2;
- cal2 = tmp;
+ if(ldt1.isAfter(ldt2)) {
+ LocalDateTime tmp = ldt1;
+ ldt1 = ldt2;
+ ldt2 = tmp;
sign = -1;
}
@@ -229,22 +225,22 @@ public class DefaultDateFunctions
int result = -1;
if(intv.equalsIgnoreCase(INTV_YEAR)) {
- result = cal2.get(Calendar.YEAR) - cal1.get(Calendar.YEAR);
+ result = ldt2.getYear() - ldt1.getYear();
} else if(intv.equalsIgnoreCase(INTV_QUARTER)) {
- int y1 = cal1.get(Calendar.YEAR);
- int q1 = getQuarter(cal1);
- int y2 = cal2.get(Calendar.YEAR);
- int q2 = getQuarter(cal2);
+ int y1 = ldt1.getYear();
+ int q1 = getQuarter(ldt1);
+ int y2 = ldt2.getYear();
+ int q2 = getQuarter(ldt2);
while(y2 > y1) {
q2 += 4;
--y2;
}
result = q2 - q1;
} else if(intv.equalsIgnoreCase(INTV_MONTH)) {
- int y1 = cal1.get(Calendar.YEAR);
- int m1 = cal1.get(Calendar.MONTH);
- int y2 = cal2.get(Calendar.YEAR);
- int m2 = cal2.get(Calendar.MONTH);
+ int y1 = ldt1.getYear();
+ int m1 = ldt1.getMonthValue();
+ int y2 = ldt2.getYear();
+ int m2 = ldt2.getMonthValue();
while(y2 > y1) {
m2 += 12;
--y2;
@@ -252,30 +248,30 @@ public class DefaultDateFunctions
result = m2 - m1;
} else if(intv.equalsIgnoreCase(INTV_DAY_OF_YEAR) ||
intv.equalsIgnoreCase(INTV_DAY)) {
- result = getDayDiff(cal1, cal2);
+ result = getDayDiff(ldt1, ldt2);
} else if(intv.equalsIgnoreCase(INTV_WEEKDAY)) {
// this calulates number of 7 day periods between two dates
- result = getDayDiff(cal1, cal2) / 7;
+ result = getDayDiff(ldt1, ldt2) / 7;
} else if(intv.equalsIgnoreCase(INTV_WEEK)) {
// this counts number of "week of year" intervals between two dates
- int w1 = weekOfYear(cal1, firstDay, firstWeekType);
- int y1 = getWeekOfYearYear(cal1, w1);
- int w2 = weekOfYear(cal2, firstDay, firstWeekType);
- int y2 = getWeekOfYearYear(cal2, w2);
+ WeekFields weekFields = weekFields(firstDay, firstWeekType);
+ int w1 = ldt1.get(weekFields.weekOfWeekBasedYear());
+ int y1 = ldt1.get(weekFields.weekBasedYear());
+ int w2 = ldt2.get(weekFields.weekOfWeekBasedYear());
+ int y2 = ldt2.get(weekFields.weekBasedYear());
while(y2 > y1) {
- cal2.add(Calendar.YEAR, -1);
- w2 += weeksInYear(cal2, firstDay, firstWeekType);
- y2 = cal2.get(Calendar.YEAR);
+ --y2;
+ w2 += weeksInYear(y2, weekFields);
}
result = w2 - w1;
} else if(intv.equalsIgnoreCase(INTV_HOUR)) {
- result = getHourDiff(cal1, cal2);
+ result = getHourDiff(ldt1, ldt2);
} else if(intv.equalsIgnoreCase(INTV_MINUTE)) {
- result = getMinuteDiff(cal1, cal2);
+ result = getMinuteDiff(ldt1, ldt2);
} else if(intv.equalsIgnoreCase(INTV_SECOND)) {
- int s1 = cal1.get(Calendar.SECOND);
- int s2 = cal2.get(Calendar.SECOND);
- int minuteDiff = getMinuteDiff(cal1, cal2);
+ int s1 = ldt1.getSecond();
+ int s2 = ldt2.getSecond();
+ int minuteDiff = getMinuteDiff(ldt1, ldt2);
result = (s2 + (60 * minuteDiff)) - s1;
} else {
throw new EvalException("Invalid interval " + intv);
@@ -288,15 +284,15 @@ public class DefaultDateFunctions
public static final Function NOW = registerFunc(new Func0("Now") {
@Override
protected Value eval0(EvalContext ctx) {
- return ValueSupport.toValue(Value.Type.DATE_TIME, new Date());
+ return ValueSupport.toValue(Value.Type.DATE_TIME,
+ LocalDateTime.now(ctx.getZoneId()));
}
});
public static final Function TIME = registerFunc(new Func0("Time") {
@Override
protected Value eval0(EvalContext ctx) {
- double dd = timeOnly(currentTimeDouble(ctx));
- return ValueSupport.toDateValue(ctx, Value.Type.TIME, dd);
+ return ValueSupport.toValue(LocalTime.now(ctx.getZoneId()));
}
});
@@ -307,15 +303,15 @@ public class DefaultDateFunctions
if(dv.getType() == Value.Type.TIME) {
return dv;
}
- double dd = timeOnly(dv.getAsDouble(ctx));
- return ValueSupport.toDateValue(ctx, Value.Type.TIME, dd);
+ return ValueSupport.toValue(dv.getAsLocalDateTime(ctx).toLocalTime());
}
});
public static final Function TIMER = registerFunc(new Func0("Timer") {
@Override
protected Value eval0(EvalContext ctx) {
- double dd = timeOnly(currentTimeDouble(ctx)) * DSECONDS_PER_DAY;
+ double dd = LocalTime.now(ctx.getZoneId())
+ .get(ChronoField.MILLI_OF_DAY) / 1000d;
return ValueSupport.toValue(dd);
}
});
@@ -327,59 +323,46 @@ public class DefaultDateFunctions
int minutes = param2.getAsLongInt(ctx);
int seconds = param3.getAsLongInt(ctx);
- long totalSeconds = (hours * SECONDS_PER_HOUR) +
- (minutes * SECONDS_PER_MINUTE) + seconds;
- if(totalSeconds < 0L) {
- do {
- totalSeconds += SECONDS_PER_DAY;
- } while(totalSeconds < 0L);
- } else if(totalSeconds > SECONDS_PER_DAY) {
- totalSeconds %= SECONDS_PER_DAY;
- }
+ // we have to construct incrementatlly to handle out of range values
+ LocalTime lt = ColumnImpl.BASE_LT.plusHours(hours).plusMinutes(minutes)
+ .plusSeconds(seconds);
- double dd = totalSeconds / DSECONDS_PER_DAY;
- return ValueSupport.toDateValue(ctx, Value.Type.TIME, dd);
+ return ValueSupport.toValue(lt);
}
});
public static final Function HOUR = registerFunc(new Func1NullIsNull("Hour") {
@Override
protected Value eval1(EvalContext ctx, Value param1) {
- return ValueSupport.toValue(
- nonNullToCalendarField(ctx, param1, Calendar.HOUR_OF_DAY));
+ return ValueSupport.toValue(param1.getAsLocalDateTime(ctx).getHour());
}
});
public static final Function MINUTE = registerFunc(new Func1NullIsNull("Minute") {
@Override
protected Value eval1(EvalContext ctx, Value param1) {
- return ValueSupport.toValue(
- nonNullToCalendarField(ctx, param1, Calendar.MINUTE));
+ return ValueSupport.toValue(param1.getAsLocalDateTime(ctx).getMinute());
}
});
public static final Function SECOND = registerFunc(new Func1NullIsNull("Second") {
@Override
protected Value eval1(EvalContext ctx, Value param1) {
- return ValueSupport.toValue(
- nonNullToCalendarField(ctx, param1, Calendar.SECOND));
+ return ValueSupport.toValue(param1.getAsLocalDateTime(ctx).getSecond());
}
});
public static final Function YEAR = registerFunc(new Func1NullIsNull("Year") {
@Override
protected Value eval1(EvalContext ctx, Value param1) {
- return ValueSupport.toValue(
- nonNullToCalendarField(ctx, param1, Calendar.YEAR));
+ return ValueSupport.toValue(param1.getAsLocalDateTime(ctx).getYear());
}
});
public static final Function MONTH = registerFunc(new Func1NullIsNull("Month") {
@Override
protected Value eval1(EvalContext ctx, Value param1) {
- // convert from 0 based to 1 based value
- return ValueSupport.toValue(
- nonNullToCalendarField(ctx, param1, Calendar.MONTH) + 1);
+ return ValueSupport.toValue(param1.getAsLocalDateTime(ctx).getMonthValue());
}
});
@@ -390,16 +373,12 @@ public class DefaultDateFunctions
if(param1.isNull()) {
return ValueSupport.NULL_VAL;
}
- // convert from 1 based to 0 based value
- int month = param1.getAsLongInt(ctx) - 1;
-
- boolean abbreviate = getOptionalBooleanParam(ctx, params, 1);
+ Month month = Month.of(param1.getAsLongInt(ctx));
- DateFormatSymbols syms = ctx.getTemporalConfig().getDateFormatSymbols();
- String[] monthNames = (abbreviate ?
- syms.getShortMonths() : syms.getMonths());
- // note, the array is 1 based
- return ValueSupport.toValue(monthNames[month]);
+ TextStyle textStyle = getTextStyle(ctx, params, 1);
+ String monthName = month.getDisplayName(
+ textStyle, ctx.getTemporalConfig().getLocale());
+ return ValueSupport.toValue(monthName);
}
});
@@ -407,7 +386,7 @@ public class DefaultDateFunctions
@Override
protected Value eval1(EvalContext ctx, Value param1) {
return ValueSupport.toValue(
- nonNullToCalendarField(ctx, param1, Calendar.DAY_OF_MONTH));
+ param1.getAsLocalDateTime(ctx).getDayOfMonth());
}
});
@@ -418,7 +397,8 @@ public class DefaultDateFunctions
if(param1.isNull()) {
return ValueSupport.NULL_VAL;
}
- int dayOfWeek = nonNullToCalendarField(ctx, param1, Calendar.DAY_OF_WEEK);
+ int dayOfWeek = param1.getAsLocalDateTime(ctx)
+ .get(SUNDAY_FIRST.dayOfWeek());
int firstDay = getFirstDayParam(ctx, params, 1);
@@ -435,31 +415,17 @@ public class DefaultDateFunctions
}
int weekday = param1.getAsLongInt(ctx);
- boolean abbreviate = getOptionalBooleanParam(ctx, params, 1);
+ TextStyle textStyle = getTextStyle(ctx, params, 1);
int firstDay = getFirstDayParam(ctx, params, 2);
int dayOfWeek = weekDayToDayOfWeek(weekday, firstDay);
-
- DateFormatSymbols syms = ctx.getTemporalConfig().getDateFormatSymbols();
- String[] weekdayNames = (abbreviate ?
- syms.getShortWeekdays() : syms.getWeekdays());
- // note, the array is 1 based
- return ValueSupport.toValue(weekdayNames[dayOfWeek]);
+ String weekdayName = dayOfWeek(dayOfWeek).getDisplayName(
+ textStyle, ctx.getTemporalConfig().getLocale());
+ return ValueSupport.toValue(weekdayName);
}
});
- private static int nonNullToCalendarField(EvalContext ctx, Value param,
- int field) {
- return nonNullToCalendar(ctx, param).get(field);
- }
-
- private static Calendar nonNullToCalendar(EvalContext ctx, Value param) {
- Calendar cal = ctx.getCalendar();
- cal.setTime(param.getAsDateTime(ctx));
- return cal;
- }
-
static Value stringToDateValue(LocaleContext ctx, String valStr) {
// see if we can coerce to date/time
TemporalConfig.Type valTempType = ExpressionTokenizer.determineDateType(
@@ -467,26 +433,31 @@ public class DefaultDateFunctions
if(valTempType != null) {
- DateFormat parseDf = ExpressionTokenizer.createParseDateTimeFormat(
- valTempType, ctx);
+ DateTimeFormatter parseDf = ctx.createDateFormatter(
+ ctx.getTemporalConfig().getDateTimeFormat(valTempType));
try {
- Date dateVal = ExpressionTokenizer.parseComplete(parseDf, valStr);
- return ValueSupport.toValue(valTempType.getValueType(), dateVal);
- } catch(java.text.ParseException pe) {
+ TemporalAccessor parsedInfo = parseDf.parse(valStr);
+ LocalDate ld = ColumnImpl.BASE_LD;
if(valTempType.includesDate()) {
- // the date may not include a year value, in which case it means
- // to use the "current" year. see if this is an implicit year date
- parseDf = ExpressionTokenizer.createParseImplicitYearDateTimeFormat(
- valTempType, ctx);
- try {
- Date dateVal = ExpressionTokenizer.parseComplete(parseDf, valStr);
- return ValueSupport.toValue(valTempType.getValueType(), dateVal);
- } catch(java.text.ParseException pe2) {
- // guess not, continue on to failure
+ // the year may not be explicitly specified
+ if(parsedInfo.isSupported(ChronoField.YEAR)) {
+ ld = LocalDate.from(parsedInfo);
+ } else {
+ ld = MonthDay.from(parsedInfo).atYear(
+ Year.now(ctx.getZoneId()).getValue());
}
}
+
+ LocalTime lt = ColumnImpl.BASE_LT;
+ if(valTempType.includesTime()) {
+ lt = LocalTime.from(parsedInfo);
+ }
+
+ return ValueSupport.toValue(LocalDateTime.of(ld, lt));
+ } catch(DateTimeException de) {
+ // note a valid date/time
}
}
@@ -494,35 +465,18 @@ public class DefaultDateFunctions
return null;
}
- static Value numberToDateValue(LocaleContext ctx, double dd) {
- if((dd < MIN_DATE) || (dd > MAX_DATE)) {
+ static boolean isValidDateDouble(double dd) {
+ return ((dd >= MIN_DATE) && (dd <= MAX_DATE));
+ }
+
+ static Value numberToDateValue(double dd) {
+ if(!isValidDateDouble(dd)) {
// outside valid date range
return null;
}
- boolean hasDate = (dateOnly(dd) != 0.0d);
- boolean hasTime = (timeOnly(dd) != 0.0d);
-
- Value.Type type = (hasDate ? (hasTime ? Value.Type.DATE_TIME :
- Value.Type.DATE) :
- Value.Type.TIME);
- return ValueSupport.toDateValue(ctx, type, dd);
- }
-
- private static double dateOnly(double dd) {
- // the integral part of the date/time double is the date value. discard
- // the fractional portion
- return (long)dd;
- }
-
- private static double timeOnly(double dd) {
- // the fractional part of the date/time double is the time value. discard
- // the integral portion
- return new BigDecimal(dd).remainder(BigDecimal.ONE).doubleValue();
- }
-
- private static double currentTimeDouble(LocaleContext ctx) {
- return ColumnImpl.toDateDouble(System.currentTimeMillis(), ctx.getCalendar());
+ LocalDateTime ldt = ColumnImpl.ldtFromLocalDateDouble(dd);
+ return ValueSupport.toValue(ldt);
}
private static int dayOfWeekToWeekDay(int day, int firstDay) {
@@ -551,114 +505,85 @@ public class DefaultDateFunctions
return getOptionalIntParam(ctx, params, idx, 1, 0);
}
- private static int weekOfYear(EvalContext ctx, Value param, int firstDay,
- int firstWeekType) {
- return doWeekOp(nonNullToCalendar(ctx, param), firstDay, firstWeekType,
- WeekOpType.GET_WEEK);
- }
+ private static WeekFields weekFields(int firstDay, int firstWeekType) {
+
+ int minDays = 1;
+ switch(firstWeekType) {
+ case 1:
+ // vbUseSystem 0
+ // vbFirstJan1 1 (default)
+ break;
+ case 2:
+ // vbFirstFourDays 2
+ minDays = 4;
+ break;
+ case 3:
+ // vbFirstFullWeek 3
+ minDays = 7;
+ break;
+ default:
+ throw new EvalException("Invalid first week of year type " +
+ firstWeekType);
+ }
- private static int weekOfYear(Calendar cal, int firstDay, int firstWeekType) {
- return doWeekOp(cal, firstDay, firstWeekType, WeekOpType.GET_WEEK);
+ return WeekFields.of(dayOfWeek(firstDay), minDays);
}
- private static int weeksInYear(Calendar cal, int firstDay, int firstWeekType) {
- return doWeekOp(cal, firstDay, firstWeekType, WeekOpType.GET_NUM_WEEKS);
+ private static DayOfWeek dayOfWeek(int dayOfWeek) {
+ return DayOfWeek.SUNDAY.plus(dayOfWeek - 1);
}
- private static int doWeekOp(Calendar cal, int firstDay, int firstWeekType,
- WeekOpType opType) {
- // need to mess with some calendar settings, but they need to be restored
- // when done because the Calendar instance may be shared
- int origFirstDay = cal.getFirstDayOfWeek();
- int origMinDays = cal.getMinimalDaysInFirstWeek();
- try {
-
- int minDays = 1;
- switch(firstWeekType) {
- case 1:
- // vbUseSystem 0
- // vbFirstJan1 1 (default)
- break;
- case 2:
- // vbFirstFourDays 2
- minDays = 4;
- break;
- case 3:
- // vbFirstFullWeek 3
- minDays = 7;
- break;
- default:
- throw new EvalException("Invalid first week of year type " +
- firstWeekType);
- }
-
- cal.setFirstDayOfWeek(firstDay);
- cal.setMinimalDaysInFirstWeek(minDays);
+ private static TextStyle getTextStyle(EvalContext ctx, Value[] params,
+ int idx) {
+ boolean abbreviate = getOptionalBooleanParam(ctx, params, 1);
+ return (abbreviate ? TextStyle.SHORT : TextStyle.FULL);
+ }
- switch(opType) {
- case GET_WEEK:
- return cal.get(Calendar.WEEK_OF_YEAR);
- case GET_NUM_WEEKS:
- return cal.getActualMaximum(Calendar.WEEK_OF_YEAR);
- default:
- throw new RuntimeException("Unknown op type " + opType);
- }
+ private static int weekOfYear(EvalContext ctx, Value param, int firstDay,
+ int firstWeekType) {
+ return weekOfYear(param.getAsLocalDateTime(ctx), firstDay, firstWeekType);
+ }
- } finally {
- cal.setFirstDayOfWeek(origFirstDay);
- cal.setMinimalDaysInFirstWeek(origMinDays);
- }
+ private static int weekOfYear(LocalDateTime ldt, int firstDay,
+ int firstWeekType) {
+ WeekFields weekFields = weekFields(firstDay, firstWeekType);
+ return ldt.get(weekFields.weekOfWeekBasedYear());
}
- private static int getQuarter(Calendar cal) {
- // month is 0 based
- int month = cal.get(Calendar.MONTH);
- return (month / 3) + 1;
+ private static int weeksInYear(int year, WeekFields weekFields) {
+ return (int)LocalDate.of(year,2,1).range(weekFields.weekOfWeekBasedYear())
+ .getMaximum();
}
- private static int getWeekOfYearYear(Calendar cal, int weekOfYear) {
- // the "week of year" gets weird at the beginning/end of the year.
- // e.g. 12/31 might be int the first week of the next year, and 1/1 might
- // be in the last week of the previous year. we need to detect this and
- // adjust the intervals accordingly
- if(cal.get(Calendar.MONTH) == Calendar.JANUARY) {
- if(weekOfYear >= 52) {
- // this week of year is effectively for the previous year
- cal.add(Calendar.YEAR, -1);
- }
- } else {
- if(weekOfYear == 1) {
- // this week of year is effectively for next year
- cal.add(Calendar.YEAR, 1);
- }
- }
- return cal.get(Calendar.YEAR);
+ private static int getQuarter(LocalDateTime ldt) {
+ int month = ldt.getMonthValue() - 1;
+ return (month / 3) + 1;
}
- private static int getDayDiff(Calendar cal1, Calendar cal2) {
- int y1 = cal1.get(Calendar.YEAR);
- int d1 = cal1.get(Calendar.DAY_OF_YEAR);
- int y2 = cal2.get(Calendar.YEAR);
- int d2 = cal2.get(Calendar.DAY_OF_YEAR);
- while(y2 > y1) {
- cal2.add(Calendar.YEAR, -1);
- d2 += cal2.getActualMaximum(Calendar.DAY_OF_YEAR);
- y2 = cal2.get(Calendar.YEAR);
+ private static int getDayDiff(LocalDateTime ldt1, LocalDateTime ldt2) {
+ int y1 = ldt1.getYear();
+ int d1 = ldt1.getDayOfYear();
+ int y2 = ldt2.getYear();
+ int d2 = ldt2.getDayOfYear();
+ while(y2 > y1) {
+ ldt2 = ldt2.minusYears(1);
+ d2 += ldt2.range(ChronoField.DAY_OF_YEAR).getMaximum();
+ y2 = ldt2.getYear();
}
return d2 - d1;
}
- private static int getHourDiff(Calendar cal1, Calendar cal2) {
- int h1 = cal1.get(Calendar.HOUR_OF_DAY);
- int h2 = cal2.get(Calendar.HOUR_OF_DAY);
- int dayDiff = getDayDiff(cal1, cal2);
+ private static int getHourDiff(LocalDateTime ldt1, LocalDateTime ldt2) {
+ int h1 = ldt1.getHour();
+ int h2 = ldt2.getHour();
+ int dayDiff = getDayDiff(ldt1, ldt2);
return (h2 + (24 * dayDiff)) - h1;
}
- private static int getMinuteDiff(Calendar cal1, Calendar cal2) {
- int m1 = cal1.get(Calendar.MINUTE);
- int m2 = cal2.get(Calendar.MINUTE);
- int hourDiff = getHourDiff(cal1, cal2);
+ private static int getMinuteDiff(LocalDateTime ldt1, LocalDateTime ldt2) {
+ int m1 = ldt1.getMinute();
+ int m2 = ldt2.getMinute();
+ int hourDiff = getHourDiff(ldt1, ldt2);
return (m2 + (60 * hourDiff)) - m1;
}
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java
index 29c0f71..763d1d5 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java
@@ -18,10 +18,9 @@ package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.text.DateFormat;
import java.text.DecimalFormat;
-import java.util.Calendar;
-import java.util.Date;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
@@ -310,16 +309,14 @@ public class DefaultFunctions
return ValueSupport.NULL_VAL;
}
- Date d = param1.getAsDateTime(ctx);
+ LocalDateTime ldt = param1.getAsLocalDateTime(ctx);
int fmtType = getOptionalIntParam(ctx, params, 1, 0);
TemporalConfig.Type tempType = null;
switch(fmtType) {
case 0:
// vbGeneralDate
- Calendar cal = ctx.getCalendar();
- cal.setTime(d);
- Value.Type valType = ValueSupport.getDateTimeType(cal);
+ Value.Type valType = ValueSupport.getDateTimeType(ldt);
switch(valType) {
case DATE:
tempType = TemporalConfig.Type.SHORT_DATE;
@@ -351,9 +348,9 @@ public class DefaultFunctions
throw new EvalException("Unknown format " + fmtType);
}
- DateFormat sdf = ctx.createDateFormat(
+ DateTimeFormatter dtf = ctx.createDateFormatter(
ctx.getTemporalConfig().getDateTimeFormat(tempType));
- return ValueSupport.toValue(sdf.format(d));
+ return ValueSupport.toValue(dtf.format(ldt));
}
});
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java
index 1ec08db..70eb5a9 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java
@@ -49,7 +49,7 @@ public class DefaultNumberFunctions
case DATE_TIME:
// dates/times get converted to date doubles for arithmetic
double result = Math.abs(param1.getAsDouble(ctx));
- return ValueSupport.toDateValue(ctx, mathType, result);
+ return ValueSupport.toDateValueIfPossible(mathType, result);
case LONG:
return ValueSupport.toValue(Math.abs(param1.getAsLongInt(ctx)));
case DOUBLE:
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 74856aa..77edf80 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultTextFunctions.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultTextFunctions.java
@@ -13,7 +13,6 @@ 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.impl.expr;
import java.math.BigDecimal;
@@ -23,7 +22,6 @@ import com.healthmarketscience.jackcess.expr.EvalException;
import com.healthmarketscience.jackcess.expr.Function;
import com.healthmarketscience.jackcess.expr.LocaleContext;
import com.healthmarketscience.jackcess.expr.Value;
-import org.apache.commons.lang.WordUtils;
import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*;
import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*;
@@ -360,6 +358,7 @@ public class DefaultTextFunctions
}
});
+ @SuppressWarnings("deprecation")
public static final Function STRCONV = registerStringFunc(new FuncVar("StrConv", 2, 3) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
@@ -387,7 +386,8 @@ public class DefaultTextFunctions
break;
case 3:
// vbProperCase
- str = WordUtils.capitalize(str.toLowerCase());
+ str = org.apache.commons.lang3.text.WordUtils.capitalize(
+ str.toLowerCase());
break;
default:
// do nothing
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java
index cc0fca4..4db436a 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java
@@ -17,26 +17,27 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
-import java.text.DateFormat;
-import java.text.FieldPosition;
-import java.text.ParsePosition;
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Date;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.TimeZone;
import static com.healthmarketscience.jackcess.impl.expr.Expressionator.*;
import com.healthmarketscience.jackcess.expr.LocaleContext;
import com.healthmarketscience.jackcess.expr.ParseException;
import com.healthmarketscience.jackcess.expr.TemporalConfig;
import com.healthmarketscience.jackcess.expr.Value;
+import com.healthmarketscience.jackcess.impl.ColumnImpl;
/**
@@ -53,15 +54,6 @@ class ExpressionTokenizer
private static final char DATE_LIT_QUOTE_CHAR = '#';
private static final char EQUALS_CHAR = '=';
- // access times are based on this date (not the UTC base)
- static final int BASE_DATE_YEAR = 1899;
- static final int BASE_DATE_MONTH = 12;
- static final int BASE_DATE_DAY = 30;
- private static final String BASE_DATE_PREFIX = "1899/12/30 ";
- private static final String BASE_DATE_FMT_PREFIX = "yyyy/M/d ";
-
- private static final String IMPLICIT_YEAR_FMT_PREFIX = "yyyy ";
-
private static final byte IS_OP_FLAG = 0x01;
private static final byte IS_COMP_FLAG = 0x02;
private static final byte IS_DELIM_FLAG = 0x04;
@@ -305,14 +297,26 @@ class ExpressionTokenizer
// note that although we may parse in the time "24" format, we will
// display as the default time format
- DateFormat parseDf = buf.getParseDateTimeFormat(type);
+ DateTimeFormatter parseDf = buf.getParseDateTimeFormat(type);
try {
- return new Token(TokenType.LITERAL, parseComplete(parseDf, dateStr),
+ TemporalAccessor parsedInfo = parseDf.parse(dateStr);
+
+ LocalDate ld = ColumnImpl.BASE_LD;
+ if(type.includesDate()) {
+ ld = LocalDate.from(parsedInfo);
+ }
+
+ LocalTime lt = ColumnImpl.BASE_LT;
+ if(type.includesTime()) {
+ lt = LocalTime.from(parsedInfo);
+ }
+
+ return new Token(TokenType.LITERAL, LocalDateTime.of(ld, lt),
dateStr, type.getValueType());
- } catch(java.text.ParseException pe) {
+ } catch(DateTimeException de) {
throw new ParseException(
- "Invalid date/time literal " + dateStr + " " + buf, pe);
+ "Invalid date/time literal " + dateStr + " " + buf, de);
}
}
@@ -325,7 +329,7 @@ class ExpressionTokenizer
boolean hasAmPm = false;
if(hasTime) {
- String[] amPmStrs = cfg.getDateFormatSymbols().getAmPmStrings();
+ String[] amPmStrs = cfg.getAmPmStrings();
String amStr = " " + amPmStrs[0];
String pmStr = " " + amPmStrs[1];
hasAmPm = (hasSuffix(dateStr, amStr) || hasSuffix(dateStr, pmStr));
@@ -352,23 +356,6 @@ class ExpressionTokenizer
suffStr, 0, suffStrLen));
}
- static DateFormat createParseDateTimeFormat(TemporalConfig.Type type,
- LocaleContext ctx)
- {
- if(type.isTimeOnly()) {
- return new ParseTimeFormat(type, ctx);
- }
-
- TemporalConfig cfg = ctx.getTemporalConfig();
- return ctx.createDateFormat(cfg.getDateTimeFormat(type));
- }
-
- static DateFormat createParseImplicitYearDateTimeFormat(
- TemporalConfig.Type type, LocaleContext ctx)
- {
- return new ParseImplicitYearFormat(type, ctx);
- }
-
private static Token maybeParseNumberLiteral(char firstChar, ExprBuf buf) {
StringBuilder sb = buf.getScratchBuffer().append(firstChar);
boolean hasDigit = isDigit(firstChar);
@@ -464,29 +451,14 @@ class ExpressionTokenizer
return new AbstractMap.SimpleImmutableEntry<K,V>(a, b);
}
- static Date parseComplete(DateFormat df, String str)
- throws java.text.ParseException
- {
- // the java parsers will parse "successfully" even if there is leftover
- // information. we only want to consider a parse operation successful if
- // it parses the entire string (ignoring surrounding whitespace)
- str = str.trim();
- ParsePosition pp = new ParsePosition(0);
- Object d = df.parse(str, pp);
- if(pp.getIndex() < str.length()) {
- throw new java.text.ParseException("Failed parsing '" + str + "'",
- pp.getIndex());
- }
- return (Date)d;
- }
-
private static final class ExprBuf
{
private final String _str;
private final ParseContext _ctx;
private int _pos;
- private final Map<TemporalConfig.Type,DateFormat> _dateTimeFmts =
- new EnumMap<TemporalConfig.Type,DateFormat>(TemporalConfig.Type.class);
+ private final Map<TemporalConfig.Type,DateTimeFormatter> _dateTimeFmts =
+ new EnumMap<TemporalConfig.Type,DateTimeFormatter>(
+ TemporalConfig.Type.class);
private final StringBuilder _scratch = new StringBuilder();
private ExprBuf(String str, ParseContext ctx) {
@@ -538,10 +510,11 @@ class ExpressionTokenizer
return _ctx;
}
- public DateFormat getParseDateTimeFormat(TemporalConfig.Type type) {
- DateFormat df = _dateTimeFmts.get(type);
+ public DateTimeFormatter getParseDateTimeFormat(TemporalConfig.Type type) {
+ DateTimeFormatter df = _dateTimeFmts.get(type);
if(df == null) {
- df = createParseDateTimeFormat(type, _ctx);
+ df = _ctx.createDateFormatter(
+ _ctx.getTemporalConfig().getDateTimeFormat(type));
_dateTimeFmts.put(type, df);
}
return df;
@@ -605,98 +578,4 @@ class ExpressionTokenizer
}
}
- /**
- * Base DateFormat implementation for parsing date/time formats where
- * additional information is added on to the format in order for it to be
- * parsed correctly.
- */
- private static abstract class ParsePrefixFormat extends DateFormat
- {
- private static final long serialVersionUID = 0L;
-
- private final DateFormat _parseDelegate;
-
- private ParsePrefixFormat(String formatPrefix, String formatStr,
- LocaleContext ctx) {
- _parseDelegate = ctx.createDateFormat(formatPrefix + formatStr);
- }
-
- @Override
- public StringBuffer format(Date date, StringBuffer toAppendTo,
- FieldPosition fieldPosition) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Date parse(String source, ParsePosition pos) {
- String prefix = getPrefix();
-
- Date result = _parseDelegate.parse(prefix + source, pos);
-
- // adjust index for original string
- pos.setIndex(pos.getIndex() - prefix.length());
-
- return result;
- }
-
- @Override
- public Calendar getCalendar() {
- return _parseDelegate.getCalendar();
- }
-
- @Override
- public TimeZone getTimeZone() {
- return _parseDelegate.getTimeZone();
- }
-
- protected abstract String getPrefix();
- }
-
- /**
- * Special date/time format which will parse time-only strings "correctly"
- * according to how access handles time-only values.
- */
- private static final class ParseTimeFormat extends ParsePrefixFormat
- {
- private static final long serialVersionUID = 0L;
-
- private ParseTimeFormat(TemporalConfig.Type timeType, LocaleContext ctx) {
- super(BASE_DATE_FMT_PREFIX,
- ctx.getTemporalConfig().getDateTimeFormat(timeType), ctx);
- }
-
- @Override
- protected String getPrefix() {
- // we parse as a full date/time in order to get the correct "base date"
- // used by access
- return BASE_DATE_PREFIX;
- }
- }
-
- /**
- * Special date/time format which will parse dates with implicit (current)
- * years.
- */
- private static final class ParseImplicitYearFormat extends ParsePrefixFormat
- {
- private static final long serialVersionUID = 0L;
-
- private ParseImplicitYearFormat(TemporalConfig.Type type,
- LocaleContext ctx) {
- super(IMPLICIT_YEAR_FMT_PREFIX,
- ctx.getTemporalConfig().getImplicitYearDateTimeFormat(type),
- ctx);
- }
-
- @Override
- protected String getPrefix() {
- // need to get the current year
- Calendar cal = getCalendar();
- cal.setTimeInMillis(System.currentTimeMillis());
- int year = cal.get(Calendar.YEAR);
- // return a value matching IMPLICIT_YEAR_FMT_PREFIX
- return year + " ";
- }
- }
-
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java
index d2bb847..92da3f6 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java
@@ -17,13 +17,11 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
@@ -43,11 +41,10 @@ import com.healthmarketscience.jackcess.expr.FunctionLookup;
import com.healthmarketscience.jackcess.expr.Identifier;
import com.healthmarketscience.jackcess.expr.LocaleContext;
import com.healthmarketscience.jackcess.expr.ParseException;
-import com.healthmarketscience.jackcess.expr.TemporalConfig;
import com.healthmarketscience.jackcess.expr.Value;
import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.Token;
import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.TokenType;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
/**
@@ -68,8 +65,6 @@ public class Expressionator
}
public interface ParseContext extends LocaleContext {
- public TemporalConfig getTemporalConfig();
- public SimpleDateFormat createDateFormat(String formatStr);
public FunctionLookup getFunctionLookup();
}
@@ -1318,7 +1313,7 @@ public class Expressionator
case DATE:
case TIME:
case DATE_TIME:
- return ValueSupport.toValue(valType, (Date)value);
+ return ValueSupport.toValue(valType, (LocalDateTime)value);
case LONG:
return ValueSupport.toValue((Integer)value);
case DOUBLE:
@@ -2081,7 +2076,7 @@ public class Expressionator
case DATE:
case TIME:
case DATE_TIME:
- return val.getAsDateTime(ctx);
+ return val.getAsLocalDateTime(ctx);
case LONG:
return val.getAsLongInt(ctx);
case DOUBLE:
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/FormatUtil.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/FormatUtil.java
index 2c475eb..a21cd88 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/FormatUtil.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/FormatUtil.java
@@ -17,9 +17,9 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
-import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
+import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
@@ -172,10 +172,10 @@ public class FormatUtil
@Override
public Value format(EvalContext ctx, Value expr, String fmtStr,
- int firstDay, int firstWeekType) {
- DateFormat sdf = ctx.createDateFormat(
+ int firstDay, int firstWeekType) {
+ DateTimeFormatter dtf = ctx.createDateFormatter(
ctx.getTemporalConfig().getDateTimeFormat(_type));
- return ValueSupport.toValue(sdf.format(expr.getAsDateTime(ctx)));
+ return ValueSupport.toValue(dtf.format(expr.getAsLocalDateTime(ctx)));
}
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java
index 19b22eb..ca74f35 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java
@@ -17,13 +17,12 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
-import java.math.BigInteger;
import java.text.DecimalFormatSymbols;
import com.healthmarketscience.jackcess.expr.EvalException;
import com.healthmarketscience.jackcess.expr.LocaleContext;
import com.healthmarketscience.jackcess.expr.Value;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
/**
*
@@ -84,7 +83,7 @@ public class StringValue extends BaseValue
// numberToDateValue may return null for out of range numbers)
try {
dateValue = DefaultDateFunctions.numberToDateValue(
- ctx, getNumber(ctx).doubleValue());
+ getNumber(ctx).doubleValue());
} catch(EvalException ignored) {
// not a number, not a date/time
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/ValueSupport.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/ValueSupport.java
index 3040920..805cc32 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/ValueSupport.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/ValueSupport.java
@@ -18,9 +18,10 @@ package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.text.DateFormat;
-import java.util.Calendar;
-import java.util.Date;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
import java.util.regex.Pattern;
import com.healthmarketscience.jackcess.expr.EvalException;
@@ -99,36 +100,42 @@ public class ValueSupport
return new BigDecimalValue(normalize(s));
}
- public static Value toDateValue(LocaleContext ctx, Value.Type type, double dd)
- {
- return toValue(type, new Date(
- ColumnImpl.fromDateDouble(dd, ctx.getCalendar())));
+ static Value toDateValueIfPossible(Value.Type dateType, double dd) {
+ if(DefaultDateFunctions.isValidDateDouble(dd)) {
+ return ValueSupport.toValue(
+ dateType, ColumnImpl.ldtFromLocalDateDouble(dd));
+ }
+ return ValueSupport.toValue(dd);
+ }
+
+ public static Value toValue(LocalDate ld) {
+ return new DateTimeValue(
+ Value.Type.DATE, LocalDateTime.of(ld, ColumnImpl.BASE_LT));
}
- public static Value toValue(Calendar cal) {
- return new DateTimeValue(getDateTimeType(cal), cal.getTime());
+ public static Value toValue(LocalTime lt) {
+ return new DateTimeValue(
+ Value.Type.TIME, LocalDateTime.of(ColumnImpl.BASE_LD, lt));
}
- public static Value.Type getDateTimeType(Calendar cal) {
- boolean hasTime = ((cal.get(Calendar.HOUR_OF_DAY) != 0) ||
- (cal.get(Calendar.MINUTE) != 0) ||
- (cal.get(Calendar.SECOND) != 0));
+ public static Value toValue(LocalDateTime ldt) {
+ return new DateTimeValue(getDateTimeType(ldt), ldt);
+ }
- boolean hasDate =
- ((cal.get(Calendar.YEAR) != ExpressionTokenizer.BASE_DATE_YEAR) ||
- ((cal.get(Calendar.MONTH) + 1) != ExpressionTokenizer.BASE_DATE_MONTH) ||
- (cal.get(Calendar.DAY_OF_MONTH) != ExpressionTokenizer.BASE_DATE_DAY));
+ public static Value.Type getDateTimeType(LocalDateTime ldt) {
+ boolean hasDate = !ColumnImpl.BASE_LD.equals(ldt.toLocalDate());
+ boolean hasTime = !ColumnImpl.BASE_LT.equals(ldt.toLocalTime());
return (hasDate ?
(hasTime ? Value.Type.DATE_TIME : Value.Type.DATE) :
Value.Type.TIME);
}
- public static Value toValue(Value.Type type, Date d) {
- return new DateTimeValue(type, d);
+ public static Value toValue(Value.Type type, LocalDateTime ldt) {
+ return new DateTimeValue(type, ldt);
}
- public static DateFormat getDateFormatForType(LocaleContext ctx, Value.Type type) {
+ public static DateTimeFormatter getDateFormatForType(LocaleContext ctx, Value.Type type) {
String fmtStr = null;
switch(type) {
case DATE:
@@ -143,7 +150,7 @@ public class ValueSupport
default:
throw new EvalException("Unexpected date/time type " + type);
}
- return ctx.createDateFormat(fmtStr);
+ return ctx.createDateFormatter(fmtStr);
}
/**
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryFormat.java b/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryFormat.java
index f9513d4..f0272f3 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryFormat.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryFormat.java
@@ -22,14 +22,13 @@ import java.util.regex.Pattern;
import com.healthmarketscience.jackcess.DataType;
import com.healthmarketscience.jackcess.query.Query;
-import org.apache.commons.lang.SystemUtils;
/**
* Constants used by the query data parsing.
- *
+ *
* @author James Ahlborn
*/
-public class QueryFormat
+public class QueryFormat
{
private QueryFormat() {}
@@ -90,7 +89,7 @@ public class QueryFormat
public static final short APPEND_VALUE_FLAG = (short)0x8000;
public static final short CROSSTAB_PIVOT_FLAG = 0x01;
- public static final short CROSSTAB_NORMAL_FLAG = 0x02;
+ public static final short CROSSTAB_NORMAL_FLAG = 0x02;
public static final String UNION_PART1 = "X7YZ_____1";
public static final String UNION_PART2 = "X7YZ_____2";
@@ -102,10 +101,10 @@ public class QueryFormat
public static final Pattern IDENTIFIER_SEP_PAT = Pattern.compile("\\.");
public static final char IDENTIFIER_SEP_CHAR = '.';
- public static final String NEWLINE = SystemUtils.LINE_SEPARATOR;
+ public static final String NEWLINE = System.lineSeparator();
- public static final Map<Short,String> PARAM_TYPE_MAP =
+ public static final Map<Short,String> PARAM_TYPE_MAP =
new HashMap<Short,String>();
static {
PARAM_TYPE_MAP.put((short)0, "Value");
@@ -123,7 +122,7 @@ public class QueryFormat
PARAM_TYPE_MAP.put((short)DataType.GUID.getValue(), "Guid");
}
- public static final Map<Short,String> JOIN_TYPE_MAP =
+ public static final Map<Short,String> JOIN_TYPE_MAP =
new HashMap<Short,String>();
static {
JOIN_TYPE_MAP.put((short)1, " INNER JOIN ");
@@ -131,7 +130,7 @@ public class QueryFormat
JOIN_TYPE_MAP.put((short)3, " RIGHT JOIN ");
}
- public static final Map<Short,Query.Type> TYPE_MAP =
+ public static final Map<Short,Query.Type> TYPE_MAP =
new HashMap<Short,Query.Type>();
static {
for(Query.Type type : Query.Type.values()) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java
index 6b51236..389ca90 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java
@@ -27,7 +27,7 @@ import com.healthmarketscience.jackcess.impl.RowIdImpl;
import com.healthmarketscience.jackcess.impl.RowImpl;
import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
import com.healthmarketscience.jackcess.query.Query;
-import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java b/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java
index fce5289..1dc0b80 100644
--- a/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java
+++ b/src/main/java/com/healthmarketscience/jackcess/util/CustomLinkResolver.java
@@ -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;
}
@@ -161,24 +162,26 @@ 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,
+ boolean inMemory, Path tempDir,
boolean readOnly)
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);
@@ -196,9 +199,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) {}
}
}
@@ -256,7 +262,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, boolean readOnly)
throws IOException
{
@@ -283,7 +289,7 @@ public abstract class CustomLinkResolver implements LinkResolver
try {
super.close();
} finally {
- deleteDbFile(getFile());
+ deleteDbFile(getPath());
closeCustomFile(_customFile);
}
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/MemFileChannel.java b/src/main/java/com/healthmarketscience/jackcess/util/MemFileChannel.java
index 528ddbb..4420938 100644
--- a/src/main/java/com/healthmarketscience/jackcess/util/MemFileChannel.java
+++ b/src/main/java/com/healthmarketscience/jackcess/util/MemFileChannel.java
@@ -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;
@@ -41,11 +44,11 @@ import com.healthmarketscience.jackcess.impl.DatabaseImpl;
* where disk usage may not be possible or desirable). Obviously, this
* requires enough jvm heap space to fit the file data. Use one of the
* {@code newChannel()} methods to construct an instance of this class.
- * <p/>
+ * <br>
* In order to use this class with a Database, you <i>must</i> use the {@link
* DatabaseBuilder} to open/create the Database instance, passing an instance
* of this class to the {@link DatabaseBuilder#setChannel} method.
- * <p/>
+ * <br>
* Implementation note: this class is optimized for use with {@link Database}.
* Therefore not all methods may be implemented and individual read/write
* operations are only supported within page boundaries.
@@ -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,13 +113,41 @@ 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);
@@ -120,10 +156,19 @@ public class MemFileChannel extends FileChannel
/**
* 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();
- }
+ }
}
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/OleBlob.java b/src/main/java/com/healthmarketscience/jackcess/util/OleBlob.java
index 8a775ad..4a85e13 100644
--- a/src/main/java/com/healthmarketscience/jackcess/util/OleBlob.java
+++ b/src/main/java/com/healthmarketscience/jackcess/util/OleBlob.java
@@ -34,7 +34,7 @@ import com.healthmarketscience.jackcess.impl.OleUtil;
* with nested filesystems!), and jackcess only supports a small portion of
* it. That said, jackcess should support the bulk of the common
* functionality.
- * <p/>
+ * <br>
* The main Blob methods will interact with the <i>entire</i> OLE field data
* which, in most cases, contains additional wrapper information. In order to
* access the ultimate "content" contained within the OLE data, the {@link
@@ -42,15 +42,15 @@ import com.healthmarketscience.jackcess.impl.OleUtil;
* variety of formats, so additional sub-interfaces are available to interact
* with it. The most specific sub-interface can be determined by the {@link
* ContentType} of the Content.
- * <p/>
+ * <br>
* Once an OleBlob is no longer useful, <i>it should be closed</i> using
* {@link #free} or {@link #close} methods (after which, the instance will no
* longer be functional).
- * <p/>
+ * <br>
* Note, the OleBlob implementation is read-only (through the interface). In
* order to modify blob contents, create a new OleBlob instance using {@link
* OleBlob.Builder} and write it to the access database.
- * <p/>
+ * <br>
* <b>Example for interpreting an existing OLE field:</b>
* <pre>
* OleBlob oleBlob = null;
@@ -66,7 +66,7 @@ import com.healthmarketscience.jackcess.impl.OleUtil;
* if(oleBlob != null) { oleBlob.close(); }
* }
* </pre>
- * <p/>
+ * <br>
* <b>Example for creating new, embedded ole data:</b>
* <pre>
* OleBlob oleBlob = null;
@@ -79,7 +79,7 @@ import com.healthmarketscience.jackcess.impl.OleUtil;
* if(oleBlob != null) { oleBlob.close(); }
* }
* </pre>
- * <p/>
+ * <br>
* <b>Example for creating new, linked ole data:</b>
* <pre>
* OleBlob oleBlob = null;
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/ReadOnlyFileChannel.java b/src/main/java/com/healthmarketscience/jackcess/util/ReadOnlyFileChannel.java
index 85620e8..0fb6f50 100644
--- a/src/main/java/com/healthmarketscience/jackcess/util/ReadOnlyFileChannel.java
+++ b/src/main/java/com/healthmarketscience/jackcess/util/ReadOnlyFileChannel.java
@@ -28,7 +28,7 @@ import com.healthmarketscience.jackcess.Database;
/**
* Wrapper for existing FileChannel which is read-only.
- * <p/>
+ * <br>
* Implementation note: this class is optimized for use with {@link Database}.
* Therefore not all methods may be implemented.
*
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/RowFilter.java b/src/main/java/com/healthmarketscience/jackcess/util/RowFilter.java
index 440d9dd..607bf9a 100644
--- a/src/main/java/com/healthmarketscience/jackcess/util/RowFilter.java
+++ b/src/main/java/com/healthmarketscience/jackcess/util/RowFilter.java
@@ -19,17 +19,17 @@ package com.healthmarketscience.jackcess.util;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Objects;
import com.healthmarketscience.jackcess.Column;
import com.healthmarketscience.jackcess.Row;
-import org.apache.commons.lang.ObjectUtils;
/**
* The RowFilter class encapsulates a filter test for a table row. This can
* be used by the {@link #apply(Iterable)} method to create an Iterable over a
* table which returns only rows matching some criteria.
- *
+ *
* @author Patricia Donaldson, Xerox Corporation
* @usage _general_class_
*/
@@ -59,22 +59,22 @@ public abstract class RowFilter
/**
* Creates a filter based on a row pattern.
- *
+ *
* @param rowPattern Map from column names to the values to be matched.
* A table row will match the target if
- * {@code ObjectUtils.equals(rowPattern.get(s), row.get(s))}
+ * {@code Objects.equals(rowPattern.get(s), row.get(s))}
* for all column names in the pattern map.
* @return a filter which matches table rows which match the values in the
* row pattern
*/
- public static RowFilter matchPattern(final Map<String,?> rowPattern)
+ public static RowFilter matchPattern(final Map<String,?> rowPattern)
{
return new RowFilter() {
@Override
- public boolean matches(Row row)
+ public boolean matches(Row row)
{
for(Map.Entry<String,?> e : rowPattern.entrySet()) {
- if(!ObjectUtils.equals(e.getValue(), row.get(e.getKey()))) {
+ if(!Objects.equals(e.getValue(), row.get(e.getKey()))) {
return false;
}
}
@@ -89,18 +89,18 @@ public abstract class RowFilter
* @param columnPattern column to be matched
* @param valuePattern value to be matched.
* A table row will match the target if
- * {@code ObjectUtils.equals(valuePattern, row.get(columnPattern.getName()))}.
+ * {@code Objects.equals(valuePattern, row.get(columnPattern.getName()))}.
* @return a filter which matches table rows which match the value in the
* row pattern
*/
- public static RowFilter matchPattern(final Column columnPattern,
- final Object valuePattern)
+ public static RowFilter matchPattern(final Column columnPattern,
+ final Object valuePattern)
{
return new RowFilter() {
@Override
- public boolean matches(Row row)
+ public boolean matches(Row row)
{
- return ObjectUtils.equals(valuePattern, columnPattern.getRowValue(row));
+ return Objects.equals(valuePattern, columnPattern.getRowValue(row));
}
};
}
@@ -118,7 +118,7 @@ public abstract class RowFilter
{
return new RowFilter() {
@Override
- public boolean matches(Row row)
+ public boolean matches(Row row)
{
return !filter.matches(row);
}
@@ -140,7 +140,7 @@ public abstract class RowFilter
public static Iterable<Row> apply(RowFilter rowFilter,
Iterable<? extends Row> iterable)
{
- return((rowFilter != null) ? rowFilter.apply(iterable) :
+ return((rowFilter != null) ? rowFilter.apply(iterable) :
(Iterable<Row>)iterable);
}
@@ -152,7 +152,7 @@ public abstract class RowFilter
{
private final Iterable<? extends Row> _iterable;
- private FilterIterable(Iterable<? extends Row> iterable)
+ private FilterIterable(Iterable<? extends Row> iterable)
{
_iterable = iterable;
}
@@ -163,7 +163,7 @@ public abstract class RowFilter
* iterable, returning only rows for which the {@link RowFilter#matches}
* method returns {@code true}
*/
- public Iterator<Row> iterator()
+ public Iterator<Row> iterator()
{
return new Iterator<Row>() {
private final Iterator<? extends Row> _iter = _iterable.iterator();
diff --git a/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnMatcher.java b/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnMatcher.java
index a564834..b814e93 100644
--- a/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnMatcher.java
+++ b/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnMatcher.java
@@ -19,12 +19,12 @@ package com.healthmarketscience.jackcess.util;
import java.io.IOException;
import java.util.Arrays;
+import java.util.Objects;
import com.healthmarketscience.jackcess.DataType;
import com.healthmarketscience.jackcess.Table;
import com.healthmarketscience.jackcess.impl.ColumnImpl;
import com.healthmarketscience.jackcess.impl.DatabaseImpl;
-import org.apache.commons.lang.ObjectUtils;
/**
* Simple concrete implementation of ColumnMatcher which tests for equality.
@@ -48,7 +48,7 @@ public class SimpleColumnMatcher implements ColumnMatcher {
return true;
}
- if((value1 != null) && (value2 != null) &&
+ if((value1 != null) && (value2 != null) &&
(value1.getClass() != value2.getClass())) {
// the values aren't the same type, try coercing them to "internal"
@@ -58,7 +58,7 @@ public class SimpleColumnMatcher implements ColumnMatcher {
DatabaseImpl db = (DatabaseImpl)table.getDatabase();
Object internalV1 = ColumnImpl.toInternalValue(dataType, value1, db);
Object internalV2 = ColumnImpl.toInternalValue(dataType, value2, db);
-
+
return equals(internalV1, internalV2);
} catch(IOException e) {
// ignored, just go with the original result
@@ -73,7 +73,7 @@ public class SimpleColumnMatcher implements ColumnMatcher {
*/
private static boolean equals(Object o1, Object o2)
{
- return (ObjectUtils.equals(o1, o2) ||
+ return (Objects.equals(o1, o2) ||
((o1 instanceof byte[]) && (o2 instanceof byte[]) &&
Arrays.equals((byte[])o1, (byte[])o2)));
}
diff --git a/src/main/resources/com/healthmarketscience/jackcess/log4j.properties b/src/main/resources/com/healthmarketscience/jackcess/log4j.properties
deleted file mode 100644
index 092468c..0000000
--- a/src/main/resources/com/healthmarketscience/jackcess/log4j.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-log4j.rootCategory=INFO, stdout
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=**** %-5p %d{MMM d HH:mm:ss} [%F] - %m%n
-
-log4j.category.com.healthmarketscience.jackcess=INFO
diff --git a/src/site/javadoc/taglets.properties b/src/site/javadoc/taglets.properties
deleted file mode 100644
index b24a1e3..0000000
--- a/src/site/javadoc/taglets.properties
+++ /dev/null
@@ -1,41 +0,0 @@
-# basic taglets config
-Taglets.splash=false
-Taglets.verbose=false
-Taglets.debug=false
-Taglets.drivers= drivers/j2se15.jar, drivers/j2se14.jar
-
-# custom usage formatting
-Taglets.shutdown.usage-tag= net.sourceforge.taglets.simple.shutdown.RegexReplacer
-Taglets.shutdown.usage-tag.files= **/*.html
-Taglets.shutdown.usage-tag.token.0=_general_method_
-Taglets.shutdown.usage-tag.value.0=<span class="UsageGeneral"><span class="UsageGeneralHeader">General</span>: This method is general use.</span>
-
-Taglets.shutdown.usage-tag.token.1=_intermediate_method_
-Taglets.shutdown.usage-tag.value.1=<span class="UsageIntermediate"><span class="UsageIntermediateHeader">Intermediate</span>: This method requires moderate API knowledge.</span>
-
-Taglets.shutdown.usage-tag.token.2=_advanced_method_
-Taglets.shutdown.usage-tag.value.2=<span class="UsageAdvanced"><span class="UsageAdvancedHeader">Advanced</span>: This method is for advanced/internal use.</span>
-
-Taglets.shutdown.usage-tag.token.3=_general_class_
-Taglets.shutdown.usage-tag.value.3=<span class="UsageGeneral"><span class="UsageGeneralHeader">General</span>: This class is general use.</span>
-
-Taglets.shutdown.usage-tag.token.4=_intermediate_class_
-Taglets.shutdown.usage-tag.value.4=<span class="UsageIntermediate"><span class="UsageIntermediateHeader">Intermediate</span>: This class requires moderate API knowledge.</span>
-
-Taglets.shutdown.usage-tag.token.5=_advanced_class_
-Taglets.shutdown.usage-tag.value.5=<span class="UsageAdvanced"><span class="UsageAdvancedHeader">Advanced</span>: This class is for advanced/internal use.</span>
-
-Taglets.shutdown.usage-tag.token.6=_general_field_
-Taglets.shutdown.usage-tag.value.6=<span class="UsageGeneral"><span class="UsageGeneralHeader">General</span>: This field is general use.</span>
-
-Taglets.shutdown.usage-tag.token.7=_intermediate_field_
-Taglets.shutdown.usage-tag.value.7=<span class="UsageIntermediate"><span class="UsageIntermediateHeader">Intermediate</span>: This field requires moderate API knowledge.</span>
-
-Taglets.shutdown.usage-tag.token.8=_advanced_field_
-Taglets.shutdown.usage-tag.value.8=<span class="UsageAdvanced"><span class="UsageAdvancedHeader">Advanced</span>: This field is for advanced/internal use.</span>
-
-
-# apparently we need one "normal" tag or the taglets code gets unhappy
-Taglets.taglet.todo= net.sourceforge.taglets.simple.block.ParamBlockTaglet
-Taglets.taglet.todo.dl.class= tagletsTodo
-Taglets.taglet.todo.dl.header= <b>Todo:</b>
diff --git a/src/test/java/com/healthmarketscience/jackcess/ComplexColumnTest.java b/src/test/java/com/healthmarketscience/jackcess/ComplexColumnTest.java
index b4704bf..c8b81a3 100644
--- a/src/test/java/com/healthmarketscience/jackcess/ComplexColumnTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/ComplexColumnTest.java
@@ -39,7 +39,8 @@ import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
*
* @author James Ahlborn
*/
-public class ComplexColumnTest extends TestCase
+@SuppressWarnings("deprecation")
+public class ComplexColumnTest extends TestCase
{
public ComplexColumnTest(String name) {
@@ -66,7 +67,7 @@ public class ComplexColumnTest extends TestCase
(ComplexValueForeignKey)verCol.getRowValue(row);
String curValue = (String)col.getRowValue(row);
-
+
if(rowId.equals("row1")) {
checkVersions(1, complexValueFk, curValue);
} else if(rowId.equals("row2")) {
@@ -94,7 +95,7 @@ public class ComplexColumnTest extends TestCase
Date upTime = new Date();
row8ValFk.addVersion("row8-memo", upTime);
checkVersions(row8ValFk.get(), row8ValFk, "row8-memo",
- "row8-memo", upTime);
+ "row8-memo", upTime);
Cursor cursor = CursorBuilder.createCursor(t1);
assertTrue(cursor.findFirstRow(t1.getColumn("id"), "row3"));
@@ -120,7 +121,7 @@ public class ComplexColumnTest extends TestCase
"row3-memo-again", new Date(1315876965382L),
"row3-memo-revised", new Date(1315876953077L),
"row3-memo", new Date(1315876879126L));
-
+
try {
v.delete();
fail("UnsupportedOperationException should have been thrown");
@@ -133,7 +134,7 @@ public class ComplexColumnTest extends TestCase
"row3-memo-again", new Date(1315876965382L),
"row3-memo-revised", new Date(1315876953077L),
"row3-memo", new Date(1315876879126L));
-
+
try {
v.getComplexValueForeignKey().deleteAllValues();
fail("UnsupportedOperationException should have been thrown");
@@ -146,7 +147,7 @@ public class ComplexColumnTest extends TestCase
"row3-memo-again", new Date(1315876965382L),
"row3-memo-revised", new Date(1315876953077L),
"row3-memo", new Date(1315876879126L));
-
+
db.close();
}
}
@@ -154,7 +155,7 @@ public class ComplexColumnTest extends TestCase
public void testAttachments() throws Exception
{
for(final TestDB testDB : TestDB.getSupportedForBasename(Basename.COMPLEX)) {
-
+
Database db = openCopy(testDB);
Table t1 = db.getTable("Table1");
@@ -187,12 +188,12 @@ public class ComplexColumnTest extends TestCase
ComplexValueForeignKey row8ValFk = (ComplexValueForeignKey)
col.getRowValue(row8);
row8ValFk.addAttachment(null, "test_data.txt", "txt",
- getFileBytes("test_data.txt"), null, null);
+ getFileBytes("test_data.txt"), (Date)null, null);
checkAttachments(row8ValFk.get(), row8ValFk, "test_data.txt");
row8ValFk.addEncodedAttachment(null, "test_data2.txt", "txt",
- getEncodedFileBytes("test_data2.txt"), null,
- null);
- checkAttachments(row8ValFk.get(), row8ValFk, "test_data.txt",
+ getEncodedFileBytes("test_data2.txt"),
+ (Date)null, null);
+ checkAttachments(row8ValFk.get(), row8ValFk, "test_data.txt",
"test_data2.txt");
Cursor cursor = CursorBuilder.createCursor(t1);
@@ -200,8 +201,8 @@ public class ComplexColumnTest extends TestCase
ComplexValueForeignKey row4ValFk = (ComplexValueForeignKey)
cursor.getCurrentRowValue(col);
Attachment a = row4ValFk.addAttachment(null, "test_data.txt", "txt",
- getFileBytes("test_data.txt"), null,
- null);
+ getFileBytes("test_data.txt"),
+ (Date)null, null);
checkAttachments(4, row4ValFk, "test_data2.txt", "test_data.txt");
a.setFileType("zip");
@@ -230,8 +231,8 @@ public class ComplexColumnTest extends TestCase
ComplexValueForeignKey row2ValFk = (ComplexValueForeignKey)
cursor.getCurrentRowValue(col);
row2ValFk.deleteAllValues();
- checkAttachments(2, row2ValFk);
-
+ checkAttachments(2, row2ValFk);
+
db.close();
}
}
@@ -239,7 +240,7 @@ public class ComplexColumnTest extends TestCase
public void testMultiValues() throws Exception
{
for(final TestDB testDB : TestDB.getSupportedForBasename(Basename.COMPLEX)) {
-
+
Database db = openCopy(testDB);
Table t1 = db.getTable("Table1");
@@ -264,7 +265,7 @@ public class ComplexColumnTest extends TestCase
} else {
assertTrue(false);
}
- }
+ }
Object[] row8 = {"row8", Column.AUTO_NUMBER, "some-data", "row8-memo",
Column.AUTO_NUMBER, Column.AUTO_NUMBER};
@@ -307,17 +308,17 @@ public class ComplexColumnTest extends TestCase
PropertyMap props = col.getProperties();
assertEquals(Boolean.TRUE, props.getValue(PropertyMap.ALLOW_MULTI_VALUE_PROP));
assertEquals("Value List", props.getValue(PropertyMap.ROW_SOURCE_TYPE_PROP));
- assertEquals("\"value1\";\"value2\";\"value3\";\"value4\"",
+ assertEquals("\"value1\";\"value2\";\"value3\";\"value4\"",
props.getValue(PropertyMap.ROW_SOURCE_PROP));
-
+
db.close();
}
}
-
+
public void testUnsupported() throws Exception
{
for(final TestDB testDB : TestDB.getSupportedForBasename(Basename.UNSUPPORTED)) {
-
+
Database db = openCopy(testDB);
Table t1 = db.getTable("Test");
@@ -331,7 +332,7 @@ public class ComplexColumnTest extends TestCase
(ComplexValueForeignKey)col.getRowValue(row);
if(rowId.equals(1)) {
- checkUnsupportedValues(1, complexValueFk,
+ checkUnsupportedValues(1, complexValueFk,
"RawData[(5) FF FE 62 61 7A]");
} else if(rowId.equals(2)) {
checkUnsupportedValues(2, complexValueFk, "RawData[(5) FF FE 66 6F 6F]", "RawData[(5) FF FE 62 61 7A]");
@@ -340,12 +341,12 @@ public class ComplexColumnTest extends TestCase
} else {
assertTrue(false);
}
- }
-
+ }
+
db.close();
}
}
-
+
private static void checkVersions(
int cValId, ComplexValueForeignKey complexValueFk,
String curValue, Object... versionInfos)
@@ -376,7 +377,7 @@ public class ComplexColumnTest extends TestCase
throws Exception
{
assertEquals(cValId, complexValueFk.get());
-
+
List<Attachment> attachments = complexValueFk.getAttachments();
if(fileNames.length == 0) {
assertTrue(attachments.isEmpty());
@@ -388,12 +389,12 @@ public class ComplexColumnTest extends TestCase
assertEquals(fname, a.getFileName());
assertEquals("txt", a.getFileType());
assertTrue(Arrays.equals(getFileBytes(fname), a.getFileData()));
- assertTrue(Arrays.equals(getEncodedFileBytes(fname),
+ assertTrue(Arrays.equals(getEncodedFileBytes(fname),
a.getEncodedFileData()));
}
}
}
-
+
private static void checkMultiValues(
int cValId, ComplexValueForeignKey complexValueFk,
Object... expectedValues)
@@ -411,7 +412,7 @@ public class ComplexColumnTest extends TestCase
SingleValue v = values.get(i);
assertEquals(value, v.get());
}
- }
+ }
}
private static void checkUnsupportedValues(
@@ -434,7 +435,7 @@ public class ComplexColumnTest extends TestCase
assertTrue(ColumnImpl.isRawData(rv));
assertEquals(value, rv.toString());
}
- }
+ }
}
private static byte[] getFileBytes(String fname) throws Exception
@@ -447,7 +448,7 @@ public class ComplexColumnTest extends TestCase
}
throw new RuntimeException("unexpected bytes");
}
-
+
private static byte[] getEncodedFileBytes(String fname) throws Exception
{
if("test_data.txt".equals(fname)) {
@@ -458,9 +459,9 @@ public class ComplexColumnTest extends TestCase
}
throw new RuntimeException("unexpected bytes");
}
-
+
private static byte b(int i) { return (byte)i; }
-
+
private static byte[] getAsciiBytes(String str) {
try {
return str.getBytes("US-ASCII");
@@ -468,7 +469,7 @@ public class ComplexColumnTest extends TestCase
throw new RuntimeException(e);
}
}
-
+
private static final byte[] TEST_ENC_BYTES = new byte[] {
b(0x01),b(0x00),b(0x00),b(0x00),b(0x3A),b(0x00),b(0x00),b(0x00),b(0x78),b(0x5E),b(0x13),b(0x61),b(0x60),b(0x60),b(0x60),b(0x04),b(0x62),b(0x16),b(0x20),b(0x2E),b(0x61),b(0xA8),b(0x00),b(0x62),
b(0x20),b(0x9D),b(0x91),b(0x59),b(0xAC),b(0x00),b(0x44),b(0xC5),b(0xF9),b(0xB9),b(0xA9),b(0x0A),b(0x25),b(0xA9),b(0xC5),b(0x25),b(0x0A),b(0x29),b(0x89),b(0x25),b(0x89),b(0x0A),b(0x69),b(0xF9),
@@ -476,7 +477,7 @@ public class ComplexColumnTest extends TestCase
};
private static final byte[] TEST_BYTES = getAsciiBytes("this is some test data for attachment.");
-
+
private static final byte[] TEST2_ENC_BYTES = new byte[] {
b(0x01),b(0x00),b(0x00),b(0x00),b(0x3F),b(0x00),b(0x00),b(0x00),b(0x78),b(0x5E),b(0x13),b(0x61),b(0x60),b(0x60),b(0x60),b(0x04),b(0x62),b(0x16),b(0x20),b(0x2E),b(0x61),b(0xA8),b(0x00),b(0x62),
b(0x20),b(0x9D),b(0x91),b(0x59),b(0xAC),b(0x00),b(0x44),b(0xC5),b(0xF9),b(0xB9),b(0xA9),b(0x0A),b(0xB9),b(0xF9),b(0x45),b(0xA9),b(0x0A),b(0x25),b(0xA9),b(0xC5),b(0x25),b(0x0A),b(0x29),b(0x89),
@@ -484,5 +485,5 @@ public class ComplexColumnTest extends TestCase
};
private static final byte[] TEST2_BYTES = getAsciiBytes("this is some more test data for attachment.");
-
+
}
diff --git a/src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java b/src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java
index 025e180..1aa2483 100644
--- a/src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/DatabaseTest.java
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -51,7 +52,8 @@ import static com.healthmarketscience.jackcess.TestUtil.*;
/**
* @author Tim McCune
*/
-public class DatabaseTest extends TestCase
+@SuppressWarnings("deprecation")
+public class DatabaseTest extends TestCase
{
public DatabaseTest(String name) throws Exception {
super(name);
@@ -114,7 +116,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 +124,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 +145,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 +155,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 +185,7 @@ public class DatabaseTest extends TestCase
db.close();
}
}
-
+
public void testDeleteCurrentRow() throws Exception {
// make sure correct row is deleted
@@ -269,7 +271,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 +284,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 +328,7 @@ public class DatabaseTest extends TestCase
}
rowNum++;
}
-
+
table.getDatabase().close();
}
}
@@ -508,7 +510,7 @@ public class DatabaseTest extends TestCase
db.close();
}
- }
+ }
public void testMultiPageTableDef() throws Exception
{
@@ -580,7 +582,7 @@ public class DatabaseTest extends TestCase
db.close();
}
- }
+ }
public void testLargeTableDef() throws Exception {
@@ -685,8 +687,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 +705,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 +743,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 +768,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 +784,7 @@ public class DatabaseTest extends TestCase
assertEquals("MSysObjects", tmd.getName());
assertFalse(tmd.isLinked());
assertTrue(tmd.isSystem());
-
+
db.close();
}
}
@@ -839,7 +841,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 +893,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 +903,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 +932,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 +944,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 +978,7 @@ public class DatabaseTest extends TestCase
}
return tableList;
}
-
+
public void testTimeZone() throws Exception
{
TimeZone tz = TimeZone.getTimeZone("America/New_York");
@@ -989,7 +992,9 @@ public class DatabaseTest extends TestCase
{
ColumnImpl col = new ColumnImpl(null, null, DataType.SHORT_DATE_TIME, 0, 0, 0) {
@Override
- protected Calendar getCalendar() { return Calendar.getInstance(tz); }
+ public TimeZone getTimeZone() { return tz; }
+ @Override
+ public ZoneId getZoneId() { return null; }
};
SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd");
diff --git a/src/test/java/com/healthmarketscience/jackcess/LocalDateTimeTest.java b/src/test/java/com/healthmarketscience/jackcess/LocalDateTimeTest.java
new file mode 100644
index 0000000..637629a
--- /dev/null
+++ b/src/test/java/com/healthmarketscience/jackcess/LocalDateTimeTest.java
@@ -0,0 +1,160 @@
+/*
+Copyright (c) 2018 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;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import com.healthmarketscience.jackcess.impl.ColumnImpl;
+import com.healthmarketscience.jackcess.impl.DatabaseImpl;
+import com.healthmarketscience.jackcess.impl.RowIdImpl;
+import com.healthmarketscience.jackcess.impl.RowImpl;
+import com.healthmarketscience.jackcess.impl.TableImpl;
+import com.healthmarketscience.jackcess.util.LinkResolver;
+import junit.framework.TestCase;
+import static com.healthmarketscience.jackcess.TestUtil.*;
+import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
+import static com.healthmarketscience.jackcess.Database.*;
+
+/**
+ *
+ * @author James Ahlborn
+ */
+public class LocalDateTimeTest extends TestCase
+{
+ public LocalDateTimeTest(String name) throws Exception {
+ super(name);
+ }
+
+ public void testAncientDates() throws Exception
+ {
+ ZoneId zoneId = ZoneId.of("America/New_York");
+ DateTimeFormatter sdf = DateTimeFormatter.ofPattern("uuuu-MM-dd");
+
+ List<String> dates = Arrays.asList("1582-10-15", "1582-10-14",
+ "1492-01-10", "1392-01-10");
+
+ for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
+ Database db = createMem(fileFormat);
+ db.setZoneId(zoneId);
+ db.setDateTimeType(DateTimeType.LOCAL_DATE_TIME);
+
+ Table table = new TableBuilder("test")
+ .addColumn(new ColumnBuilder("name", DataType.TEXT))
+ .addColumn(new ColumnBuilder("date", DataType.SHORT_DATE_TIME))
+ .toTable(db);
+
+ for(String dateStr : dates) {
+ LocalDate ld = LocalDate.parse(dateStr, sdf);
+ table.addRow("row " + dateStr, ld);
+ }
+
+ List<String> foundDates = new ArrayList<String>();
+ for(Row row : table) {
+ foundDates.add(sdf.format(row.getLocalDateTime("date")));
+ }
+
+ assertEquals(dates, foundDates);
+
+ db.close();
+ }
+
+ for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.OLD_DATES)) {
+ Database db = openCopy(testDB);
+ db.setDateTimeType(DateTimeType.LOCAL_DATE_TIME);
+
+ Table t = db.getTable("Table1");
+
+ List<String> foundDates = new ArrayList<String>();
+ for(Row row : t) {
+ foundDates.add(sdf.format(row.getLocalDateTime("DateField")));
+ }
+
+ assertEquals(dates, foundDates);
+
+ db.close();
+ }
+
+ }
+
+ public void testZoneId() throws Exception
+ {
+ ZoneId zoneId = ZoneId.of("America/New_York");
+ doTestZoneId(zoneId);
+
+ zoneId = ZoneId.of("Australia/Sydney");
+ doTestZoneId(zoneId);
+ }
+
+ private static void doTestZoneId(final ZoneId zoneId) throws Exception
+ {
+ final TimeZone tz = TimeZone.getTimeZone(zoneId);
+ ColumnImpl col = new ColumnImpl(null, null, DataType.SHORT_DATE_TIME, 0, 0, 0) {
+ @Override
+ public TimeZone getTimeZone() { return tz; }
+ @Override
+ public ZoneId getZoneId() { return zoneId; }
+ };
+
+ SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd");
+ df.setTimeZone(tz);
+
+ long startDate = df.parse("2012.01.01").getTime();
+ long endDate = df.parse("2013.01.01").getTime();
+
+ Calendar curCal = Calendar.getInstance(tz);
+ curCal.setTimeInMillis(startDate);
+
+ DateTimeFormatter sdf = DateTimeFormatter.ofPattern("uuuu.MM.dd HH:mm:ss");
+
+ while(curCal.getTimeInMillis() < endDate) {
+ Date curDate = curCal.getTime();
+ LocalDateTime curLdt = LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(curDate.getTime()), zoneId);
+ LocalDateTime newLdt = ColumnImpl.ldtFromLocalDateDouble(
+ col.toDateDouble(curDate));
+ if(!curLdt.equals(newLdt)) {
+ System.out.println("FOO " + curLdt + " " + newLdt);
+ assertEquals(sdf.format(curLdt), sdf.format(newLdt));
+ }
+ curCal.add(Calendar.MINUTE, 30);
+ }
+ }
+
+}
diff --git a/src/test/java/com/healthmarketscience/jackcess/PropertyExpressionTest.java b/src/test/java/com/healthmarketscience/jackcess/PropertyExpressionTest.java
index 384386e..bb26719 100644
--- a/src/test/java/com/healthmarketscience/jackcess/PropertyExpressionTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/PropertyExpressionTest.java
@@ -294,8 +294,8 @@ public class PropertyExpressionTest extends TestCase
public static void testCustomEvalConfig() throws Exception
{
- TemporalConfig tempConf = new TemporalConfig("yyyy/M/d", "M/d",
- "yyyy-MMM-d",
+ TemporalConfig tempConf = new TemporalConfig("[uuuu/]M/d",
+ "uuuu-MMM-d",
"hh.mm.ss a",
"HH.mm.ss", '/', '.',
Locale.US);
diff --git a/src/test/java/com/healthmarketscience/jackcess/TableTest.java b/src/test/java/com/healthmarketscience/jackcess/TableTest.java
index 339ba39..eaae617 100644
--- a/src/test/java/com/healthmarketscience/jackcess/TableTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/TableTest.java
@@ -21,8 +21,8 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.List;
+import java.util.TimeZone;
import com.healthmarketscience.jackcess.impl.ColumnImpl;
import com.healthmarketscience.jackcess.impl.JetFormat;
@@ -40,8 +40,8 @@ public class TableTest extends TestCase {
private TestTable _testTable;
private int _varLenIdx;
private int _fixedOffset;
-
-
+
+
public TableTest(String name) {
super(name);
}
@@ -52,14 +52,14 @@ public class TableTest extends TestCase {
_varLenIdx = 0;
_fixedOffset = 0;
}
-
+
public void testCreateRow() throws Exception {
reset();
newTestColumn(DataType.INT, false);
newTestColumn(DataType.TEXT, false);
newTestColumn(DataType.TEXT, false);
newTestTable();
-
+
int colCount = _columns.size();
ByteBuffer buffer = createRow(9, "Tim", "McCune");
@@ -91,10 +91,10 @@ public class TableTest extends TestCase {
newTestColumn(DataType.TEXT, true);
newTestColumn(DataType.TEXT, true);
newTestTable();
-
+
ByteBuffer[] bufCmp1 = encodeColumns(small, large);
ByteBuffer[] bufCmp2 = encodeColumns(smallNotAscii, largeNotAscii);
-
+
assertEquals(buf1[0].remaining(),
(bufCmp1[0].remaining() + small.length() - 2));
assertEquals(buf1[1].remaining(),
@@ -111,7 +111,7 @@ public class TableTest extends TestCase {
}
- private ByteBuffer createRow(Object... row)
+ private ByteBuffer createRow(Object... row)
throws IOException
{
return _testTable.createRow(row);
@@ -146,7 +146,7 @@ public class TableTest extends TestCase {
return b;
}
- private TableImpl newTestTable()
+ private TableImpl newTestTable()
throws Exception
{
_testTable = new TestTable();
@@ -185,8 +185,8 @@ public class TableTest extends TestCase {
return getFormat().CHARSET;
}
@Override
- protected Calendar getCalendar() {
- return Calendar.getInstance();
+ public TimeZone getTimeZone() {
+ return TimeZone.getDefault();
}
@Override
public boolean isCompressedUnicode() {
diff --git a/src/test/java/com/healthmarketscience/jackcess/TestUtil.java b/src/test/java/com/healthmarketscience/jackcess/TestUtil.java
index 7680fb3..b5db277 100644
--- a/src/test/java/com/healthmarketscience/jackcess/TestUtil.java
+++ b/src/test/java/com/healthmarketscience/jackcess/TestUtil.java
@@ -53,6 +53,7 @@ import org.junit.Assert;
*
* @author James Ahlborn
*/
+@SuppressWarnings("deprecation")
public class TestUtil
{
public static final TimeZone TEST_TZ =
@@ -86,7 +87,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();
diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/DatabaseReadWriteTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/DatabaseReadWriteTest.java
index 561f1e8..104b266 100644
--- a/src/test/java/com/healthmarketscience/jackcess/impl/DatabaseReadWriteTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/impl/DatabaseReadWriteTest.java
@@ -16,6 +16,9 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -53,7 +56,7 @@ public class DatabaseReadWriteTest extends TestCase
db.close();
}
}
-
+
public void testWriteAndReadInMem() throws Exception {
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
Database db = createMem(fileFormat);
@@ -61,7 +64,7 @@ public class DatabaseReadWriteTest extends TestCase
db.close();
}
}
-
+
private static void doTestWriteAndRead(Database db) throws Exception {
createTestTable(db);
Object[] row = createTestRow();
@@ -117,7 +120,7 @@ public class DatabaseReadWriteTest extends TestCase
}
}
- public void testUpdateRow() throws Exception
+ public void testUpdateRow() throws Exception
{
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
Database db = createMem(fileFormat);
@@ -250,11 +253,18 @@ public class DatabaseReadWriteTest extends TestCase
final long timeRange = 100000000L;
final long timeStep = 37L;
- for(long time = testTime - timeRange; time < testTime + timeRange;
+ for(long time = testTime - timeRange; time < testTime + timeRange;
time += timeStep) {
double accTime = ColumnImpl.toLocalDateDouble(time);
long newTime = ColumnImpl.fromLocalDateDouble(accTime);
assertEquals(time, newTime);
+
+ Instant inst = Instant.ofEpochMilli(time);
+ LocalDateTime ldt = LocalDateTime.ofInstant(inst, ZoneOffset.UTC);
+
+ accTime = ColumnImpl.toDateDouble(ldt);
+ LocalDateTime newLdt = ColumnImpl.ldtFromLocalDateDouble(accTime);
+ assertEquals(ldt, newLdt);
}
}
}
diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java
index 1ed17b7..36ab9bd 100644
--- a/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java
@@ -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)) {
diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java
index 132b788..895bbed 100644
--- a/src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java
@@ -17,6 +17,7 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl.expr;
import java.math.BigDecimal;
+import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
@@ -78,9 +79,9 @@ public class DefaultFunctionsTest extends TestCase
eval("=CSng(\"57.12345\")"));
assertEval("9786", "=CStr(9786)");
assertEval("-42", "=CStr(-42)");
- assertEval(new Date(1041483600000L), "=CDate('01/02/2003')");
- assertEval(new Date(1041508800000L), "=CDate('01/02/2003 7:00:00 AM')");
- assertEval(new Date(-1948781520000L), "=CDate(3013.45)");
+ assertEval(LocalDateTime.of(2003,1,2,0,0), "=CDate('01/02/2003')");
+ assertEval(LocalDateTime.of(2003,1,2,7,0), "=CDate('01/02/2003 7:00:00 AM')");
+ assertEval(LocalDateTime.of(1908,3,31,10,48), "=CDate(3013.45)");
assertEval(-1, "=IsNull(Null)");
@@ -589,7 +590,7 @@ public class DefaultFunctionsTest extends TestCase
assertEval("409.090909090909", "=CStr(SYD(30000,7500,10,10))");
assertEval("-1.63048347266756E-02", "=CStr(Rate(3,200,-610,0,-20,0.1))");
- assertEval("7.70147248820175E-03", "=CStr(Rate(4*12,-200,8000))");
+ assertEval("7.70147248820165E-03", "=CStr(Rate(4*12,-200,8000))");
assertEval("-1.09802980531205", "=CStr(Rate(60,93.22,5000,0.1))");
}
diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java
index c67dfe7..67ad20b 100644
--- a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java
@@ -20,14 +20,13 @@ import java.io.BufferedReader;
import java.io.FileReader;
import java.math.BigDecimal;
import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import javax.script.Bindings;
import javax.script.SimpleBindings;
import com.healthmarketscience.jackcess.DataType;
-import com.healthmarketscience.jackcess.DatabaseBuilder;
import com.healthmarketscience.jackcess.TestUtil;
import com.healthmarketscience.jackcess.expr.EvalContext;
import com.healthmarketscience.jackcess.expr.Expression;
@@ -38,7 +37,6 @@ import com.healthmarketscience.jackcess.expr.ParseException;
import com.healthmarketscience.jackcess.expr.TemporalConfig;
import com.healthmarketscience.jackcess.expr.Value;
import com.healthmarketscience.jackcess.impl.BaseEvalContext;
-import com.healthmarketscience.jackcess.impl.expr.NumberFormatter;
import junit.framework.TestCase;
/**
@@ -318,11 +316,11 @@ public class ExpressionatorTest extends TestCase
public void testDateArith() throws Exception
{
- assertEquals(new Date(1041508800000L), eval("=#01/02/2003# + #7:00:00 AM#"));
- assertEquals(new Date(1041458400000L), eval("=#01/02/2003# - #7:00:00 AM#"));
- assertEquals(new Date(1044680400000L), eval("=#01/02/2003# + '37'"));
- assertEquals(new Date(1044680400000L), eval("='37' + #01/02/2003#"));
- assertEquals(new Date(1041508800000L), eval("=#01/02/2003 7:00:00 AM#"));
+ assertEquals(LocalDateTime.of(2003,1,2,7,0), eval("=#01/02/2003# + #7:00:00 AM#"));
+ assertEquals(LocalDateTime.of(2003,1,1,17,0), eval("=#01/02/2003# - #7:00:00 AM#"));
+ assertEquals(LocalDateTime.of(2003,2,8,0,0), eval("=#01/02/2003# + '37'"));
+ assertEquals(LocalDateTime.of(2003,2,8,0,0), eval("='37' + #01/02/2003#"));
+ assertEquals(LocalDateTime.of(2003,1,2,7,0), eval("=#01/02/2003 7:00:00 AM#"));
assertEquals("2/8/2003", eval("=CStr(#01/02/2003# + '37')"));
assertEquals("9:24:00 AM", eval("=CStr(#7:00:00 AM# + 0.1)"));
@@ -404,7 +402,7 @@ public class ExpressionatorTest extends TestCase
assertEquals("foo37", eval("=\"foo\" + (12 + 25)"));
assertEquals("25foo12", eval("=\"25foo\" + 12"));
- assertEquals(new Date(1485579600000L), eval("=#1/1/2017# + 27"));
+ assertEquals(LocalDateTime.of(2017,1,28,0,0), eval("=#1/1/2017# + 27"));
assertEquals(128208, eval("=#1/1/2017# * 3"));
}
@@ -590,15 +588,14 @@ public class ExpressionatorTest extends TestCase
return TemporalConfig.US_TEMPORAL_CONFIG;
}
- public SimpleDateFormat createDateFormat(String formatStr) {
- SimpleDateFormat sdf = DatabaseBuilder.createDateFormat(formatStr);
- sdf.setTimeZone(TestUtil.TEST_TZ);
- return sdf;
+ public DateTimeFormatter createDateFormatter(String formatStr) {
+ DateTimeFormatter dtf = DateTimeFormatter.ofPattern(
+ formatStr, TemporalConfig.US_TEMPORAL_CONFIG.getLocale());
+ return dtf;
}
- public Calendar getCalendar() {
- return createDateFormat(getTemporalConfig().getDefaultDateTimeFormat())
- .getCalendar();
+ public ZoneId getZoneId() {
+ return TestUtil.TEST_TZ.toZoneId();
}
public NumericConfig getNumericConfig() {
diff --git a/src/test/java/com/healthmarketscience/jackcess/query/QueryTest.java b/src/test/java/com/healthmarketscience/jackcess/query/QueryTest.java
index f7e48f4..f2fe675 100644
--- a/src/test/java/com/healthmarketscience/jackcess/query/QueryTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/query/QueryTest.java
@@ -29,9 +29,8 @@ import com.healthmarketscience.jackcess.TestUtil;
import com.healthmarketscience.jackcess.impl.query.QueryImpl;
import com.healthmarketscience.jackcess.impl.query.QueryImpl.Row;
import junit.framework.TestCase;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
-import static org.apache.commons.lang.SystemUtils.LINE_SEPARATOR;
import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
@@ -53,22 +52,22 @@ public class QueryTest extends TestCase
String expr2 = "Select * from Table2";
UnionQuery query = (UnionQuery)newQuery(
- Query.Type.UNION,
+ Query.Type.UNION,
newRow(TABLE_ATTRIBUTE, expr1, null, UNION_PART1),
newRow(TABLE_ATTRIBUTE, expr2, null, UNION_PART2));
setFlag(query, 3);
assertEquals(multiline("Select * from Table1",
- "UNION Select * from Table2;"),
+ "UNION Select * from Table2;"),
query.toSQLString());
setFlag(query, 1);
assertEquals(multiline("Select * from Table1",
- "UNION ALL Select * from Table2;"),
+ "UNION ALL Select * from Table2;"),
query.toSQLString());
- addRows(query, newRow(ORDERBY_ATTRIBUTE, "Table1.id",
+ addRows(query, newRow(ORDERBY_ATTRIBUTE, "Table1.id",
null, null));
assertEquals(multiline("Select * from Table1",
@@ -112,7 +111,7 @@ public class QueryTest extends TestCase
public void testUpdateQuery() throws Exception
{
UpdateQuery query = (UpdateQuery)newQuery(
- Query.Type.UPDATE,
+ Query.Type.UPDATE,
newRow(TABLE_ATTRIBUTE, null, "Table1", null),
newRow(COLUMN_ATTRIBUTE, "\"some string\"", null, "Table1.id"),
newRow(COLUMN_ATTRIBUTE, "42", null, "Table1.col1"));
@@ -122,7 +121,7 @@ public class QueryTest extends TestCase
"SET Table1.id = \"some string\", Table1.col1 = 42;"),
query.toSQLString());
- addRows(query, newRow(WHERE_ATTRIBUTE, "(Table1.col2 < 13)",
+ addRows(query, newRow(WHERE_ATTRIBUTE, "(Table1.col2 < 13)",
null, null));
assertEquals(
@@ -135,14 +134,14 @@ public class QueryTest extends TestCase
public void testSelectQuery() throws Exception
{
SelectQuery query = (SelectQuery)newQuery(
- Query.Type.SELECT,
+ Query.Type.SELECT,
newRow(TABLE_ATTRIBUTE, null, "Table1", null));
setFlag(query, 1);
assertEquals(multiline("SELECT *",
"FROM Table1;"),
query.toSQLString());
-
+
doTestColumns(query);
doTestSelectFlags(query);
doTestParameters(query);
@@ -168,7 +167,7 @@ public class QueryTest extends TestCase
}
addRows(query, newRow(TYPE_ATTRIBUTE, null, -1, null, null));
-
+
try {
query.getTypeRow();
fail("IllegalStateException should have been thrown");
@@ -265,7 +264,7 @@ public class QueryTest extends TestCase
newRow(COLUMN_ATTRIBUTE, "54", APPEND_VALUE_FLAG, null, null),
newRow(COLUMN_ATTRIBUTE, "'hello'", APPEND_VALUE_FLAG, null, null));
- assertEquals(multiline("INSERT INTO Table2",
+ assertEquals(multiline("INSERT INTO Table2",
"VALUES (54, 'hello');"), query.toSQLString());
query = (AppendQuery)newQuery(
@@ -274,7 +273,7 @@ public class QueryTest extends TestCase
newRow(COLUMN_ATTRIBUTE, "54", APPEND_VALUE_FLAG, null, "ID"),
newRow(COLUMN_ATTRIBUTE, "'hello'", APPEND_VALUE_FLAG, null, "Field 3"));
- assertEquals(multiline("INSERT INTO Table2 (ID, [Field 3])",
+ assertEquals(multiline("INSERT INTO Table2 (ID, [Field 3])",
"VALUES (54, 'hello');"), query.toSQLString());
}
@@ -291,27 +290,27 @@ public class QueryTest extends TestCase
private void doTestSelectFlags(SelectQuery query) throws Exception
{
setFlag(query, 3);
-
+
assertEquals(multiline("SELECT DISTINCT Table1.id, Table1.col AS [Some.Alias], *",
"FROM Table1;"),
query.toSQLString());
setFlag(query, 9);
-
+
assertEquals(multiline("SELECT DISTINCTROW Table1.id, Table1.col AS [Some.Alias], *",
"FROM Table1;"),
query.toSQLString());
setFlag(query, 7);
-
+
assertEquals(multiline("SELECT DISTINCT Table1.id, Table1.col AS [Some.Alias], *",
"FROM Table1",
"WITH OWNERACCESS OPTION;"),
query.toSQLString());
- replaceRows(query,
+ replaceRows(query,
newRow(FLAG_ATTRIBUTE, null, 49, null, "5", null));
-
+
assertEquals(multiline("SELECT TOP 5 PERCENT Table1.id, Table1.col AS [Some.Alias], *",
"FROM Table1;"),
query.toSQLString());
@@ -354,7 +353,7 @@ public class QueryTest extends TestCase
assertEquals(multiline("SELECT Table1.id, Table1.col AS [Some.Alias]",
"FROM Table1, Table2 AS [Another Table], [Select val from Table3].val AS Table3Val;"),
- query.toSQLString());
+ query.toSQLString());
}
private void doTestRemoteDb(SelectQuery query) throws Exception
@@ -387,7 +386,7 @@ public class QueryTest extends TestCase
assertEquals(multiline("SELECT Table1.id, Table1.col AS [Some.Alias]",
"FROM [Select val from Table3].val AS Table3Val, Table1 INNER JOIN Table2 AS [Another Table] ON (Table1.id = [Another Table].id);"),
query.toSQLString());
-
+
addRows(query, newRow(JOIN_ATTRIBUTE, "(Table1.id = Table3Val.id)", 2, "Table1", "Table3Val"));
assertEquals(multiline("SELECT Table1.id, Table1.col AS [Some.Alias]",
@@ -417,13 +416,13 @@ public class QueryTest extends TestCase
private void doTestWhereExpression(SelectQuery query) throws Exception
{
- addRows(query, newRow(WHERE_ATTRIBUTE, "(Table1.col2 < 13)",
+ addRows(query, newRow(WHERE_ATTRIBUTE, "(Table1.col2 < 13)",
null, null));
assertEquals(multiline("SELECT Table1.id, Table1.col AS [Some.Alias]",
"FROM Table1, Table2 AS [Another Table], [Select val from Table3].val AS Table3Val",
"WHERE (Table1.col2 < 13);"),
- query.toSQLString());
+ query.toSQLString());
}
private void doTestGroupings(SelectQuery query) throws Exception
@@ -435,7 +434,7 @@ public class QueryTest extends TestCase
"FROM Table1, Table2 AS [Another Table], [Select val from Table3].val AS Table3Val",
"WHERE (Table1.col2 < 13)",
"GROUP BY Table1.id, SUM(Table1.val);"),
- query.toSQLString());
+ query.toSQLString());
}
private void doTestHavingExpression(SelectQuery query) throws Exception
@@ -447,7 +446,7 @@ public class QueryTest extends TestCase
"WHERE (Table1.col2 < 13)",
"GROUP BY Table1.id, SUM(Table1.val)",
"HAVING (SUM(Table1.val) = 500);"),
- query.toSQLString());
+ query.toSQLString());
}
private void doTestOrderings(SelectQuery query) throws Exception
@@ -461,7 +460,7 @@ public class QueryTest extends TestCase
"GROUP BY Table1.id, SUM(Table1.val)",
"HAVING (SUM(Table1.val) = 500)",
"ORDER BY Table1.id, Table2.val DESC;"),
- query.toSQLString());
+ query.toSQLString());
}
public void testComplexJoins() throws Exception
@@ -484,7 +483,7 @@ public class QueryTest extends TestCase
addJoinRows(query, 1, 2, 1,
2, 1, 1);
-
+
assertEquals(multiline("SELECT *",
"FROM Table3, Table4, Table5, Table6, Table7, Table8, Table9, Table10, Table1 INNER JOIN Table2 ON (Table2.f3 = Table1.f3) AND (Table1.f0 = Table2.f0);"),
query.toSQLString());
@@ -498,7 +497,7 @@ public class QueryTest extends TestCase
} catch(IllegalStateException e) {
// success
}
-
+
addJoinRows(query, 1, 2, 1,
3, 4, 1,
5, 6, 1,
@@ -613,7 +612,7 @@ public class QueryTest extends TestCase
private static void setFlag(Query query, Number newFlagNum)
{
- replaceRows(query,
+ replaceRows(query,
newRow(FLAG_ATTRIBUTE, null, newFlagNum, null, null, null));
}
@@ -646,7 +645,7 @@ public class QueryTest extends TestCase
private static String multiline(String... strs)
{
- return StringUtils.join(strs, LINE_SEPARATOR);
+ return StringUtils.join(strs, System.lineSeparator());
}
}
diff --git a/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java b/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java
index 87f8e6f..d0446b2 100644
--- a/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/util/CustomLinkResolverTest.java
@@ -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;
@@ -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,
boolean readOnly)
throws IOException
{
diff --git a/src/test/java/com/healthmarketscience/jackcess/util/ExportTest.java b/src/test/java/com/healthmarketscience/jackcess/util/ExportTest.java
index 754be06..5ffdbd9 100644
--- a/src/test/java/com/healthmarketscience/jackcess/util/ExportTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/util/ExportTest.java
@@ -30,7 +30,6 @@ import com.healthmarketscience.jackcess.Table;
import com.healthmarketscience.jackcess.TableBuilder;
import com.healthmarketscience.jackcess.impl.JetFormatTest;
import junit.framework.TestCase;
-import org.apache.commons.lang.SystemUtils;
import static com.healthmarketscience.jackcess.TestUtil.*;
/**
@@ -39,7 +38,7 @@ import static com.healthmarketscience.jackcess.TestUtil.*;
*/
public class ExportTest extends TestCase
{
- private static final String NL = SystemUtils.LINE_SEPARATOR;
+ private static final String NL = System.lineSeparator();
public ExportTest(String name) {
@@ -78,7 +77,7 @@ public class ExportTest extends TestCase
new ExportUtil.Builder(db, "test")
.exportWriter(new BufferedWriter(out));
- String expected =
+ String expected =
"some text||some more,13,13.25,\"61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78\n79 7A 61 62 63 64\",true," + testDate + NL +
"\"crazy'data\"\"here\",-345,-3.45E-4,61 62 63 64 65 66 67,true," + NL +
"C:\\temp\\some_file.txt,25,0.0,,false," + NL;
@@ -86,14 +85,14 @@ public class ExportTest extends TestCase
assertEquals(expected, out.toString());
out = new StringWriter();
-
+
new ExportUtil.Builder(db, "test")
.setHeader(true)
.setDelimiter("||")
.setQuote('\'')
.exportWriter(new BufferedWriter(out));
- expected =
+ expected =
"col1||col2||col3||col4||col5||col6" + NL +
"'some text||some more'||13||13.25||'61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78\n79 7A 61 62 63 64'||true||" + testDate + NL +
"'crazy''data\"here'||-345||-3.45E-4||61 62 63 64 65 66 67||true||" + NL +
@@ -117,7 +116,7 @@ public class ExportTest extends TestCase
.setFilter(oddFilter)
.exportWriter(new BufferedWriter(out));
- expected =
+ expected =
"some text||some more,13,13.25,\"61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78\n79 7A 61 62 63 64\",true," + testDate + NL +
"C:\\temp\\some_file.txt,25,0.0,,false," + NL;
diff --git a/src/test/java/com/healthmarketscience/jackcess/util/OleBlobTest.java b/src/test/java/com/healthmarketscience/jackcess/util/OleBlobTest.java
index be3a0a9..725daa0 100644
--- a/src/test/java/com/healthmarketscience/jackcess/util/OleBlobTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/util/OleBlobTest.java
@@ -34,7 +34,7 @@ import com.healthmarketscience.jackcess.impl.CompoundOleUtil;
import junit.framework.TestCase;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import static com.healthmarketscience.jackcess.TestUtil.*;
import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
@@ -42,7 +42,7 @@ import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
*
* @author James Ahlborn
*/
-public class OleBlobTest extends TestCase
+public class OleBlobTest extends TestCase
{
public OleBlobTest(String name) {
@@ -73,7 +73,7 @@ public class OleBlobTest extends TestCase
} finally {
ByteUtil.closeQuietly(blob);
}
-
+
try {
blob = new OleBlob.Builder()
.setLink(sampleFile)
@@ -82,7 +82,7 @@ public class OleBlobTest extends TestCase
} finally {
ByteUtil.closeQuietly(blob);
}
-
+
try {
blob = new OleBlob.Builder()
.setPackagePrettyName("Text File")
@@ -109,14 +109,14 @@ public class OleBlobTest extends TestCase
assertEquals(sampleFilePath, spc.getFilePath());
assertEquals(sampleFilePath, spc.getLocalFilePath());
assertEquals(sampleFileName, spc.getFileName());
- assertEquals(OleBlob.Builder.PACKAGE_PRETTY_NAME,
+ assertEquals(OleBlob.Builder.PACKAGE_PRETTY_NAME,
spc.getPrettyName());
- assertEquals(OleBlob.Builder.PACKAGE_TYPE_NAME,
+ assertEquals(OleBlob.Builder.PACKAGE_TYPE_NAME,
spc.getTypeName());
- assertEquals(OleBlob.Builder.PACKAGE_TYPE_NAME,
+ assertEquals(OleBlob.Builder.PACKAGE_TYPE_NAME,
spc.getClassName());
assertEquals(sampleFileBytes.length, spc.length());
- assertTrue(Arrays.equals(sampleFileBytes,
+ assertTrue(Arrays.equals(sampleFileBytes,
toByteArray(spc.getStream(), spc.length())));
break;
@@ -130,7 +130,7 @@ public class OleBlobTest extends TestCase
assertEquals(OleBlob.Builder.PACKAGE_TYPE_NAME, lc.getTypeName());
assertEquals(OleBlob.Builder.PACKAGE_TYPE_NAME, lc.getClassName());
break;
-
+
case 3:
OleBlob.OtherContent oc = (OleBlob.OtherContent)content;
assertEquals(OleBlob.ContentType.OTHER, oc.getType());
@@ -138,7 +138,7 @@ public class OleBlobTest extends TestCase
assertEquals("Text.File", oc.getClassName());
assertEquals("TextFile", oc.getTypeName());
assertEquals(sampleFileBytes.length, oc.length());
- assertTrue(Arrays.equals(sampleFileBytes,
+ assertTrue(Arrays.equals(sampleFileBytes,
toByteArray(oc.getStream(), oc.length())));
break;
default:
@@ -149,8 +149,8 @@ public class OleBlobTest extends TestCase
}
}
- db.close();
- }
+ db.close();
+ }
}
public void testReadBlob() throws Exception
@@ -198,7 +198,7 @@ public class OleBlobTest extends TestCase
} else {
if("test_word.doc".equals(name)) {
- checkCompoundEntries(cc,
+ checkCompoundEntries(cc,
"/%02OlePres000", 466,
"/WordDocument", 4096,
"/%05SummaryInformation", 4096,
@@ -210,7 +210,7 @@ public class OleBlobTest extends TestCase
"/%01Ole", 20);
checkCompoundStorage(cc, attach);
} else if("test_excel.xls".equals(name)) {
- checkCompoundEntries(cc,
+ checkCompoundEntries(cc,
"/%02OlePres000", 1326,
"/%03AccessObjSiteData", 56,
"/%05SummaryInformation", 200,
@@ -243,10 +243,10 @@ public class OleBlobTest extends TestCase
}
db.close();
- }
+ }
}
- private static void checkCompoundEntries(OleBlob.CompoundContent cc,
+ private static void checkCompoundEntries(OleBlob.CompoundContent cc,
Object... entryInfo)
throws Exception
{
@@ -262,7 +262,7 @@ public class OleBlobTest extends TestCase
}
}
- private static void checkCompoundStorage(OleBlob.CompoundContent cc,
+ private static void checkCompoundStorage(OleBlob.CompoundContent cc,
Attachment attach)
throws Exception
{
@@ -273,7 +273,7 @@ public class OleBlobTest extends TestCase
fout.write(attach.getFileData());
fout.close();
- NPOIFSFileSystem attachFs = new NPOIFSFileSystem(tmpData, true);
+ POIFSFileSystem attachFs = new POIFSFileSystem(tmpData, true);
for(OleBlob.CompoundContent.Entry e : cc) {
DocumentEntry attachE = null;
@@ -284,7 +284,7 @@ public class OleBlobTest extends TestCase
continue;
}
- byte[] attachEBytes = toByteArray(new DocumentInputStream(attachE),
+ byte[] attachEBytes = toByteArray(new DocumentInputStream(attachE),
attachE.getSize());
byte[] entryBytes = toByteArray(e.getStream(), e.length());
@@ -292,9 +292,9 @@ public class OleBlobTest extends TestCase
}
ByteUtil.closeQuietly(attachFs);
-
+
} finally {
tmpData.delete();
- }
+ }
}
}
diff --git a/src/test/resources/log4j_test.properties b/src/test/resources/log4j_test.properties
deleted file mode 100644
index 6647228..0000000
--- a/src/test/resources/log4j_test.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-log4j.rootCategory=INFO, stdout
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=**** %-5p %d{MMM d HH:mm:ss} [%F] - %m%n
-
-log4j.category.com.healthmarketscience.jackcess=ERROR
diff --git a/src/test/resources/logging_test.properties b/src/test/resources/logging_test.properties
new file mode 100644
index 0000000..1d5324b
--- /dev/null
+++ b/src/test/resources/logging_test.properties
@@ -0,0 +1 @@
+com.healthmarketscience.jackcess.level=SEVERE