diff options
12 files changed, 287 insertions, 255 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java index 4566225..058aea8 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java @@ -33,8 +33,8 @@ import com.healthmarketscience.jackcess.expr.Expression; import com.healthmarketscience.jackcess.expr.Identifier; import com.healthmarketscience.jackcess.expr.TemporalConfig; import com.healthmarketscience.jackcess.expr.Value; -import com.healthmarketscience.jackcess.impl.expr.BuiltinOperators; import com.healthmarketscience.jackcess.impl.expr.Expressionator; +import com.healthmarketscience.jackcess.impl.expr.ValueSupport; /** * @@ -132,28 +132,28 @@ public abstract class BaseEvalContext implements EvalContext try { val = ColumnImpl.toInternalValue(dType, val, getDatabase()); if(val == null) { - return BuiltinOperators.NULL_VAL; + return ValueSupport.NULL_VAL; } Value.Type vType = toValueType(dType); switch(vType) { case STRING: - return BuiltinOperators.toValue(val.toString()); + return ValueSupport.toValue(val.toString()); case DATE: case TIME: case DATE_TIME: - return BuiltinOperators.toValue(this, vType, (Date)val); + return ValueSupport.toValue(this, vType, (Date)val); case LONG: Integer i = ((val instanceof Integer) ? (Integer)val : ((Number)val).intValue()); - return BuiltinOperators.toValue(i); + return ValueSupport.toValue(i); case DOUBLE: Double d = ((val instanceof Double) ? (Double)val : ((Number)val).doubleValue()); - return BuiltinOperators.toValue(d); + return ValueSupport.toValue(d); case BIG_DEC: BigDecimal bd = ColumnImpl.toBigDecimal(val, getDatabase()); - return BuiltinOperators.toValue(bd); + return ValueSupport.toValue(bd); default: throw new RuntimeException("Unexpected type " + vType); } 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 eef7c77..a655a1a 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java @@ -17,15 +17,13 @@ limitations under the License. package com.healthmarketscience.jackcess.impl.expr; import java.math.BigDecimal; -import java.text.DateFormat; -import java.util.Date; import java.util.regex.Pattern; import com.healthmarketscience.jackcess.expr.EvalContext; import com.healthmarketscience.jackcess.expr.EvalException; import com.healthmarketscience.jackcess.expr.Value; -import com.healthmarketscience.jackcess.impl.ColumnImpl; import com.healthmarketscience.jackcess.impl.NumberFormatter; +import static com.healthmarketscience.jackcess.impl.expr.ValueSupport.*; /** @@ -39,27 +37,6 @@ public class BuiltinOperators private static final double MIN_INT = Integer.MIN_VALUE; private static final double MAX_INT = Integer.MAX_VALUE; - public static final Value NULL_VAL = new BaseValue() { - @Override public boolean isNull() { - return true; - } - public Type getType() { - return Type.NULL; - } - public Object get() { - return null; - } - }; - // access seems to like -1 for true and 0 for false (boolean values are - // basically an illusion) - public static final Value TRUE_VAL = new LongValue(-1); - public static final Value FALSE_VAL = new LongValue(0); - public static final Value EMPTY_STR_VAL = new StringValue(""); - public static final Value ZERO_VAL = FALSE_VAL; - public static final Value NEG_ONE_VAL = TRUE_VAL; - public static final Value ONE_VAL = new LongValue(1); - - private enum CoercionType { SIMPLE(true, true), GENERAL(false, true), COMPARE(false, false); @@ -578,95 +555,6 @@ public class BuiltinOperators } } - public static Value toValue(boolean b) { - return (b ? TRUE_VAL : FALSE_VAL); - } - - public static Value toValue(String s) { - return new StringValue(s); - } - - public static Value toValue(int i) { - return new LongValue(i); - } - - public static Value toValue(Integer i) { - return new LongValue(i); - } - - public static Value toValue(float f) { - return new DoubleValue((double)f); - } - - public static Value toValue(double s) { - return new DoubleValue(s); - } - - public static Value toValue(Double s) { - return new DoubleValue(s); - } - - public static Value toValue(BigDecimal s) { - return new BigDecimalValue(normalize(s)); - } - - public static Value toValue(Value.Type type, double dd, DateFormat fmt) { - return toValue(type, new Date(ColumnImpl.fromDateDouble( - dd, fmt.getCalendar())), fmt); - } - - public static Value toValue(EvalContext ctx, Value.Type type, Date d) { - return toValue(type, d, getDateFormatForType(ctx, type)); - } - - public static Value toValue(Value.Type type, Date d, DateFormat fmt) { - switch(type) { - case DATE: - return new DateValue(d, fmt); - case TIME: - return new TimeValue(d, fmt); - case DATE_TIME: - return new DateTimeValue(d, fmt); - default: - throw new EvalException("Unexpected date/time type " + type); - } - } - - static Value toDateValue(EvalContext ctx, Value.Type type, double v, - Value param1, Value param2) - { - DateFormat fmt = null; - if((param1 instanceof BaseDateValue) && (param1.getType() == type)) { - fmt = ((BaseDateValue)param1).getFormat(); - } else if((param2 instanceof BaseDateValue) && (param2.getType() == type)) { - fmt = ((BaseDateValue)param2).getFormat(); - } else { - fmt = getDateFormatForType(ctx, type); - } - - Date d = new Date(ColumnImpl.fromDateDouble(v, fmt.getCalendar())); - - return toValue(type, d, fmt); - } - - static DateFormat getDateFormatForType(EvalContext ctx, Value.Type type) { - String fmtStr = null; - switch(type) { - case DATE: - fmtStr = ctx.getTemporalConfig().getDefaultDateFormat(); - break; - case TIME: - fmtStr = ctx.getTemporalConfig().getDefaultTimeFormat(); - break; - case DATE_TIME: - fmtStr = ctx.getTemporalConfig().getDefaultDateTimeFormat(); - break; - default: - throw new EvalException("Unexpected date/time type " + type); - } - return ctx.createDateFormat(fmtStr); - } - private static Value.Type getMathTypePrecedence( Value param1, Value param2, CoercionType cType) { @@ -779,22 +667,4 @@ public class BuiltinOperators return ((d == id) && (d >= MIN_INT) && (d <= MAX_INT) && !Double.isInfinite(d) && !Double.isNaN(d)); } - - /** - * Converts the given BigDecimal to the minimal scale >= 0; - */ - static BigDecimal normalize(BigDecimal bd) { - if(bd.scale() == 0) { - return bd; - } - // handle a bug in the jdk which doesn't strip zero values - if(bd.compareTo(BigDecimal.ZERO) == 0) { - return BigDecimal.ZERO; - } - bd = bd.stripTrailingZeros(); - if(bd.scale() < 0) { - bd = bd.setScale(0); - } - return bd; - } } 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 83c6ee8..2168ceb 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultDateFunctions.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultDateFunctions.java @@ -56,9 +56,9 @@ public class DefaultDateFunctions public static final Function DATE = registerFunc(new Func0("Date") { @Override protected Value eval0(EvalContext ctx) { - DateFormat fmt = BuiltinOperators.getDateFormatForType(ctx, Value.Type.DATE); + DateFormat fmt = ValueSupport.getDateFormatForType(ctx, Value.Type.DATE); double dd = dateOnly(currentTimeDouble(fmt)); - return BuiltinOperators.toValue(Value.Type.DATE, dd, fmt); + return ValueSupport.toValue(Value.Type.DATE, dd, fmt); } }); @@ -70,8 +70,8 @@ public class DefaultDateFunctions return dv; } double dd = dateOnly(dv.getAsDouble()); - DateFormat fmt = BuiltinOperators.getDateFormatForType(ctx, Value.Type.DATE); - return BuiltinOperators.toValue(Value.Type.DATE, dd, fmt); + DateFormat fmt = ValueSupport.getDateFormatForType(ctx, Value.Type.DATE); + return ValueSupport.toValue(Value.Type.DATE, dd, fmt); } }); @@ -87,7 +87,7 @@ public class DefaultDateFunctions year += ((year <= 29) ? 2000 : 1900); } - DateFormat fmt = BuiltinOperators.getDateFormatForType(ctx, Value.Type.DATE); + DateFormat fmt = ValueSupport.getDateFormatForType(ctx, Value.Type.DATE); Calendar cal = fmt.getCalendar(); cal.clear(); @@ -96,24 +96,24 @@ public class DefaultDateFunctions cal.set(Calendar.MONTH, month - 1); cal.set(Calendar.DAY_OF_MONTH, day); - return BuiltinOperators.toValue(Value.Type.DATE, cal.getTime(), fmt); + return ValueSupport.toValue(Value.Type.DATE, cal.getTime(), fmt); } }); public static final Function NOW = registerFunc(new Func0("Now") { @Override protected Value eval0(EvalContext ctx) { - DateFormat fmt = BuiltinOperators.getDateFormatForType(ctx, Value.Type.DATE_TIME); - return BuiltinOperators.toValue(Value.Type.DATE_TIME, new Date(), fmt); + DateFormat fmt = ValueSupport.getDateFormatForType(ctx, Value.Type.DATE_TIME); + return ValueSupport.toValue(Value.Type.DATE_TIME, new Date(), fmt); } }); public static final Function TIME = registerFunc(new Func0("Time") { @Override protected Value eval0(EvalContext ctx) { - DateFormat fmt = BuiltinOperators.getDateFormatForType(ctx, Value.Type.TIME); + DateFormat fmt = ValueSupport.getDateFormatForType(ctx, Value.Type.TIME); double dd = timeOnly(currentTimeDouble(fmt)); - return BuiltinOperators.toValue(Value.Type.TIME, dd, fmt); + return ValueSupport.toValue(Value.Type.TIME, dd, fmt); } }); @@ -125,17 +125,17 @@ public class DefaultDateFunctions return dv; } double dd = timeOnly(dv.getAsDouble()); - DateFormat fmt = BuiltinOperators.getDateFormatForType(ctx, Value.Type.TIME); - return BuiltinOperators.toValue(Value.Type.TIME, dd, fmt); + DateFormat fmt = ValueSupport.getDateFormatForType(ctx, Value.Type.TIME); + return ValueSupport.toValue(Value.Type.TIME, dd, fmt); } }); public static final Function TIMER = registerFunc(new Func0("Timer") { @Override protected Value eval0(EvalContext ctx) { - DateFormat fmt = BuiltinOperators.getDateFormatForType(ctx, Value.Type.TIME); + DateFormat fmt = ValueSupport.getDateFormatForType(ctx, Value.Type.TIME); double dd = timeOnly(currentTimeDouble(fmt)) * DSECONDS_PER_DAY; - return BuiltinOperators.toValue(dd); + return ValueSupport.toValue(dd); } }); @@ -156,16 +156,16 @@ public class DefaultDateFunctions totalSeconds %= SECONDS_PER_DAY; } - DateFormat fmt = BuiltinOperators.getDateFormatForType(ctx, Value.Type.TIME); + DateFormat fmt = ValueSupport.getDateFormatForType(ctx, Value.Type.TIME); double dd = totalSeconds / DSECONDS_PER_DAY; - return BuiltinOperators.toValue(Value.Type.TIME, dd, fmt); + return ValueSupport.toValue(Value.Type.TIME, dd, fmt); } }); public static final Function HOUR = registerFunc(new Func1NullIsNull("Hour") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue( + return ValueSupport.toValue( nonNullToCalendarField(ctx, param1, Calendar.HOUR_OF_DAY)); } }); @@ -173,7 +173,7 @@ public class DefaultDateFunctions public static final Function MINUTE = registerFunc(new Func1NullIsNull("Minute") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue( + return ValueSupport.toValue( nonNullToCalendarField(ctx, param1, Calendar.MINUTE)); } }); @@ -181,7 +181,7 @@ public class DefaultDateFunctions public static final Function SECOND = registerFunc(new Func1NullIsNull("Second") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue( + return ValueSupport.toValue( nonNullToCalendarField(ctx, param1, Calendar.SECOND)); } }); @@ -189,7 +189,7 @@ public class DefaultDateFunctions public static final Function YEAR = registerFunc(new Func1NullIsNull("Year") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue( + return ValueSupport.toValue( nonNullToCalendarField(ctx, param1, Calendar.YEAR)); } }); @@ -198,7 +198,7 @@ public class DefaultDateFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { // convert from 0 based to 1 based value - return BuiltinOperators.toValue( + return ValueSupport.toValue( nonNullToCalendarField(ctx, param1, Calendar.MONTH) + 1); } }); @@ -206,7 +206,7 @@ public class DefaultDateFunctions public static final Function DAY = registerFunc(new Func1NullIsNull("Day") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue( + return ValueSupport.toValue( nonNullToCalendarField(ctx, param1, Calendar.DAY_OF_MONTH)); } }); @@ -234,7 +234,7 @@ public class DefaultDateFunctions // back to 1 based to return the result day = (((day - 1) - (firstDay - 1) + 7) % 7) + 1; - return BuiltinOperators.toValue(day); + return ValueSupport.toValue(day); } }); @@ -292,14 +292,14 @@ public class DefaultDateFunctions Value.Type type = (hasDate ? (hasTime ? Value.Type.DATE_TIME : Value.Type.DATE) : Value.Type.TIME); - DateFormat fmt = BuiltinOperators.getDateFormatForType(ctx, type); - return BuiltinOperators.toValue(type, dd, fmt); + DateFormat fmt = ValueSupport.getDateFormatForType(ctx, type); + return ValueSupport.toValue(type, dd, fmt); } private static DateFormat getDateValueFormat(EvalContext ctx, Value param) { return ((param instanceof BaseDateValue) ? ((BaseDateValue)param).getFormat() : - BuiltinOperators.getDateFormatForType(ctx, param.getType())); + ValueSupport.getDateFormatForType(ctx, param.getType())); } private static double dateOnly(double dd) { diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFinancialFunctions.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFinancialFunctions.java index df397b0..3ca6725 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFinancialFunctions.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFinancialFunctions.java @@ -65,7 +65,7 @@ public class DefaultFinancialFunctions result += calculateAnnuityPaymentPeriods(rate, pmt, fv, pmtType); } - return BuiltinOperators.toValue(result); + return ValueSupport.toValue(result); } }); @@ -92,7 +92,7 @@ public class DefaultFinancialFunctions double result = calculateFutureValue(rate, nper, pmt, pmtType); - return BuiltinOperators.toValue(result); + return ValueSupport.toValue(result); } }); @@ -119,7 +119,7 @@ public class DefaultFinancialFunctions double result = calculatePresentValue(rate, nper, pmt, pmtType); - return BuiltinOperators.toValue(result); + return ValueSupport.toValue(result); } }); @@ -146,7 +146,7 @@ public class DefaultFinancialFunctions result += calculateAnnuityPayment(rate, nper, fv, pmtType); } - return BuiltinOperators.toValue(result); + return ValueSupport.toValue(result); } }); @@ -177,7 +177,7 @@ public class DefaultFinancialFunctions // double result = calculateInterestPayment(pmt, rate, per, pv, pmtType); - // return BuiltinOperators.toValue(result); + // return ValueSupport.toValue(result); // } // }); @@ -209,7 +209,7 @@ public class DefaultFinancialFunctions // double result = pmt - calculateInterestPayment(pmt, rate, per, pv, // pmtType); - // return BuiltinOperators.toValue(result); + // return ValueSupport.toValue(result); // } // }); @@ -255,7 +255,7 @@ public class DefaultFinancialFunctions // cost -= result; // } - // return BuiltinOperators.toValue(result); + // return ValueSupport.toValue(result); // } // }); @@ -269,7 +269,7 @@ public class DefaultFinancialFunctions // double result = calculateStraightLineDepreciation(cost, salvage, life); - // return BuiltinOperators.toValue(result); + // return ValueSupport.toValue(result); // } // }); @@ -285,7 +285,7 @@ public class DefaultFinancialFunctions // double result = calculateSumOfYearsDepreciation( // cost, salvage, life, period); - // return BuiltinOperators.toValue(result); + // return ValueSupport.toValue(result); // } // }); 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 bfbf105..e927f9a 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java @@ -69,10 +69,10 @@ public class DefaultFunctions protected Value eval1(EvalContext ctx, Value param1) { if((param1.getType() == Value.Type.STRING) && (param1.getAsString().length() == 0)) { - return BuiltinOperators.ZERO_VAL; + return ValueSupport.ZERO_VAL; } int lv = param1.getAsLongInt(); - return BuiltinOperators.toValue(Integer.toHexString(lv).toUpperCase()); + return ValueSupport.toValue(Integer.toHexString(lv).toUpperCase()); } }); @@ -89,7 +89,7 @@ public class DefaultFunctions Value.Type resultType = ctx.getResultType(); return (((resultType == null) || (resultType == Value.Type.STRING)) ? - BuiltinOperators.EMPTY_STR_VAL : BuiltinOperators.ZERO_VAL); + ValueSupport.EMPTY_STR_VAL : ValueSupport.ZERO_VAL); } }); @@ -99,7 +99,7 @@ public class DefaultFunctions Value param1 = params[0]; int idx = param1.getAsLongInt(); if((idx < 1) || (idx >= params.length)) { - return BuiltinOperators.NULL_VAL; + return ValueSupport.NULL_VAL; } return params[idx]; } @@ -116,7 +116,7 @@ public class DefaultFunctions return params[i + 1]; } } - return BuiltinOperators.NULL_VAL; + return ValueSupport.NULL_VAL; } }); @@ -125,10 +125,10 @@ public class DefaultFunctions protected Value eval1(EvalContext ctx, Value param1) { if((param1.getType() == Value.Type.STRING) && (param1.getAsString().length() == 0)) { - return BuiltinOperators.ZERO_VAL; + return ValueSupport.ZERO_VAL; } int lv = param1.getAsLongInt(); - return BuiltinOperators.toValue(Integer.toOctalString(lv)); + return ValueSupport.toValue(Integer.toOctalString(lv)); } }); @@ -136,7 +136,7 @@ public class DefaultFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { boolean b = param1.getAsBoolean(); - return BuiltinOperators.toValue(b); + return ValueSupport.toValue(b); } }); @@ -147,7 +147,7 @@ public class DefaultFunctions if((lv < 0) || (lv > 255)) { throw new EvalException("Byte code '" + lv + "' out of range "); } - return BuiltinOperators.toValue(lv); + return ValueSupport.toValue(lv); } }); @@ -156,7 +156,7 @@ public class DefaultFunctions protected Value eval1(EvalContext ctx, Value param1) { BigDecimal bd = param1.getAsBigDecimal(); bd = bd.setScale(4, NumberFormatter.ROUND_MODE); - return BuiltinOperators.toValue(bd); + return ValueSupport.toValue(bd); } }); @@ -174,7 +174,7 @@ public class DefaultFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { Double dv = param1.getAsDouble(); - return BuiltinOperators.toValue(dv); + return ValueSupport.toValue(dv); } }); @@ -182,7 +182,7 @@ public class DefaultFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { BigDecimal bd = param1.getAsBigDecimal(); - return BuiltinOperators.toValue(bd); + return ValueSupport.toValue(bd); } }); @@ -193,7 +193,7 @@ public class DefaultFunctions if((lv < Short.MIN_VALUE) || (lv > Short.MAX_VALUE)) { throw new EvalException("Int value '" + lv + "' out of range "); } - return BuiltinOperators.toValue(lv); + return ValueSupport.toValue(lv); } }); @@ -201,7 +201,7 @@ public class DefaultFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { int lv = param1.getAsLongInt(); - return BuiltinOperators.toValue(lv); + return ValueSupport.toValue(lv); } }); @@ -212,14 +212,14 @@ public class DefaultFunctions if((dv < Float.MIN_VALUE) || (dv > Float.MAX_VALUE)) { throw new EvalException("Single value '" + dv + "' out of range "); } - return BuiltinOperators.toValue(dv.floatValue()); + return ValueSupport.toValue(dv.floatValue()); } }); public static final Function CSTR = registerFunc(new Func1("CStr") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue(param1.getAsString()); + return ValueSupport.toValue(param1.getAsString()); } }); @@ -233,14 +233,14 @@ public class DefaultFunctions public static final Function ISNULL = registerFunc(new Func1("IsNull") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue(param1.isNull()); + return ValueSupport.toValue(param1.isNull()); } }); public static final Function ISDATE = registerFunc(new Func1("IsDate") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue( + return ValueSupport.toValue( !param1.isNull() && (DefaultDateFunctions.nonNullToDateValue(ctx, param1) != null)); } @@ -281,7 +281,7 @@ public class DefaultFunctions default: throw new EvalException("Unknown type " + type); } - return BuiltinOperators.toValue(vType); + return ValueSupport.toValue(vType); } }); @@ -314,7 +314,7 @@ public class DefaultFunctions default: throw new EvalException("Unknown type " + type); } - return BuiltinOperators.toValue(tName); + return ValueSupport.toValue(tName); } }); 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 aeca27d..278306e 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java @@ -50,14 +50,14 @@ public class DefaultNumberFunctions case DATE_TIME: // dates/times get converted to date doubles for arithmetic double result = Math.abs(param1.getAsDouble()); - return BuiltinOperators.toDateValue(ctx, mathType, result, param1, null); + return ValueSupport.toDateValue(ctx, mathType, result, param1, null); case LONG: - return BuiltinOperators.toValue(Math.abs(param1.getAsLongInt())); + return ValueSupport.toValue(Math.abs(param1.getAsLongInt())); case DOUBLE: - return BuiltinOperators.toValue(Math.abs(param1.getAsDouble())); + return ValueSupport.toValue(Math.abs(param1.getAsDouble())); case STRING: case BIG_DEC: - return BuiltinOperators.toValue(param1.getAsBigDecimal().abs( + return ValueSupport.toValue(param1.getAsBigDecimal().abs( NumberFormatter.DEC_MATH_CONTEXT)); default: throw new EvalException("Unexpected type " + mathType); @@ -68,21 +68,21 @@ public class DefaultNumberFunctions public static final Function ATAN = registerFunc(new Func1("Atan") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue(Math.atan(param1.getAsDouble())); + return ValueSupport.toValue(Math.atan(param1.getAsDouble())); } }); public static final Function COS = registerFunc(new Func1("Cos") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue(Math.cos(param1.getAsDouble())); + return ValueSupport.toValue(Math.cos(param1.getAsDouble())); } }); public static final Function EXP = registerFunc(new Func1("Exp") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue(Math.exp(param1.getAsDouble())); + return ValueSupport.toValue(Math.exp(param1.getAsDouble())); } }); @@ -92,7 +92,7 @@ public class DefaultNumberFunctions if(param1.getType().isIntegral()) { return param1; } - return BuiltinOperators.toValue(param1.getAsDouble().intValue()); + return ValueSupport.toValue(param1.getAsDouble().intValue()); } }); @@ -102,14 +102,14 @@ public class DefaultNumberFunctions if(param1.getType().isIntegral()) { return param1; } - return BuiltinOperators.toValue((int)Math.floor(param1.getAsDouble())); + return ValueSupport.toValue((int)Math.floor(param1.getAsDouble())); } }); public static final Function LOG = registerFunc(new Func1("Log") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue(Math.log(param1.getAsDouble())); + return ValueSupport.toValue(Math.log(param1.getAsDouble())); } }); @@ -121,7 +121,7 @@ public class DefaultNumberFunctions @Override protected Value evalVar(EvalContext ctx, Value[] params) { Integer seed = ((params.length > 0) ? params[0].getAsLongInt() : null); - return BuiltinOperators.toValue(ctx.getRandom(seed)); + return ValueSupport.toValue(ctx.getRandom(seed)); } }); @@ -141,7 +141,7 @@ public class DefaultNumberFunctions } BigDecimal bd = param1.getAsBigDecimal() .setScale(scale, NumberFormatter.ROUND_MODE); - return BuiltinOperators.toValue(bd); + return ValueSupport.toValue(bd); } }); @@ -154,9 +154,9 @@ public class DefaultNumberFunctions } else { val = param1.getAsBigDecimal().signum(); } - return ((val > 0) ? BuiltinOperators.ONE_VAL : - ((val < 0) ? BuiltinOperators.NEG_ONE_VAL : - BuiltinOperators.ZERO_VAL)); + return ((val > 0) ? ValueSupport.ONE_VAL : + ((val < 0) ? ValueSupport.NEG_ONE_VAL : + ValueSupport.ZERO_VAL)); } }); @@ -167,21 +167,21 @@ public class DefaultNumberFunctions if(dv < 0.0d) { throw new EvalException("Invalid value '" + dv + "'"); } - return BuiltinOperators.toValue(Math.sqrt(dv)); + return ValueSupport.toValue(Math.sqrt(dv)); } }); public static final Function SIN = registerFunc(new Func1("Sin") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue(Math.sin(param1.getAsDouble())); + return ValueSupport.toValue(Math.sin(param1.getAsDouble())); } }); public static final Function TAN = registerFunc(new Func1("Tan") { @Override protected Value eval1(EvalContext ctx, Value param1) { - return BuiltinOperators.toValue(Math.tan(param1.getAsDouble())); + return ValueSupport.toValue(Math.tan(param1.getAsDouble())); } }); 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 18f7c1f..bd1aac2 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultTextFunctions.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultTextFunctions.java @@ -51,7 +51,7 @@ public class DefaultTextFunctions throw new EvalException("Character code '" + lv + "' out of range "); } - return BuiltinOperators.toValue(lv); + return ValueSupport.toValue(lv); } }); @@ -64,7 +64,7 @@ public class DefaultTextFunctions throw new EvalException("No characters in string"); } int lv = str.charAt(0); - return BuiltinOperators.toValue(lv); + return ValueSupport.toValue(lv); } }); @@ -77,7 +77,7 @@ public class DefaultTextFunctions "' out of range "); } char[] cs = Character.toChars(lv); - return BuiltinOperators.toValue(new String(cs)); + return ValueSupport.toValue(new String(cs)); } }); @@ -86,7 +86,7 @@ public class DefaultTextFunctions protected Value eval1(EvalContext ctx, Value param1) { int lv = param1.getAsLongInt(); char[] cs = Character.toChars(lv); - return BuiltinOperators.toValue(new String(cs)); + return ValueSupport.toValue(new String(cs)); } }); @@ -98,7 +98,7 @@ public class DefaultTextFunctions if(bd.compareTo(BigDecimal.ZERO) >= 0) { str = " " + str; } - return BuiltinOperators.toValue(str); + return ValueSupport.toValue(str); } }); @@ -119,7 +119,7 @@ public class DefaultTextFunctions String s1 = param1.getAsString(); int s1Len = s1.length(); if(s1Len == 0) { - return BuiltinOperators.ZERO_VAL; + return ValueSupport.ZERO_VAL; } Value param2 = params[idx++]; if(param2.isNull()) { @@ -129,7 +129,7 @@ public class DefaultTextFunctions int s2Len = s2.length(); if(s2Len == 0) { // 1 based offsets - return BuiltinOperators.toValue(start + 1); + return ValueSupport.toValue(start + 1); } boolean ignoreCase = true; if(params.length > 3) { @@ -139,11 +139,11 @@ public class DefaultTextFunctions while(start < end) { if(s1.regionMatches(ignoreCase, start, s2, 0, s2Len)) { // 1 based offsets - return BuiltinOperators.toValue(start + 1); + return ValueSupport.toValue(start + 1); } ++start; } - return BuiltinOperators.ZERO_VAL; + return ValueSupport.ZERO_VAL; } }); @@ -157,7 +157,7 @@ public class DefaultTextFunctions String s1 = param1.getAsString(); int s1Len = s1.length(); if(s1Len == 0) { - return BuiltinOperators.ZERO_VAL; + return ValueSupport.ZERO_VAL; } Value param2 = params[1]; if(param2.isNull()) { @@ -168,7 +168,7 @@ public class DefaultTextFunctions int start = s1Len - 1; if(s2Len == 0) { // 1 based offsets - return BuiltinOperators.toValue(start + 1); + return ValueSupport.toValue(start + 1); } if(params.length > 2) { start = params[2].getAsLongInt(); @@ -186,11 +186,11 @@ public class DefaultTextFunctions while(start >= 0) { if(s1.regionMatches(ignoreCase, start, s2, 0, s2Len)) { // 1 based offsets - return BuiltinOperators.toValue(start + 1); + return ValueSupport.toValue(start + 1); } --start; } - return BuiltinOperators.ZERO_VAL; + return ValueSupport.ZERO_VAL; } }); @@ -198,7 +198,7 @@ public class DefaultTextFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { String str = param1.getAsString(); - return BuiltinOperators.toValue(str.toLowerCase()); + return ValueSupport.toValue(str.toLowerCase()); } }); @@ -206,7 +206,7 @@ public class DefaultTextFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { String str = param1.getAsString(); - return BuiltinOperators.toValue(str.toUpperCase()); + return ValueSupport.toValue(str.toUpperCase()); } }); @@ -218,7 +218,7 @@ public class DefaultTextFunctions } String str = param1.getAsString(); int len = Math.min(str.length(), param2.getAsLongInt()); - return BuiltinOperators.toValue(str.substring(0, len)); + return ValueSupport.toValue(str.substring(0, len)); } }); @@ -231,7 +231,7 @@ public class DefaultTextFunctions String str = param1.getAsString(); int strLen = str.length(); int len = Math.min(strLen, param2.getAsLongInt()); - return BuiltinOperators.toValue(str.substring(strLen - len, strLen)); + return ValueSupport.toValue(str.substring(strLen - len, strLen)); } }); @@ -249,7 +249,7 @@ public class DefaultTextFunctions int len = Math.min( ((params.length > 2) ? params[2].getAsLongInt() : strLen), (strLen - start)); - return BuiltinOperators.toValue(str.substring(start, start + len)); + return ValueSupport.toValue(str.substring(start, start + len)); } }); @@ -257,7 +257,7 @@ public class DefaultTextFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { String str = param1.getAsString(); - return BuiltinOperators.toValue(str.length()); + return ValueSupport.toValue(str.length()); } }); @@ -265,7 +265,7 @@ public class DefaultTextFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { String str = param1.getAsString(); - return BuiltinOperators.toValue(trim(str, true, false)); + return ValueSupport.toValue(trim(str, true, false)); } }); @@ -273,7 +273,7 @@ public class DefaultTextFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { String str = param1.getAsString(); - return BuiltinOperators.toValue(trim(str, false, true)); + return ValueSupport.toValue(trim(str, false, true)); } }); @@ -281,7 +281,7 @@ public class DefaultTextFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { String str = param1.getAsString(); - return BuiltinOperators.toValue(trim(str, true, true)); + return ValueSupport.toValue(trim(str, true, true)); } }); @@ -289,7 +289,7 @@ public class DefaultTextFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { int lv = param1.getAsLongInt(); - return BuiltinOperators.toValue(nchars(lv, ' ')); + return ValueSupport.toValue(nchars(lv, ' ')); } }); @@ -299,7 +299,7 @@ public class DefaultTextFunctions Value param1 = params[0]; Value param2 = params[1]; if(param1.isNull() || param2.isNull()) { - return BuiltinOperators.NULL_VAL; + return ValueSupport.NULL_VAL; } String s1 = param1.getAsString(); String s2 = param2.getAsString(); @@ -310,9 +310,9 @@ public class DefaultTextFunctions int cmp = (ignoreCase ? s1.compareToIgnoreCase(s2) : s1.compareTo(s2)); // stupid java doesn't return 1, -1, 0... - return ((cmp < 0) ? BuiltinOperators.NEG_ONE_VAL : - ((cmp > 0) ? BuiltinOperators.ONE_VAL : - BuiltinOperators.ZERO_VAL)); + return ((cmp < 0) ? ValueSupport.NEG_ONE_VAL : + ((cmp > 0) ? ValueSupport.ONE_VAL : + ValueSupport.ZERO_VAL)); } }); @@ -320,11 +320,11 @@ public class DefaultTextFunctions @Override protected Value eval2(EvalContext ctx, Value param1, Value param2) { if(param1.isNull() || param2.isNull()) { - return BuiltinOperators.NULL_VAL; + return ValueSupport.NULL_VAL; } int lv = param1.getAsLongInt(); char c = (char)(param2.getAsString().charAt(0) % 256); - return BuiltinOperators.toValue(nchars(lv, c)); + return ValueSupport.toValue(nchars(lv, c)); } }); @@ -332,7 +332,7 @@ public class DefaultTextFunctions @Override protected Value eval1(EvalContext ctx, Value param1) { String str = param1.getAsString(); - return BuiltinOperators.toValue( + return ValueSupport.toValue( new StringBuilder(str).reverse().toString()); } }); 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 04c8aa0..f948d15 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java @@ -401,11 +401,11 @@ public class Expressionator private static final Expr THIS_COL_VALUE = new EThisValue(); private static final Expr NULL_VALUE = new EConstValue( - BuiltinOperators.NULL_VAL, "Null"); + ValueSupport.NULL_VAL, "Null"); private static final Expr TRUE_VALUE = new EConstValue( - BuiltinOperators.TRUE_VAL, "True"); + ValueSupport.TRUE_VAL, "True"); private static final Expr FALSE_VALUE = new EConstValue( - BuiltinOperators.FALSE_VAL, "False"); + ValueSupport.FALSE_VAL, "False"); private Expressionator() {} @@ -1302,7 +1302,7 @@ public class Expressionator { switch(valType) { case STRING: - return BuiltinOperators.toValue((String)value); + return ValueSupport.toValue((String)value); case DATE: return new DateValue((Date)value, sdf); case TIME: @@ -1310,11 +1310,11 @@ public class Expressionator case DATE_TIME: return new DateTimeValue((Date)value, sdf); case LONG: - return BuiltinOperators.toValue((Integer)value); + return ValueSupport.toValue((Integer)value); case DOUBLE: - return BuiltinOperators.toValue((Double)value); + return ValueSupport.toValue((Double)value); case BIG_DEC: - return BuiltinOperators.toValue((BigDecimal)value); + return ValueSupport.toValue((BigDecimal)value); default: throw new ParseException("unexpected literal type " + valType); } 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 014e371..d3e2cac 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java @@ -75,7 +75,7 @@ public class StringValue extends BaseValue if(_num == null) { // see if it is parseable as a number try { - _num = BuiltinOperators.normalize(new BigDecimal(_val)); + _num = ValueSupport.normalize(new BigDecimal(_val)); return (BigDecimal)_num; } catch(NumberFormatException nfe) { _num = NOT_A_NUMBER; diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/ValueSupport.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/ValueSupport.java new file mode 100644 index 0000000..279a4f0 --- /dev/null +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/ValueSupport.java @@ -0,0 +1,162 @@ +/* +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.expr; + +import java.math.BigDecimal; +import java.text.DateFormat; +import java.util.Date; + +import com.healthmarketscience.jackcess.expr.EvalContext; +import com.healthmarketscience.jackcess.expr.EvalException; +import com.healthmarketscience.jackcess.expr.Value; +import com.healthmarketscience.jackcess.impl.ColumnImpl; + +/** + * + * @author James Ahlborn + */ +public class ValueSupport +{ + public static final Value NULL_VAL = new BaseValue() { + @Override public boolean isNull() { + return true; + } + public Type getType() { + return Type.NULL; + } + public Object get() { + return null; + } + }; + // access seems to like -1 for true and 0 for false (boolean values are + // basically an illusion) + public static final Value TRUE_VAL = new LongValue(-1); + public static final Value FALSE_VAL = new LongValue(0); + public static final Value EMPTY_STR_VAL = new StringValue(""); + public static final Value ZERO_VAL = FALSE_VAL; + public static final Value NEG_ONE_VAL = TRUE_VAL; + public static final Value ONE_VAL = new LongValue(1); + + private ValueSupport() {} + + public static Value toValue(boolean b) { + return (b ? TRUE_VAL : FALSE_VAL); + } + + public static Value toValue(String s) { + return new StringValue(s); + } + + public static Value toValue(int i) { + return new LongValue(i); + } + + public static Value toValue(Integer i) { + return new LongValue(i); + } + + public static Value toValue(float f) { + return new DoubleValue((double)f); + } + + public static Value toValue(double s) { + return new DoubleValue(s); + } + + public static Value toValue(Double s) { + return new DoubleValue(s); + } + + public static Value toValue(BigDecimal s) { + return new BigDecimalValue(normalize(s)); + } + + public static Value toValue(Value.Type type, double dd, DateFormat fmt) { + return toValue(type, new Date(ColumnImpl.fromDateDouble( + dd, fmt.getCalendar())), fmt); + } + + public static Value toValue(EvalContext ctx, Value.Type type, Date d) { + return toValue(type, d, getDateFormatForType(ctx, type)); + } + + public static Value toValue(Value.Type type, Date d, DateFormat fmt) { + switch(type) { + case DATE: + return new DateValue(d, fmt); + case TIME: + return new TimeValue(d, fmt); + case DATE_TIME: + return new DateTimeValue(d, fmt); + default: + throw new EvalException("Unexpected date/time type " + type); + } + } + + static Value toDateValue(EvalContext ctx, Value.Type type, double v, + Value param1, Value param2) + { + DateFormat fmt = null; + if((param1 instanceof BaseDateValue) && (param1.getType() == type)) { + fmt = ((BaseDateValue)param1).getFormat(); + } else if((param2 instanceof BaseDateValue) && (param2.getType() == type)) { + fmt = ((BaseDateValue)param2).getFormat(); + } else { + fmt = getDateFormatForType(ctx, type); + } + + Date d = new Date(ColumnImpl.fromDateDouble(v, fmt.getCalendar())); + + return toValue(type, d, fmt); + } + + static DateFormat getDateFormatForType(EvalContext ctx, Value.Type type) { + String fmtStr = null; + switch(type) { + case DATE: + fmtStr = ctx.getTemporalConfig().getDefaultDateFormat(); + break; + case TIME: + fmtStr = ctx.getTemporalConfig().getDefaultTimeFormat(); + break; + case DATE_TIME: + fmtStr = ctx.getTemporalConfig().getDefaultDateTimeFormat(); + break; + default: + throw new EvalException("Unexpected date/time type " + type); + } + return ctx.createDateFormat(fmtStr); + } + + /** + * Converts the given BigDecimal to the minimal scale >= 0; + */ + static BigDecimal normalize(BigDecimal bd) { + if(bd.scale() == 0) { + return bd; + } + // handle a bug in the jdk which doesn't strip zero values + if(bd.compareTo(BigDecimal.ZERO) == 0) { + return BigDecimal.ZERO; + } + bd = bd.stripTrailingZeros(); + if(bd.scale() < 0) { + bd = bd.setScale(0); + } + return bd; + } +} diff --git a/src/test/java/com/healthmarketscience/jackcess/PropertyExpressionTest.java b/src/test/java/com/healthmarketscience/jackcess/PropertyExpressionTest.java index b73496f..e5d7295 100644 --- a/src/test/java/com/healthmarketscience/jackcess/PropertyExpressionTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/PropertyExpressionTest.java @@ -26,9 +26,9 @@ import com.healthmarketscience.jackcess.expr.Function; import com.healthmarketscience.jackcess.expr.FunctionLookup; import com.healthmarketscience.jackcess.expr.TemporalConfig; import com.healthmarketscience.jackcess.expr.Value; -import com.healthmarketscience.jackcess.impl.expr.BuiltinOperators; import com.healthmarketscience.jackcess.impl.expr.DefaultFunctions; import com.healthmarketscience.jackcess.impl.expr.FunctionSupport; +import com.healthmarketscience.jackcess.impl.expr.ValueSupport; import junit.framework.TestCase; import static com.healthmarketscience.jackcess.Database.*; @@ -354,7 +354,7 @@ public class PropertyExpressionTest extends TestCase @Override protected Value eval0(EvalContext ctx) { Object val = ctx.get("someKey"); - return BuiltinOperators.toValue("FOO_" + val); + return ValueSupport.toValue("FOO_" + val); } }; } 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 8e077b0..188172d 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java @@ -429,12 +429,12 @@ public class ExpressionatorTest extends TestCase TestContext tc = new TestContext() { @Override public Value getThisColumnValue() { - return BuiltinOperators.toValue(23.0); + return ValueSupport.toValue(23.0); } @Override public Value getIdentifierValue(Identifier identifier) { - return BuiltinOperators.toValue(23.0); + return ValueSupport.toValue(23.0); } }; @@ -536,7 +536,7 @@ public class ExpressionatorTest extends TestCase } private static Boolean evalCondition(String exprStr, String thisVal) { - TestContext tc = new TestContext(BuiltinOperators.toValue(thisVal)); + TestContext tc = new TestContext(ValueSupport.toValue(thisVal)); Expression expr = Expressionator.parse( Expressionator.Type.FIELD_VALIDATOR, exprStr, null, tc); return (Boolean)expr.eval(tc); @@ -552,7 +552,7 @@ public class ExpressionatorTest extends TestCase } static BigDecimal toBD(BigDecimal bd) { - return BuiltinOperators.normalize(bd); + return ValueSupport.normalize(bd); } private static class TestContext |