From 831d8c0b1d0996ba63c9e2ef3b73d6a8982a3ad2 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Mon, 11 Feb 2019 22:26:03 +0000 Subject: [PATCH] Add ColumnFormatter utility which can apply Column Format property for display of column values git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1291 f203690c-595d-4dc9-a70b-905162fa7fd2 --- src/changes/changes.xml | 6 + .../jackcess/expr/package-info.java | 2 +- .../jackcess/impl/ColEvalContext.java | 6 + .../impl/ColValidatorEvalContext.java | 2 +- .../jackcess/impl/expr/DefaultFunctions.java | 7 +- .../impl/expr/ExpressionTokenizer.java | 3 +- .../jackcess/impl/expr/FormatUtil.java | 121 +++++++++----- .../jackcess/util/ColumnFormatter.java | 150 ++++++++++++++++++ .../jackcess/util/ColumnValidator.java | 2 +- .../jackcess/TestUtil.java | 12 +- .../jackcess/impl/CodecHandlerTest.java | 38 ++--- .../jackcess/util/ColumnFormatterTest.java | 114 +++++++++++++ 12 files changed, 398 insertions(+), 65 deletions(-) create mode 100644 src/main/java/com/healthmarketscience/jackcess/util/ColumnFormatter.java create mode 100644 src/test/java/com/healthmarketscience/jackcess/util/ColumnFormatterTest.java diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 972ab9d..c7a37e0 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -4,6 +4,12 @@ Tim McCune + + + Add ColumnFormatter utility which can apply Column "Format" property + for display of column values. + + Jackcess now requires a Java 8+ runtime. As part of this update, all 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 d5d380a..d6f137e 100644 --- a/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java +++ b/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java @@ -215,7 +215,7 @@ limitations under the License. * * * - * + * * * * diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColEvalContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColEvalContext.java index e62f0db..6ec3f49 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/ColEvalContext.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColEvalContext.java @@ -16,6 +16,8 @@ limitations under the License. package com.healthmarketscience.jackcess.impl; +import com.healthmarketscience.jackcess.expr.Value; + @@ -40,4 +42,8 @@ public abstract class ColEvalContext extends BaseEvalContext protected String withErrorContext(String msg) { return _col.withErrorContext(msg); } + + protected Value toValue(Object val) { + return toValue(val, _col.getType()); + } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColValidatorEvalContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColValidatorEvalContext.java index 794def1..27ca9dd 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/ColValidatorEvalContext.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColValidatorEvalContext.java @@ -65,7 +65,7 @@ public class ColValidatorEvalContext extends ColEvalContext @Override public Value getThisColumnValue() { - return toValue(_val, getCol().getType()); + return toValue(_val); } @Override 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 5a871e4..6182dd4 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java @@ -29,6 +29,7 @@ import com.healthmarketscience.jackcess.expr.EvalContext; import com.healthmarketscience.jackcess.expr.EvalException; import com.healthmarketscience.jackcess.expr.Function; import com.healthmarketscience.jackcess.expr.FunctionLookup; +import com.healthmarketscience.jackcess.expr.LocaleContext; import com.healthmarketscience.jackcess.expr.NumericConfig; import com.healthmarketscience.jackcess.expr.TemporalConfig; import com.healthmarketscience.jackcess.expr.Value; @@ -473,11 +474,11 @@ public class DefaultFunctions } }); - private static boolean stringIsNumeric(EvalContext ctx, Value param) { + private static boolean stringIsNumeric(LocaleContext ctx, Value param) { return (maybeGetAsBigDecimal(ctx, param) != null); } - static BigDecimal maybeGetAsBigDecimal(EvalContext ctx, Value param) { + static BigDecimal maybeGetAsBigDecimal(LocaleContext ctx, Value param) { try { return param.getAsBigDecimal(ctx); } catch(EvalException ignored) { @@ -490,7 +491,7 @@ public class DefaultFunctions return (maybeGetAsDateTimeValue(ctx, param) != null); } - static Value maybeGetAsDateTimeValue(EvalContext ctx, Value param) { + static Value maybeGetAsDateTimeValue(LocaleContext ctx, Value param) { try { // see if we can coerce to date/time return param.getAsDateTimeValue(ctx); 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 39f7050..544c08b 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java @@ -38,6 +38,7 @@ import com.healthmarketscience.jackcess.expr.ParseException; import com.healthmarketscience.jackcess.expr.TemporalConfig; import com.healthmarketscience.jackcess.expr.Value; import com.healthmarketscience.jackcess.impl.ColumnImpl; +import org.apache.commons.lang3.StringUtils; /** @@ -89,7 +90,7 @@ class ExpressionTokenizer exprStr = exprStr.trim(); } - if((exprStr == null) || (exprStr.length() == 0)) { + if(StringUtils.isEmpty(exprStr)) { return null; } 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 14ad3fa..68fce3c 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/FormatUtil.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/FormatUtil.java @@ -41,6 +41,7 @@ import com.healthmarketscience.jackcess.expr.EvalException; import com.healthmarketscience.jackcess.expr.NumericConfig; import com.healthmarketscience.jackcess.expr.TemporalConfig; import com.healthmarketscience.jackcess.expr.Value; +import org.apache.commons.lang3.StringUtils; import static com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.ExprBuf; /** @@ -114,40 +115,41 @@ public class FormatUtil private static final Map PREDEF_FMTS = new HashMap(); static { - PREDEF_FMTS.put("General Date", args -> ValueSupport.toValue( + putPredefFormat("General Date", args -> ValueSupport.toValue( args.coerceToDateTimeValue().getAsString())); - PREDEF_FMTS.put("Long Date", + putPredefFormat("Long Date", new PredefDateFmt(TemporalConfig.Type.LONG_DATE)); - PREDEF_FMTS.put("Medium Date", + putPredefFormat("Medium Date", new PredefDateFmt(TemporalConfig.Type.MEDIUM_DATE)); - PREDEF_FMTS.put("Short Date", + putPredefFormat("Short Date", new PredefDateFmt(TemporalConfig.Type.SHORT_DATE)); - PREDEF_FMTS.put("Long Time", + putPredefFormat("Long Time", new PredefDateFmt(TemporalConfig.Type.LONG_TIME)); - PREDEF_FMTS.put("Medium Time", + putPredefFormat("Medium Time", new PredefDateFmt(TemporalConfig.Type.MEDIUM_TIME)); - PREDEF_FMTS.put("Short Time", + putPredefFormat("Short Time", new PredefDateFmt(TemporalConfig.Type.SHORT_TIME)); - PREDEF_FMTS.put("General Number", args -> ValueSupport.toValue( + putPredefFormat("General Number", args -> ValueSupport.toValue( args.coerceToNumberValue().getAsString())); - PREDEF_FMTS.put("Currency", + putPredefFormat("Currency", new PredefNumberFmt(NumericConfig.Type.CURRENCY)); - PREDEF_FMTS.put("Euro", new PredefNumberFmt(NumericConfig.Type.EURO)); - PREDEF_FMTS.put("Fixed", + putPredefFormat("Euro", new PredefNumberFmt(NumericConfig.Type.EURO)); + putPredefFormat("Fixed", new PredefNumberFmt(NumericConfig.Type.FIXED)); - PREDEF_FMTS.put("Standard", + putPredefFormat("Standard", new PredefNumberFmt(NumericConfig.Type.STANDARD)); - PREDEF_FMTS.put("Percent", + putPredefFormat("Percent", new PredefNumberFmt(NumericConfig.Type.PERCENT)); - PREDEF_FMTS.put("Scientific", new ScientificPredefNumberFmt()); + putPredefFormat("Scientific", new ScientificPredefNumberFmt()); - PREDEF_FMTS.put("True/False", new PredefBoolFmt("True", "False")); - PREDEF_FMTS.put("Yes/No", new PredefBoolFmt("Yes", "No")); - PREDEF_FMTS.put("On/Off", new PredefBoolFmt("On", "Off")); + putPredefFormat("True/False", new PredefBoolFmt("True", "False")); + putPredefFormat("Yes/No", new PredefBoolFmt("Yes", "No")); + putPredefFormat("On/Off", new PredefBoolFmt("On", "Off")); } private static final Fmt NULL_FMT = args -> ValueSupport.EMPTY_STR_VAL; + private static final Fmt DUMMY_FMT = args -> args.getNonNullExpr(); private static final char QUOTE_CHAR = '"'; private static final char ESCAPE_CHAR = '\\'; @@ -282,6 +284,15 @@ public class FormatUtil _firstWeekType = firstWeekType; } + public Args setExpr(Value expr) { + _expr = expr; + return this; + } + + public Value getNonNullExpr() { + return (_expr.isNull() ? ValueSupport.EMPTY_STR_VAL : _expr); + } + public boolean isNullOrEmptyString() { return(_expr.isNull() || // only a string value could ever be an empty string @@ -385,37 +396,67 @@ public class FormatUtil public String getAsString() { return _expr.getAsString(_ctx); } + + public Value format(Fmt fmt) { + Value origExpr = _expr; + try { + return fmt.format(this); + } catch(EvalException ee) { + // values which cannot be formatted as the target type are just + // returned "as is" + return origExpr; + } + } } private FormatUtil() {} + /** + * Utility for leveraging format support outside of expression evaluation. + */ + public static class StandaloneFormatter + { + private final Fmt _fmt; + private final Args _args; + + private StandaloneFormatter(Fmt fmt, Args args) { + _fmt = fmt; + _args = args; + } + + public Value format(Value expr) { + return _args.setExpr(expr).format(_fmt); + } + } public static Value format(EvalContext ctx, Value expr, String fmtStr, int firstDay, int firstWeekType) { + Args args = new Args(ctx, expr, firstDay, firstWeekType); + return args.format(createFormat(args, fmtStr)); + } - try { - Args args = new Args(ctx, expr, firstDay, firstWeekType); + public static StandaloneFormatter createStandaloneFormatter( + EvalContext ctx, String fmtStr, int firstDay, int firstWeekType) { + Args args = new Args(ctx, null, firstDay, firstWeekType); + Fmt fmt = createFormat(args, fmtStr); + return new StandaloneFormatter(fmt, args); + } - Fmt predefFmt = PREDEF_FMTS.get(fmtStr); - if(predefFmt != null) { - if(args.isNullOrEmptyString()) { - // predefined formats return empty string for null - return ValueSupport.EMPTY_STR_VAL; - } - return predefFmt.format(args); - } + private static Fmt createFormat(Args args, String fmtStr) { + Fmt predefFmt = PREDEF_FMTS.get(fmtStr); + if(predefFmt != null) { + return predefFmt; + } - // TODO implement caching for custom formats? put into Bindings. use - // special "cache" prefix to know which caches to clear when evalconfig - // is altered (could also cache other Format* functions) + if(StringUtils.isEmpty(fmtStr)) { + return DUMMY_FMT; + } - return parseCustomFormat(fmtStr, args).format(args); + // TODO implement caching for custom formats? put into Bindings. use + // special "cache" prefix to know which caches to clear when evalconfig + // is altered (could also cache other Format* functions) - } catch(EvalException ee) { - // values which cannot be formatted as the target type are just - // returned "as is" - return expr; - } + return parseCustomFormat(fmtStr, args); } private static Fmt parseCustomFormat(String fmtStr, Args args) { @@ -1178,6 +1219,14 @@ public class FormatUtil } } + private static void putPredefFormat(String key, Fmt fmt) { + // predefined formats return empty string for null + Fmt wrapFmt = args -> (args.isNullOrEmptyString() ? + ValueSupport.EMPTY_STR_VAL : + fmt.format(args)); + PREDEF_FMTS.put(key, wrapFmt); + } + private static final class PredefDateFmt implements Fmt { private final TemporalConfig.Type _type; diff --git a/src/main/java/com/healthmarketscience/jackcess/util/ColumnFormatter.java b/src/main/java/com/healthmarketscience/jackcess/util/ColumnFormatter.java new file mode 100644 index 0000000..0133db8 --- /dev/null +++ b/src/main/java/com/healthmarketscience/jackcess/util/ColumnFormatter.java @@ -0,0 +1,150 @@ +/* +Copyright (c) 2019 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.util; + +import java.io.IOException; +import java.util.Map; + +import com.healthmarketscience.jackcess.Column; +import com.healthmarketscience.jackcess.PropertyMap; +import com.healthmarketscience.jackcess.expr.EvalConfig; +import com.healthmarketscience.jackcess.expr.EvalException; +import com.healthmarketscience.jackcess.impl.ColEvalContext; +import com.healthmarketscience.jackcess.impl.ColumnImpl; +import com.healthmarketscience.jackcess.impl.expr.FormatUtil; +import org.apache.commons.lang3.StringUtils; + +/** + * Utility for applying Column formatting to column values for display. This + * utility loads the "Format" property from the given column and builds an + * appropriate formatter (essentially leveraging the internals of the + * expression execution engine's support for the "Format()" function). Since + * formats leverage the expression evaluation engine, the underlying + * Database's {@link EvalConfig} can be used to alter how this utility formats + * values. Note, formatted values may be suitable for display only + * (i.e. a formatted value may not be accepted as an input value to a Table + * add/update method). + * + * @author James Ahlborn + * @usage _general_class_ + */ +public class ColumnFormatter +{ + private final ColumnImpl _col; + private final FormatEvalContext _ctx; + private String _fmtStr; + private FormatUtil.StandaloneFormatter _fmt; + + public ColumnFormatter(Column col) throws IOException { + _col = (ColumnImpl)col; + _ctx = new FormatEvalContext(_col); + reload(); + } + + /** + * Returns the currently loaded "Format" property for this formatter, may be + * {@code null}. + */ + public String getFormatString() { + return _fmtStr; + } + + /** + * Sets the given format string as the "Format" property for the underlying + * Column and reloads this formatter. + * + * @param fmtStr the new format string. may be {@code null}, in which case + * the "Format" property is removed from the underlying Column + */ + public void setFormatString(String fmtStr) throws IOException { + PropertyMap props = _col.getProperties(); + if(!StringUtils.isEmpty(fmtStr)) { + props.put(PropertyMap.FORMAT_PROP, fmtStr); + } else { + props.remove(PropertyMap.FORMAT_PROP); + } + props.save(); + reload(); + } + + /** + * Formats the given value according to the format currently defined for the + * underlying Column. + * + * @param val a valid input value for the DataType of the underlying Column + * (i.e. a value which could be passed to a Table add/update + * method for this Column). may be {@code null} + * + * @return the formatted result, always non-{@code null} + */ + public String format(Object val) { + return _ctx.format(val); + } + + /** + * Convenience method for retrieving the appropriate Column value from the + * given row array and formatting it. + * + * @return the formatted result, always non-{@code null} + */ + public String getRowValue(Object[] rowArray) { + return format(_col.getRowValue(rowArray)); + } + + /** + * Convenience method for retrieving the appropriate Column value from the + * given row map and formatting it. + * + * @return the formatted result, always non-{@code null} + */ + public String getRowValue(Map rowMap) { + return format(_col.getRowValue(rowMap)); + } + + /** + * If the properties for the underlying Column have been modified directly + * (or the EvalConfig for the underlying Database has been modified), this + * method may be called to reload the format for the underlying Column. + */ + public final void reload() throws IOException { + _fmt = null; + _fmtStr = null; + + _fmtStr = (String)_col.getProperties().getValue(PropertyMap.FORMAT_PROP); + _fmt = FormatUtil.createStandaloneFormatter(_ctx, _fmtStr, 1, 1); + } + + /** + * Utility class to provide an EvalContext for the expression evaluation + * engine format support. + */ + private class FormatEvalContext extends ColEvalContext + { + private FormatEvalContext(ColumnImpl col) { + super(col); + } + + public String format(Object val) { + try { + return _fmt.format(toValue(val)).getAsString(this); + } catch(EvalException ee) { + // invalid values for a given format result in returning the value as is + return val.toString(); + } + } + } +} diff --git a/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidator.java b/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidator.java index 101ff1e..ffe2e71 100644 --- a/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidator.java +++ b/src/main/java/com/healthmarketscience/jackcess/util/ColumnValidator.java @@ -27,7 +27,7 @@ import com.healthmarketscience.jackcess.Column; * * @author James Ahlborn */ -public interface ColumnValidator +public interface ColumnValidator { /** * Validates and/or manipulates the given potential new value for the given diff --git a/src/test/java/com/healthmarketscience/jackcess/TestUtil.java b/src/test/java/com/healthmarketscience/jackcess/TestUtil.java index 83b2d7d..fef09cc 100644 --- a/src/test/java/com/healthmarketscience/jackcess/TestUtil.java +++ b/src/test/java/com/healthmarketscience/jackcess/TestUtil.java @@ -117,18 +117,24 @@ public class TestUtil public static Database create(FileFormat fileFormat, boolean keep) throws Exception { - return create(fileFormat, keep, false); + return create(fileFormat, keep, true); } public static Database createMem(FileFormat fileFormat) throws Exception { - return create(fileFormat, false, true); + return create(fileFormat); + } + + public static Database createFile(FileFormat fileFormat) throws Exception { + return create(fileFormat, false, false); } private static Database create(FileFormat fileFormat, boolean keep, boolean inMem) throws Exception { - FileChannel channel = (inMem ? MemFileChannel.newChannel() : null); + + FileChannel channel = ((inMem && !keep) ? MemFileChannel.newChannel() : + null); if (fileFormat == FileFormat.GENERIC_JET4) { // while we don't support creating GENERIC_JET4 as a jackcess feature, diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/CodecHandlerTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/CodecHandlerTest.java index d6c788c..51e9f3c 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/CodecHandlerTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/CodecHandlerTest.java @@ -72,7 +72,7 @@ public class CodecHandlerTest extends TestCase private static void doTestCodecHandler(boolean simple) throws Exception { for(Database.FileFormat ff : SUPPORTED_FILEFORMATS) { - Database db = TestUtil.create(ff); + Database db = TestUtil.createFile(ff); int pageSize = ((DatabaseImpl)db).getFormat().PAGE_SIZE; File dbFile = db.getFile(); db.close(); @@ -163,7 +163,7 @@ public class CodecHandlerTest extends TestCase assertEquals(valuePrefix.length() + 100, value.length()); } - private static void encodeFile(File dbFile, int pageSize, boolean simple) + private static void encodeFile(File dbFile, int pageSize, boolean simple) throws Exception { long dbLen = dbFile.length(); @@ -174,7 +174,7 @@ public class CodecHandlerTest extends TestCase bb.clear(); fileChannel.read(bb, offset); - + int pageNumber = (int)(offset / pageSize); if(simple) { simpleEncode(bb.array(), bb.array(), pageNumber, 0, pageSize); @@ -221,12 +221,12 @@ public class CodecHandlerTest extends TestCase } } - private static final class SimpleCodecHandler implements CodecHandler + private static final class SimpleCodecHandler implements CodecHandler { private final TempBufferHolder _bufH = TempBufferHolder.newHolder( TempBufferHolder.Type.HARD, true); private final PageChannel _channel; - + private SimpleCodecHandler(PageChannel channel) { _channel = channel; } @@ -238,37 +238,37 @@ public class CodecHandlerTest extends TestCase public boolean canDecodeInline() { return true; } - + public void decodePage(ByteBuffer inPage, ByteBuffer outPage, - int pageNumber) - throws IOException + int pageNumber) + throws IOException { byte[] arr = inPage.array(); simpleDecode(arr, arr, pageNumber); } public ByteBuffer encodePage(ByteBuffer page, int pageNumber, - int pageOffset) + int pageOffset) throws IOException { ByteBuffer bb = _bufH.getPageBuffer(_channel); bb.clear(); - simpleEncode(page.array(), bb.array(), pageNumber, pageOffset, + simpleEncode(page.array(), bb.array(), pageNumber, pageOffset, page.limit()); return bb; } } - private static final class FullCodecHandler implements CodecHandler + private static final class FullCodecHandler implements CodecHandler { private final TempBufferHolder _bufH = TempBufferHolder.newHolder( TempBufferHolder.Type.HARD, true); private final PageChannel _channel; - + private FullCodecHandler(PageChannel channel) { _channel = channel; } - + public boolean canEncodePartialPage() { return false; } @@ -276,17 +276,17 @@ public class CodecHandlerTest extends TestCase public boolean canDecodeInline() { return true; } - - public void decodePage(ByteBuffer inPage, ByteBuffer outPage, - int pageNumber) - throws IOException + + public void decodePage(ByteBuffer inPage, ByteBuffer outPage, + int pageNumber) + throws IOException { byte[] arr = inPage.array(); fullDecode(arr, arr, pageNumber); } - public ByteBuffer encodePage(ByteBuffer page, int pageNumber, - int pageOffset) + public ByteBuffer encodePage(ByteBuffer page, int pageNumber, + int pageOffset) throws IOException { assertEquals(0, pageOffset); diff --git a/src/test/java/com/healthmarketscience/jackcess/util/ColumnFormatterTest.java b/src/test/java/com/healthmarketscience/jackcess/util/ColumnFormatterTest.java new file mode 100644 index 0000000..d9aecb7 --- /dev/null +++ b/src/test/java/com/healthmarketscience/jackcess/util/ColumnFormatterTest.java @@ -0,0 +1,114 @@ +/* +Copyright (c) 2019 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.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import com.healthmarketscience.jackcess.Column; +import com.healthmarketscience.jackcess.ColumnBuilder; +import com.healthmarketscience.jackcess.CursorBuilder; +import com.healthmarketscience.jackcess.DataType; +import com.healthmarketscience.jackcess.Database; +import com.healthmarketscience.jackcess.Database.FileFormat; +import com.healthmarketscience.jackcess.IndexCursor; +import com.healthmarketscience.jackcess.PropertyMap; +import com.healthmarketscience.jackcess.Row; +import com.healthmarketscience.jackcess.Table; +import com.healthmarketscience.jackcess.TableBuilder; +import junit.framework.TestCase; +import static com.healthmarketscience.jackcess.TestUtil.*; +import static com.healthmarketscience.jackcess.impl.JetFormatTest.*; + +/** + * + * @author James Ahlborn + */ +public class ColumnFormatterTest extends TestCase +{ + + public void testFormat() throws Exception { + + for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { + Database db = create(fileFormat); + db.setEvaluateExpressions(true); + + Table t = new TableBuilder("test") + .addColumn(new ColumnBuilder("id", DataType.LONG).setAutoNumber(true)) + .addColumn(new ColumnBuilder("data1", DataType.TEXT) + .putProperty(PropertyMap.FORMAT_PROP, + ">@@\\x\\x")) + .addColumn(new ColumnBuilder("data2", DataType.LONG) + .putProperty(PropertyMap.FORMAT_PROP, + "#.#E+0")) + .addColumn(new ColumnBuilder("data3", DataType.MONEY) + .putProperty(PropertyMap.FORMAT_PROP, + "Currency")) + .toTable(db); + + ColumnFormatter d1Fmt = new ColumnFormatter(t.getColumn("data1")); + ColumnFormatter d2Fmt = new ColumnFormatter(t.getColumn("data2")); + ColumnFormatter d3Fmt = new ColumnFormatter(t.getColumn("data3")); + + t.addRow(Column.AUTO_NUMBER, "foobar", 37, "0.03"); + t.addRow(Column.AUTO_NUMBER, "37", 4500, 4500); + t.addRow(Column.AUTO_NUMBER, "foobarbaz", -37, "-37.13"); + t.addRow(Column.AUTO_NUMBER, null, null, null); + + List found = new ArrayList<>(); + for(Row r : t) { + found.add(d1Fmt.getRowValue(r)); + found.add(d2Fmt.getRowValue(r)); + found.add(d3Fmt.getRowValue(r)); + } + + assertEquals(Arrays.asList( + "FOxxOBAR", "3.7E+1", "$0.03", + "37xx", "4.5E+3", "$4,500.00", + "FOxxOBARBAZ", "-3.7E+1", "($37.13)", + "", "", ""), + found); + + d1Fmt.setFormatString("Scientific"); + d2Fmt.setFormatString(null); + d3Fmt.setFormatString("General Date"); + + assertEquals("Scientific", t.getColumn("data1").getProperties() + .getValue(PropertyMap.FORMAT_PROP)); + assertEquals("General Date", t.getColumn("data3").getProperties() + .getValue(PropertyMap.FORMAT_PROP)); + + found = new ArrayList<>(); + for(Row r : t) { + found.add(d1Fmt.getRowValue(r)); + found.add(d2Fmt.getRowValue(r)); + found.add(d3Fmt.getRowValue(r)); + } + + assertEquals(Arrays.asList( + "foobar", "37", "12:43:12 AM", + "3.70E+1", "4500", "4/26/1912", + "foobarbaz", "-37", "11/23/1899 3:07:12 AM", + "", "", ""), + found); + + db.close(); + } + } +} -- 2.39.5
FunctionSupported
Format[$]Partial
Format[$]Y
InStrY
InStrRevY
LCase[$]Y