aboutsummaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2019-02-11 22:26:03 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2019-02-11 22:26:03 +0000
commit831d8c0b1d0996ba63c9e2ef3b73d6a8982a3ad2 (patch)
tree863cb3ae6e4f864ba13f1bd7ab59fdc7302318c9 /src/main
parent4d6305152e060fea9e3aab12ec55bc8941d4d23f (diff)
downloadjackcess-831d8c0b1d0996ba63c9e2ef3b73d6a8982a3ad2.tar.gz
jackcess-831d8c0b1d0996ba63c9e2ef3b73d6a8982a3ad2.zip
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
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/expr/package-info.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/ColEvalContext.java6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/ColValidatorEvalContext.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java7
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java3
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/FormatUtil.java121
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/ColumnFormatter.java150
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/util/ColumnValidator.java2
8 files changed, 250 insertions, 43 deletions
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.
*
* <table border="1" width="25%" cellpadding="3" cellspacing="0">
* <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr>
- * <tr class="TableRowColor"><td>Format[$]</td><td>Partial</td></tr>
+ * <tr class="TableRowColor"><td>Format[$]</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>InStr</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>InStrRev</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>LCase[$]</td><td>Y</td></tr>
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/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<String,Fmt> PREDEF_FMTS = new HashMap<String,Fmt>();
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 <i>display only</i>
+ * (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<String,?> 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