import java.text.DecimalFormatSymbols;
import java.util.Locale;
+import com.healthmarketscience.jackcess.impl.expr.FormatUtil;
/**
* A NumericConfig encapsulates number formatting options for expression
public static final NumericConfig US_NUMERIC_CONFIG = new NumericConfig(
2, true, false, true, 3, Locale.US);
+ public enum Type {
+ CURRENCY, FIXED, STANDARD, PERCENT, SCIENTIFIC;
+ }
+
private final int _numDecDigits;
private final boolean _incLeadingDigit;
private final boolean _useNegParens;
private final boolean _useNegCurrencyParens;
private final int _numGroupDigits;
private final DecimalFormatSymbols _symbols;
+ private final String _currencyFormat;
+ private final String _fixedFormat;
+ private final String _standardFormat;
+ private final String _percentFormat;
+ private final String _scientificFormat;
public NumericConfig(int numDecDigits, boolean incLeadingDigit,
boolean useNegParens, boolean useNegCurrencyParens,
_useNegCurrencyParens = useNegCurrencyParens;
_numGroupDigits = numGroupDigits;
_symbols = DecimalFormatSymbols.getInstance(locale);
+
+ _currencyFormat = FormatUtil.createNumberFormatPattern(
+ FormatUtil.NumPatternType.CURRENCY, _numDecDigits, _incLeadingDigit,
+ _useNegCurrencyParens, _numGroupDigits);
+ _fixedFormat = FormatUtil.createNumberFormatPattern(
+ FormatUtil.NumPatternType.GENERAL, _numDecDigits, true,
+ _useNegParens, 0);
+ _standardFormat = FormatUtil.createNumberFormatPattern(
+ FormatUtil.NumPatternType.GENERAL, _numDecDigits, _incLeadingDigit,
+ _useNegParens, _numGroupDigits);
+ _percentFormat = FormatUtil.createNumberFormatPattern(
+ FormatUtil.NumPatternType.PERCENT, _numDecDigits, _incLeadingDigit,
+ _useNegParens, 0);
+ _scientificFormat = FormatUtil.createNumberFormatPattern(
+ FormatUtil.NumPatternType.SCIENTIFIC, _numDecDigits, true,
+ false, 0);
}
public int getNumDecimalDigits() {
return _numGroupDigits;
}
+ public String getNumberFormat(Type type) {
+ switch(type) {
+ case CURRENCY:
+ return _currencyFormat;
+ case FIXED:
+ return _fixedFormat;
+ case STANDARD:
+ return _standardFormat;
+ case PERCENT:
+ return _percentFormat;
+ case SCIENTIFIC:
+ return _scientificFormat;
+ default:
+ throw new IllegalArgumentException("unknown number type " + type);
+ }
+ }
+
public DecimalFormatSymbols getDecimalFormatSymbols() {
return _symbols;
}
{
NULL, STRING, DATE, TIME, DATE_TIME, LONG, DOUBLE, BIG_DEC;
+ public boolean isString() {
+ return (this == STRING);
+ }
+
public boolean isNumeric() {
return inRange(LONG, BIG_DEC);
}
import com.healthmarketscience.jackcess.expr.TemporalConfig;
import com.healthmarketscience.jackcess.impl.expr.DefaultFunctions;
import com.healthmarketscience.jackcess.impl.expr.Expressionator;
+import com.healthmarketscience.jackcess.impl.expr.NumberFormatter;
import com.healthmarketscience.jackcess.impl.expr.RandomContext;
/**
DecimalFormat df = _dfs.get(formatStr);
if(df == null) {
df = new DecimalFormat(formatStr, _numeric.getDecimalFormatSymbols());
+ df.setRoundingMode(NumberFormatter.ROUND_MODE);
_dfs.put(formatStr, df);
}
return df;
}
-
+
public float getRandom(Integer seed) {
return _rndCtx.getRandom(seed);
}
+++ /dev/null
-/*
-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.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
-import java.text.FieldPosition;
-import java.text.NumberFormat;
-import java.text.ParsePosition;
-
-/**
- *
- * @author James Ahlborn
- */
-public class NumberFormatter
-{
- public static final RoundingMode ROUND_MODE = RoundingMode.HALF_EVEN;
-
- private static final int FLT_SIG_DIGITS = 7;
- private static final int DBL_SIG_DIGITS = 15;
- private static final int DEC_SIG_DIGITS = 28;
-
- public static final MathContext FLT_MATH_CONTEXT =
- new MathContext(FLT_SIG_DIGITS, ROUND_MODE);
- public static final MathContext DBL_MATH_CONTEXT =
- new MathContext(DBL_SIG_DIGITS, ROUND_MODE);
- public static final MathContext DEC_MATH_CONTEXT =
- new MathContext(DEC_SIG_DIGITS, ROUND_MODE);
-
- // note, java doesn't distinguish between pos/neg NaN
- private static final String NAN_STR = "1.#QNAN";
- private static final String POS_INF_STR = "1.#INF";
- private static final String NEG_INf_STR = "-1.#INF";
-
- private static final ThreadLocal<NumberFormatter> INSTANCE =
- new ThreadLocal<NumberFormatter>() {
- @Override
- protected NumberFormatter initialValue() {
- return new NumberFormatter();
- }
- };
-
- private final TypeFormatter _fltFmt = new TypeFormatter(FLT_SIG_DIGITS);
- private final TypeFormatter _dblFmt = new TypeFormatter(DBL_SIG_DIGITS);
- private final TypeFormatter _decFmt = new TypeFormatter(DEC_SIG_DIGITS);
-
- private NumberFormatter() {}
-
- public static String format(float f) {
- return INSTANCE.get().formatImpl(f);
- }
-
- public static String format(double d) {
- return INSTANCE.get().formatImpl(d);
- }
-
- public static String format(BigDecimal bd) {
- return INSTANCE.get().formatImpl(bd);
- }
-
- private String formatImpl(float f) {
-
- if(Float.isNaN(f)) {
- return NAN_STR;
- }
- if(Float.isInfinite(f)) {
- return ((f < 0f) ? NEG_INf_STR : POS_INF_STR);
- }
-
- return _fltFmt.format(new BigDecimal(f, FLT_MATH_CONTEXT));
- }
-
- private String formatImpl(double d) {
-
- if(Double.isNaN(d)) {
- return NAN_STR;
- }
- if(Double.isInfinite(d)) {
- return ((d < 0d) ? NEG_INf_STR : POS_INF_STR);
- }
-
- return _dblFmt.format(new BigDecimal(d, DBL_MATH_CONTEXT));
- }
-
- private String formatImpl(BigDecimal bd) {
- return _decFmt.format(bd.round(DEC_MATH_CONTEXT));
- }
-
- private static final class TypeFormatter
- {
- private final DecimalFormat _df = new DecimalFormat("0.#");
- private final BetterDecimalFormat _dfS;
- private final int _prec;
-
- private TypeFormatter(int prec) {
- _prec = prec;
- _df.setMaximumIntegerDigits(prec);
- _df.setMaximumFractionDigits(prec);
- _df.setRoundingMode(ROUND_MODE);
- _dfS = new BetterDecimalFormat("0.#E00", prec);
- }
-
- public String format(BigDecimal bd) {
- bd = bd.stripTrailingZeros();
- int prec = bd.precision();
- int scale = bd.scale();
-
- int sigDigits = prec;
- if(scale < 0) {
- sigDigits -= scale;
- } else if(scale > prec) {
- sigDigits += (scale - prec);
- }
-
- return ((sigDigits > _prec) ? _dfS.format(bd) : _df.format(bd));
- }
- }
-
- private static final class BetterDecimalFormat extends NumberFormat
- {
- private static final long serialVersionUID = 0L;
-
- private final DecimalFormat _df;
-
- private BetterDecimalFormat(String pat, int prec) {
- super();
- _df = new DecimalFormat(pat);
- _df.setMaximumIntegerDigits(1);
- _df.setMaximumFractionDigits(prec);
- _df.setRoundingMode(ROUND_MODE);
- }
-
- @Override
- public StringBuffer format(Object number, StringBuffer toAppendTo,
- FieldPosition pos)
- {
- StringBuffer sb = _df.format(number, toAppendTo, pos);
- int idx = sb.lastIndexOf("E");
- if(sb.charAt(idx + 1) != '-') {
- sb.insert(idx + 1, '+');
- }
- return sb;
- }
-
- @Override
- public StringBuffer format(double number, StringBuffer toAppendTo,
- FieldPosition pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Number parse(String source, ParsePosition parsePosition) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public StringBuffer format(long number, StringBuffer toAppendTo,
- FieldPosition pos) {
- throw new UnsupportedOperationException();
- }
- }
-}
import com.healthmarketscience.jackcess.expr.Value;
import com.healthmarketscience.jackcess.expr.EvalException;
import com.healthmarketscience.jackcess.expr.LocaleContext;
-import com.healthmarketscience.jackcess.impl.NumberFormatter;
/**
*
import java.math.BigDecimal;
import com.healthmarketscience.jackcess.expr.LocaleContext;
-import com.healthmarketscience.jackcess.impl.NumberFormatter;
/**
*
import com.healthmarketscience.jackcess.expr.EvalException;
import com.healthmarketscience.jackcess.expr.LocaleContext;
import com.healthmarketscience.jackcess.expr.Value;
-import com.healthmarketscience.jackcess.impl.NumberFormatter;
import static com.healthmarketscience.jackcess.impl.expr.ValueSupport.*;
Value.Type mathType = getMathTypePrecedence(ctx, param1, param2,
CoercionType.GENERAL);
- if(mathType == Value.Type.STRING) {
+ if(mathType.isString()) {
throw new EvalException("Unexpected type " + mathType);
}
return toValue(param1.getAsLongInt(ctx) / param2.getAsLongInt(ctx));
Value.Type mathType = getMathTypePrecedence(ctx, param1, param2,
CoercionType.GENERAL);
- if(mathType == Value.Type.STRING) {
+ if(mathType.isString()) {
throw new EvalException("Unexpected type " + mathType);
}
return toValue(param1.getAsLongInt(ctx) % param2.getAsLongInt(ctx));
return t1;
}
- if((t1 == Value.Type.STRING) || (t2 == Value.Type.STRING)) {
+ if(t1.isString() || t2.isString()) {
if(cType._allowCoerceStringToNum) {
// see if this is mixed string/numeric and the string can be coerced
import com.healthmarketscience.jackcess.expr.TemporalConfig;
import com.healthmarketscience.jackcess.expr.Value;
import com.healthmarketscience.jackcess.impl.DatabaseImpl;
-import com.healthmarketscience.jackcess.impl.NumberFormatter;
import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*;
/**
public static final Function HEX = registerStringFunc(new Func1NullIsNull("Hex") {
@Override
protected Value eval1(EvalContext ctx, Value param1) {
- if((param1.getType() == Value.Type.STRING) &&
+ if(param1.getType().isString() &&
(param1.getAsString(ctx).length() == 0)) {
return ValueSupport.ZERO_VAL;
}
return params[1];
}
Value.Type resultType = ctx.getResultType();
- return (((resultType == null) ||
- (resultType == Value.Type.STRING)) ?
+ return (((resultType == null) || resultType.isString()) ?
ValueSupport.EMPTY_STR_VAL : ValueSupport.ZERO_VAL);
}
});
public static final Function OCT = registerStringFunc(new Func1NullIsNull("Oct") {
@Override
protected Value eval1(EvalContext ctx, Value param1) {
- if((param1.getType() == Value.Type.STRING) &&
+ if(param1.getType().isString() &&
(param1.getAsString(ctx).length() == 0)) {
return ValueSupport.ZERO_VAL;
}
// return true if it is explicitly a date/time, not if it is just a
// number (even though casting a number string to a date/time works in
// general)
- if((param1.getType() == Value.Type.STRING) &&
+ if(param1.getType().isString() &&
!stringIsNumeric(ctx, param1) &&
stringIsTemporal(ctx, param1)) {
return ValueSupport.TRUE_VAL;
// note, only a string can be considered numberic for this function,
// even though a date/time can be cast to a number in general
- if((param1.getType() == Value.Type.STRING) &&
- stringIsNumeric(ctx, param1)) {
+ if(param1.getType().isString() && stringIsNumeric(ctx, param1)) {
return ValueSupport.TRUE_VAL;
}
public static final Function FORMATNUMBER = registerFunc(new FuncVar("FormatNumber", 1, 6) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
- return formatNumber(ctx, params, false, false);
+ return formatNumber(ctx, params, FormatUtil.NumPatternType.GENERAL);
}
});
public static final Function FORMATPERCENT = registerFunc(new FuncVar("FormatPercent", 1, 6) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
- return formatNumber(ctx, params, true, false);
+ return formatNumber(ctx, params, FormatUtil.NumPatternType.PERCENT);
}
});
public static final Function FORMATCURRENCY = registerFunc(new FuncVar("FormatCurrency", 1, 6) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
- return formatNumber(ctx, params, false, true);
+ return formatNumber(ctx, params, FormatUtil.NumPatternType.CURRENCY);
}
});
});
private static boolean stringIsNumeric(EvalContext ctx, Value param) {
+ return (maybeGetAsBigDecimal(ctx, param) != null);
+ }
+
+ static BigDecimal maybeGetAsBigDecimal(EvalContext ctx, Value param) {
try {
- param.getAsBigDecimal(ctx);
- return true;
+ return param.getAsBigDecimal(ctx);
} catch(EvalException ignored) {
- // fall through to false
+ // not a number
}
- return false;
+ return null;
}
private static boolean stringIsTemporal(EvalContext ctx, Value param) {
+ return (maybeGetAsDateTimeValue(ctx, param) != null);
+ }
+
+ static Value maybeGetAsDateTimeValue(EvalContext ctx, Value param) {
try {
// see if we can coerce to date/time
- param.getAsDateTimeValue(ctx);
- return true;
+ return param.getAsDateTimeValue(ctx);
} catch(EvalException ignored) {
// not a date/time
}
- return false;
+ return null;
}
private static boolean getOptionalTriStateBoolean(
}
private static Value formatNumber(
- EvalContext ctx, Value[] params, boolean isPercent, boolean isCurrency) {
+ EvalContext ctx, Value[] params, FormatUtil.NumPatternType numPatType) {
Value param1 = params[0];
if(param1.isNull()) {
ctx, params, 1, cfg.getNumDecimalDigits(), -1);
boolean incLeadDigit = getOptionalTriStateBoolean(
ctx, params, 2, cfg.includeLeadingDigit());
- boolean defNegParens = (isCurrency ? cfg.useParensForCurrencyNegatives() :
- cfg.useParensForNegatives());
+ boolean defNegParens = numPatType.useParensForNegatives(cfg);
boolean negParens = getOptionalTriStateBoolean(
ctx, params, 3, defNegParens);
- int numGroupDigits = cfg.getNumGroupingDigits();
+ int defNumGroupDigits = cfg.getNumGroupingDigits();
boolean groupDigits = getOptionalTriStateBoolean(
- ctx, params, 4, (numGroupDigits > 0));
-
- StringBuilder fmt = new StringBuilder();
-
- if(isCurrency) {
- fmt.append("\u00A4");
- }
+ ctx, params, 4, (defNumGroupDigits > 0));
+ int numGroupDigits = (groupDigits ? defNumGroupDigits : 0);
- if(groupDigits) {
- fmt.append("#,");
- DefaultTextFunctions.nchars(fmt, numGroupDigits - 1, '#');
- }
-
- fmt.append(incLeadDigit ? "0" : "#");
- if(numDecDigits > 0) {
- fmt.append(".");
- DefaultTextFunctions.nchars(fmt, numDecDigits, '0');
- }
-
- if(isPercent) {
- fmt.append("%");
- }
-
- if(negParens) {
- // the javadocs claim the second pattern does not need to be fully
- // defined, but it doesn't seem to work that way
- String mainPat = fmt.toString();
- fmt.append(";(").append(mainPat).append(")");
- }
+ String fmtStr = FormatUtil.createNumberFormatPattern(
+ numPatType, numDecDigits, incLeadDigit, negParens, numGroupDigits);
- // Note, DecimalFormat rounding mode uses HALF_EVEN by default
- DecimalFormat df = ctx.createDecimalFormat(fmt.toString());
+ DecimalFormat df = ctx.createDecimalFormat(fmtStr);
return ValueSupport.toValue(df.format(param1.getAsBigDecimal(ctx)));
}
import com.healthmarketscience.jackcess.expr.EvalException;
import com.healthmarketscience.jackcess.expr.Function;
import com.healthmarketscience.jackcess.expr.Value;
-import com.healthmarketscience.jackcess.impl.NumberFormatter;
import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*;
import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*;
import java.math.BigDecimal;
import com.healthmarketscience.jackcess.expr.LocaleContext;
-import com.healthmarketscience.jackcess.impl.NumberFormatter;
/**
*
package com.healthmarketscience.jackcess.impl.expr;
+import java.math.BigDecimal;
import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Map;
import com.healthmarketscience.jackcess.expr.EvalContext;
+import com.healthmarketscience.jackcess.expr.NumericConfig;
import com.healthmarketscience.jackcess.expr.TemporalConfig;
import com.healthmarketscience.jackcess.expr.Value;
*/
public class FormatUtil
{
+ public enum NumPatternType {
+ GENERAL, CURRENCY {
+ @Override
+ protected void appendPrefix(StringBuilder fmt) {
+ fmt.append("\u00A4");
+ }
+ @Override
+ protected boolean useParensForNegatives(NumericConfig cfg) {
+ return cfg.useParensForCurrencyNegatives();
+ }
+ },
+ PERCENT {
+ @Override
+ protected void appendSuffix(StringBuilder fmt) {
+ fmt.append("%");
+ }
+ },
+ SCIENTIFIC {
+ @Override
+ protected void appendSuffix(StringBuilder fmt) {
+ fmt.append("E0");
+ }
+ };
+
+ protected void appendPrefix(StringBuilder fmt) {}
+
+ protected void appendSuffix(StringBuilder fmt) {}
+
+ protected boolean useParensForNegatives(NumericConfig cfg) {
+ return cfg.useParensForNegatives();
+ }
+ }
+
private static final Map<String,Fmt> PREDEF_FMTS = new HashMap<String,Fmt>();
static {
- PREDEF_FMTS.put("General Date",
- new PredefDateFmt(TemporalConfig.Type.GENERAL_DATE));
+ PREDEF_FMTS.put("General Date", new GenPredefDateFmt());
PREDEF_FMTS.put("Long Date",
new PredefDateFmt(TemporalConfig.Type.LONG_DATE));
PREDEF_FMTS.put("Medium Date",
PREDEF_FMTS.put("Short Time",
new PredefDateFmt(TemporalConfig.Type.SHORT_TIME));
+ PREDEF_FMTS.put("General Number", new GenPredefNumberFmt());
+ PREDEF_FMTS.put("Currency",
+ new PredefNumberFmt(NumericConfig.Type.CURRENCY));
+ // FIXME ?
+ // PREDEF_FMTS.put("Euro",
+ // new PredefNumberFmt(???));
+ PREDEF_FMTS.put("Fixed",
+ new PredefNumberFmt(NumericConfig.Type.FIXED));
+ PREDEF_FMTS.put("Standard",
+ new PredefNumberFmt(NumericConfig.Type.STANDARD));
+ PREDEF_FMTS.put("Percent",
+ new PredefNumberFmt(NumericConfig.Type.PERCENT));
+ PREDEF_FMTS.put("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"));
public static Value format(EvalContext ctx, Value expr, String fmtStr,
int firstDay, int firstWeekType) {
+ Fmt predefFmt = PREDEF_FMTS.get(fmtStr);
+ if(predefFmt != null) {
+ if(expr.isNull()) {
+ // predefined formats return null for null
+ return ValueSupport.NULL_VAL;
+ }
+ return predefFmt.format(ctx, expr, null, firstDay, firstWeekType);
+ }
// FIXME,
throw new UnsupportedOperationException();
}
+ public static String createNumberFormatPattern(
+ NumPatternType numPatType, int numDecDigits, boolean incLeadDigit,
+ boolean negParens, int numGroupDigits) {
+
+ StringBuilder fmt = new StringBuilder();
+
+ numPatType.appendPrefix(fmt);
+
+ if(numGroupDigits > 0) {
+ fmt.append("#,");
+ DefaultTextFunctions.nchars(fmt, numGroupDigits - 1, '#');
+ }
+
+ fmt.append(incLeadDigit ? "0" : "#");
+ if(numDecDigits > 0) {
+ fmt.append(".");
+ DefaultTextFunctions.nchars(fmt, numDecDigits, '0');
+ }
+
+ numPatType.appendSuffix(fmt);
+
+ if(negParens) {
+ // the javadocs claim the second pattern does not need to be fully
+ // defined, but it doesn't seem to work that way
+ String mainPat = fmt.toString();
+ fmt.append(";(").append(mainPat).append(")");
+ }
+
+ return fmt.toString();
+ }
+
+
private static abstract class Fmt
{
// FIXME, no null
+ // FIXME, need fmtStr?
public abstract Value format(EvalContext ctx, Value expr, String fmtStr,
int firstDay, int firstWeekType);
}
}
}
+ private static class GenPredefDateFmt extends Fmt
+ {
+ @Override
+ public Value format(EvalContext ctx, Value expr, String fmtStr,
+ int firstDay, int firstWeekType) {
+ Value tempExpr = expr;
+ if(!expr.getType().isTemporal()) {
+ Value maybe = DefaultFunctions.maybeGetAsDateTimeValue(ctx, expr);
+ if(maybe != null) {
+ tempExpr = maybe;
+ }
+ }
+ return ValueSupport.toValue(tempExpr.getAsString(ctx));
+ }
+ }
+
private static class PredefBoolFmt extends Fmt
{
private final Value _trueVal;
@Override
public Value format(EvalContext ctx, Value expr, String fmtStr,
int firstDay, int firstWeekType) {
- // FIXME, handle null?
return(expr.getAsBoolean(ctx) ? _trueVal : _falseVal);
}
}
+ private static class PredefNumberFmt extends Fmt
+ {
+ private final NumericConfig.Type _type;
+ private PredefNumberFmt(NumericConfig.Type type) {
+ _type = type;
+ }
+
+ @Override
+ public Value format(EvalContext ctx, Value expr, String fmtStr,
+ int firstDay, int firstWeekType) {
+ DecimalFormat df = ctx.createDecimalFormat(
+ ctx.getNumericConfig().getNumberFormat(_type));
+ return ValueSupport.toValue(df.format(expr.getAsBigDecimal(ctx)));
+ }
+ }
+
+ private static class GenPredefNumberFmt extends Fmt
+ {
+ @Override
+ public Value format(EvalContext ctx, Value expr, String fmtStr,
+ int firstDay, int firstWeekType) {
+ Value numExpr = expr;
+ if(!expr.getType().isNumeric()) {
+ if(expr.getType().isString()) {
+ BigDecimal bd = DefaultFunctions.maybeGetAsBigDecimal(ctx, expr);
+ if(bd != null) {
+ numExpr = ValueSupport.toValue(bd);
+ } else {
+ // convert to date to number
+ Value maybe = DefaultFunctions.maybeGetAsDateTimeValue(ctx, expr);
+ if(maybe != null) {
+ numExpr = ValueSupport.toValue(maybe.getAsDouble(ctx));
+ }
+ }
+ } else {
+ // convert date to number
+ numExpr = ValueSupport.toValue(expr.getAsDouble(ctx));
+ }
+ }
+ return ValueSupport.toValue(numExpr.getAsString(ctx));
+ }
+ }
+
+ private static class ScientificPredefNumberFmt extends Fmt
+ {
+ @Override
+ public Value format(EvalContext ctx, Value expr, String fmtStr,
+ int firstDay, int firstWeekType) {
+ NumberFormat df = ctx.createDecimalFormat(
+ ctx.getNumericConfig().getNumberFormat(
+ NumericConfig.Type.SCIENTIFIC));
+ df = new NumberFormatter.ScientificFormat(df);
+ return ValueSupport.toValue(df.format(expr.getAsBigDecimal(ctx)));
+ }
+ }
}
--- /dev/null
+/*
+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.math.MathContext;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+/**
+ *
+ * @author James Ahlborn
+ */
+public class NumberFormatter
+{
+ public static final RoundingMode ROUND_MODE = RoundingMode.HALF_EVEN;
+
+ /** designates the format of exponent notation used by ScientificFormat */
+ public enum NotationType {
+ /** Scientific notation "E", "E-" (default java behavior) */
+ EXP_E_MINUS {
+ @Override
+ protected void format(StringBuffer sb, int eIdx) {
+ // nothing to do
+ }
+ },
+ /** Scientific notation "E+", "E-" */
+ EXP_E_PLUS {
+ @Override
+ protected void format(StringBuffer sb, int eIdx) {
+ maybeInsertExpPlus(sb, eIdx);
+ }
+ },
+ /** Scientific notation "e", "e-" */
+ EXP_e_MINUS {
+ @Override
+ protected void format(StringBuffer sb, int eIdx) {
+ sb.setCharAt(eIdx, 'e');
+ }
+ },
+ /** Scientific notation "e+", "e-" */
+ EXP_e_PLUS {
+ @Override
+ protected void format(StringBuffer sb, int eIdx) {
+ sb.setCharAt(eIdx, 'e');
+ maybeInsertExpPlus(sb, eIdx);
+ }
+ };
+
+ protected abstract void format(StringBuffer sb, int idx);
+ }
+
+ private static final int FLT_SIG_DIGITS = 7;
+ private static final int DBL_SIG_DIGITS = 15;
+ private static final int DEC_SIG_DIGITS = 28;
+
+ public static final MathContext FLT_MATH_CONTEXT =
+ new MathContext(FLT_SIG_DIGITS, ROUND_MODE);
+ public static final MathContext DBL_MATH_CONTEXT =
+ new MathContext(DBL_SIG_DIGITS, ROUND_MODE);
+ public static final MathContext DEC_MATH_CONTEXT =
+ new MathContext(DEC_SIG_DIGITS, ROUND_MODE);
+
+ // note, java doesn't distinguish between pos/neg NaN
+ private static final String NAN_STR = "1.#QNAN";
+ private static final String POS_INF_STR = "1.#INF";
+ private static final String NEG_INf_STR = "-1.#INF";
+
+ private static final ThreadLocal<NumberFormatter> INSTANCE =
+ new ThreadLocal<NumberFormatter>() {
+ @Override
+ protected NumberFormatter initialValue() {
+ return new NumberFormatter();
+ }
+ };
+
+ private final TypeFormatter _fltFmt = new TypeFormatter(FLT_SIG_DIGITS);
+ private final TypeFormatter _dblFmt = new TypeFormatter(DBL_SIG_DIGITS);
+ private final TypeFormatter _decFmt = new TypeFormatter(DEC_SIG_DIGITS);
+
+ private NumberFormatter() {}
+
+ public static String format(float f) {
+ return INSTANCE.get().formatImpl(f);
+ }
+
+ public static String format(double d) {
+ return INSTANCE.get().formatImpl(d);
+ }
+
+ public static String format(BigDecimal bd) {
+ return INSTANCE.get().formatImpl(bd);
+ }
+
+ private String formatImpl(float f) {
+
+ if(Float.isNaN(f)) {
+ return NAN_STR;
+ }
+ if(Float.isInfinite(f)) {
+ return ((f < 0f) ? NEG_INf_STR : POS_INF_STR);
+ }
+
+ return _fltFmt.format(new BigDecimal(f, FLT_MATH_CONTEXT));
+ }
+
+ private String formatImpl(double d) {
+
+ if(Double.isNaN(d)) {
+ return NAN_STR;
+ }
+ if(Double.isInfinite(d)) {
+ return ((d < 0d) ? NEG_INf_STR : POS_INF_STR);
+ }
+
+ return _dblFmt.format(new BigDecimal(d, DBL_MATH_CONTEXT));
+ }
+
+ private String formatImpl(BigDecimal bd) {
+ return _decFmt.format(bd.round(DEC_MATH_CONTEXT));
+ }
+
+ private static ScientificFormat createScientificFormat(int prec) {
+ DecimalFormat df = new DecimalFormat("0.#E00");
+ df.setMaximumIntegerDigits(1);
+ df.setMaximumFractionDigits(prec);
+ df.setRoundingMode(ROUND_MODE);
+ return new ScientificFormat(df);
+ }
+
+ private static final class TypeFormatter
+ {
+ private final DecimalFormat _df = new DecimalFormat("0.#");
+ private final ScientificFormat _dfS;
+ private final int _prec;
+
+ private TypeFormatter(int prec) {
+ _prec = prec;
+ _df.setMaximumIntegerDigits(prec);
+ _df.setMaximumFractionDigits(prec);
+ _df.setRoundingMode(ROUND_MODE);
+ _dfS = createScientificFormat(prec);
+ }
+
+ public String format(BigDecimal bd) {
+ bd = bd.stripTrailingZeros();
+ int prec = bd.precision();
+ int scale = bd.scale();
+
+ int sigDigits = prec;
+ if(scale < 0) {
+ sigDigits -= scale;
+ } else if(scale > prec) {
+ sigDigits += (scale - prec);
+ }
+
+ return ((sigDigits > _prec) ? _dfS.format(bd) : _df.format(bd));
+ }
+ }
+
+ private static void maybeInsertExpPlus(StringBuffer sb, int eIdx) {
+ if(sb.charAt(eIdx + 1) != '-') {
+ sb.insert(eIdx + 1, '+');
+ }
+ }
+
+ public static class ScientificFormat extends NumberFormat
+ {
+ private static final long serialVersionUID = 0L;
+
+ private final NumberFormat _df;
+ private final NotationType _type;
+
+ public ScientificFormat(NumberFormat df) {
+ this(df, NotationType.EXP_E_PLUS);
+ }
+
+ public ScientificFormat(NumberFormat df, NotationType type) {
+ _df = df;
+ _type = type;
+ }
+
+ @Override
+ public StringBuffer format(Object number, StringBuffer toAppendTo,
+ FieldPosition pos)
+ {
+ StringBuffer sb = _df.format(number, toAppendTo, pos);
+ _type.format(sb, sb.lastIndexOf("E"));
+ return sb;
+ }
+
+ @Override
+ public StringBuffer format(double number, StringBuffer toAppendTo,
+ FieldPosition pos) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Number parse(String source, ParsePosition parsePosition) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public StringBuffer format(long number, StringBuffer toAppendTo,
+ FieldPosition pos) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
+++ /dev/null
-/*
-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.math.BigDecimal;
-
-import junit.framework.TestCase;
-
-
-/**
- *
- * @author James Ahlborn
- */
-public class NumberFormatterTest extends TestCase
-{
-
- public NumberFormatterTest(String name) {
- super(name);
- }
-
- public void testDoubleFormat() throws Exception
- {
- assertEquals("894984737284944", NumberFormatter.format(894984737284944d));
- assertEquals("-894984737284944", NumberFormatter.format(-894984737284944d));
- assertEquals("8949.84737284944", NumberFormatter.format(8949.84737284944d));
- assertEquals("8949847372844", NumberFormatter.format(8949847372844d));
- assertEquals("8949.847384944", NumberFormatter.format(8949.847384944d));
- assertEquals("8.94985647372849E+16", NumberFormatter.format(89498564737284944d));
- assertEquals("-8.94985647372849E+16", NumberFormatter.format(-89498564737284944d));
- assertEquals("895649.847372849", NumberFormatter.format(895649.84737284944d));
- assertEquals("300", NumberFormatter.format(300d));
- assertEquals("-300", NumberFormatter.format(-300d));
- assertEquals("0.3", NumberFormatter.format(0.3d));
- assertEquals("0.1", NumberFormatter.format(0.1d));
- assertEquals("2.3423421E-12", NumberFormatter.format(0.0000000000023423421d));
- assertEquals("2.3423421E-11", NumberFormatter.format(0.000000000023423421d));
- assertEquals("2.3423421E-10", NumberFormatter.format(0.00000000023423421d));
- assertEquals("-2.3423421E-10", NumberFormatter.format(-0.00000000023423421d));
- assertEquals("2.34234214E-12", NumberFormatter.format(0.00000000000234234214d));
- assertEquals("2.342342156E-12", NumberFormatter.format(0.000000000002342342156d));
- assertEquals("0.000000023423421", NumberFormatter.format(0.000000023423421d));
- assertEquals("2.342342133E-07", NumberFormatter.format(0.0000002342342133d));
- assertEquals("1.#INF", NumberFormatter.format(Double.POSITIVE_INFINITY));
- assertEquals("-1.#INF", NumberFormatter.format(Double.NEGATIVE_INFINITY));
- assertEquals("1.#QNAN", NumberFormatter.format(Double.NaN));
- }
-
- public void testFloatFormat() throws Exception
- {
- assertEquals("8949847", NumberFormatter.format(8949847f));
- assertEquals("-8949847", NumberFormatter.format(-8949847f));
- assertEquals("8949.847", NumberFormatter.format(8949.847f));
- assertEquals("894984", NumberFormatter.format(894984f));
- assertEquals("8949.84", NumberFormatter.format(8949.84f));
- assertEquals("8.949856E+16", NumberFormatter.format(89498564737284944f));
- assertEquals("-8.949856E+16", NumberFormatter.format(-89498564737284944f));
- assertEquals("895649.9", NumberFormatter.format(895649.84737284944f));
- assertEquals("300", NumberFormatter.format(300f));
- assertEquals("-300", NumberFormatter.format(-300f));
- assertEquals("0.3", NumberFormatter.format(0.3f));
- assertEquals("0.1", NumberFormatter.format(0.1f));
- assertEquals("2.342342E-12", NumberFormatter.format(0.0000000000023423421f));
- assertEquals("2.342342E-11", NumberFormatter.format(0.000000000023423421f));
- assertEquals("2.342342E-10", NumberFormatter.format(0.00000000023423421f));
- assertEquals("-2.342342E-10", NumberFormatter.format(-0.00000000023423421f));
- assertEquals("2.342342E-12", NumberFormatter.format(0.00000000000234234214f));
- assertEquals("2.342342E-12", NumberFormatter.format(0.000000000002342342156f));
- assertEquals("0.0000234", NumberFormatter.format(0.0000234f));
- assertEquals("2.342E-05", NumberFormatter.format(0.00002342f));
- assertEquals("1.#INF", NumberFormatter.format(Float.POSITIVE_INFINITY));
- assertEquals("-1.#INF", NumberFormatter.format(Float.NEGATIVE_INFINITY));
- assertEquals("1.#QNAN", NumberFormatter.format(Float.NaN));
- }
-
- public void testDecimalFormat() throws Exception
- {
- assertEquals("9874539485972.2342342234234", NumberFormatter.format(new BigDecimal("9874539485972.2342342234234")));
- assertEquals("9874539485972.234234223423468", NumberFormatter.format(new BigDecimal("9874539485972.2342342234234678")));
- assertEquals("-9874539485972.234234223423468", NumberFormatter.format(new BigDecimal("-9874539485972.2342342234234678")));
- assertEquals("9.874539485972234234223423468E+31", NumberFormatter.format(new BigDecimal("98745394859722342342234234678000")));
- assertEquals("9.874539485972234234223423468E+31", NumberFormatter.format(new BigDecimal("98745394859722342342234234678000")));
- assertEquals("-9.874539485972234234223423468E+31", NumberFormatter.format(new BigDecimal("-98745394859722342342234234678000")));
- assertEquals("300", NumberFormatter.format(new BigDecimal("300.0")));
- assertEquals("-300", NumberFormatter.format(new BigDecimal("-300.000")));
- assertEquals("0.3", NumberFormatter.format(new BigDecimal("0.3")));
- assertEquals("0.1", NumberFormatter.format(new BigDecimal("0.1000")));
- assertEquals("0.0000000000023423428930458", NumberFormatter.format(new BigDecimal("0.0000000000023423428930458")));
- assertEquals("2.3423428930458389038451E-12", NumberFormatter.format(new BigDecimal("0.0000000000023423428930458389038451")));
- assertEquals("2.342342893045838903845134766E-12", NumberFormatter.format(new BigDecimal("0.0000000000023423428930458389038451347656")));
- }
-}
assertEval("13:37", "=FormatDateTime(#1/1/1973 1:37:25 PM#,4)");
}
+ public void testFormat() throws Exception
+ {
+ assertEval("12345.6789", "=Format(12345.6789, 'General Number')");
+ assertEval("0.12345", "=Format(0.12345, 'General Number')");
+ assertEval("-12345.6789", "=Format(-12345.6789, 'General Number')");
+ assertEval("-0.12345", "=Format(-0.12345, 'General Number')");
+ assertEval("12345.6789", "=Format('12345.6789', 'General Number')");
+ assertEval("1678.9", "=Format('1.6789E+3', 'General Number')");
+ assertEval("37623.2916666667", "=Format(#01/02/2003 7:00:00 AM#, 'General Number')");
+ assertEval("foo", "=Format('foo', 'General Number')");
+
+ assertEval("12,345.68", "=Format(12345.6789, 'Standard')");
+ assertEval("0.12", "=Format(0.12345, 'Standard')");
+ assertEval("-12,345.68", "=Format(-12345.6789, 'Standard')");
+ assertEval("-0.12", "=Format(-0.12345, 'Standard')");
+
+ assertEval("12345.68", "=Format(12345.6789, 'Fixed')");
+ assertEval("0.12", "=Format(0.12345, 'Fixed')");
+ assertEval("-12345.68", "=Format(-12345.6789, 'Fixed')");
+ assertEval("-0.12", "=Format(-0.12345, 'Fixed')");
+
+ assertEval("$12,345.68", "=Format(12345.6789, 'Currency')");
+ assertEval("$0.12", "=Format(0.12345, 'Currency')");
+ assertEval("($12,345.68)", "=Format(-12345.6789, 'Currency')");
+ assertEval("($0.12)", "=Format(-0.12345, 'Currency')");
+
+ assertEval("1234567.89%", "=Format(12345.6789, 'Percent')");
+ assertEval("12.34%", "=Format(0.12345, 'Percent')");
+ assertEval("-1234567.89%", "=Format(-12345.6789, 'Percent')");
+ assertEval("-12.34%", "=Format(-0.12345, 'Percent')");
+
+ assertEval("1.23E+4", "=Format(12345.6789, 'Scientific')");
+ assertEval("1.23E-1", "=Format(0.12345, 'Scientific')");
+ assertEval("-1.23E+4", "=Format(-12345.6789, 'Scientific')");
+ assertEval("-1.23E-1", "=Format(-0.12345, 'Scientific')");
+
+ assertEval("Yes", "=Format(True, 'Yes/No')");
+ assertEval("No", "=Format(False, 'Yes/No')");
+ assertEval("True", "=Format(True, 'True/False')");
+ assertEval("False", "=Format(False, 'True/False')");
+ assertEval("On", "=Format(True, 'On/Off')");
+ assertEval("Off", "=Format(False, 'On/Off')");
+
+ assertEval("1/2/2003 7:00:00 AM", "=Format(#01/02/2003 7:00:00 AM#, 'General Date')");
+ assertEval("1/2/2003", "=Format(#01/02/2003#, 'General Date')");
+ assertEval("7:00:00 AM", "=Format(#7:00:00 AM#, 'General Date')");
+ assertEval("1/2/2003 7:00:00 AM", "=Format('37623.2916666667', 'General Date')");
+ assertEval("foo", "=Format('foo', 'General Date')");
+
+ assertEval("Thursday, January 02, 2003", "=Format(#01/02/2003 7:00:00 AM#, 'Long Date')");
+ assertEval("02-Jan-03", "=Format(#01/02/2003 7:00:00 AM#, 'Medium Date')");
+ assertEval("1/2/2003", "=Format(#01/02/2003 7:00:00 AM#, 'Short Date')");
+ assertEval("7:00:00 AM", "=Format(#01/02/2003 7:00:00 AM#, 'Long Time')");
+ assertEval("07:00 AM", "=Format(#01/02/2003 7:00:00 AM#, 'Medium Time')");
+ assertEval("07:00", "=Format(#01/02/2003 7:00:00 AM#, 'Short Time')");
+ assertEval("19:00", "=Format(#01/02/2003 7:00:00 PM#, 'Short Time')");
+
+ }
+
public void testNumberFuncs() throws Exception
{
assertEval(1, "=Abs(1)");
import com.healthmarketscience.jackcess.expr.TemporalConfig;
import com.healthmarketscience.jackcess.expr.Value;
import com.healthmarketscience.jackcess.impl.BaseEvalContext;
-import com.healthmarketscience.jackcess.impl.NumberFormatter;
+import com.healthmarketscience.jackcess.impl.expr.NumberFormatter;
import junit.framework.TestCase;
/**
return new DecimalFormat(
formatStr, NumericConfig.US_NUMERIC_CONFIG.getDecimalFormatSymbols());
}
-
+
public FunctionLookup getFunctionLookup() {
return DefaultFunctions.LOOKUP;
}
--- /dev/null
+/*
+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 junit.framework.TestCase;
+
+
+/**
+ *
+ * @author James Ahlborn
+ */
+public class NumberFormatterTest extends TestCase
+{
+
+ public NumberFormatterTest(String name) {
+ super(name);
+ }
+
+ public void testDoubleFormat() throws Exception
+ {
+ assertEquals("894984737284944", NumberFormatter.format(894984737284944d));
+ assertEquals("-894984737284944", NumberFormatter.format(-894984737284944d));
+ assertEquals("8949.84737284944", NumberFormatter.format(8949.84737284944d));
+ assertEquals("8949847372844", NumberFormatter.format(8949847372844d));
+ assertEquals("8949.847384944", NumberFormatter.format(8949.847384944d));
+ assertEquals("8.94985647372849E+16", NumberFormatter.format(89498564737284944d));
+ assertEquals("-8.94985647372849E+16", NumberFormatter.format(-89498564737284944d));
+ assertEquals("895649.847372849", NumberFormatter.format(895649.84737284944d));
+ assertEquals("300", NumberFormatter.format(300d));
+ assertEquals("-300", NumberFormatter.format(-300d));
+ assertEquals("0.3", NumberFormatter.format(0.3d));
+ assertEquals("0.1", NumberFormatter.format(0.1d));
+ assertEquals("2.3423421E-12", NumberFormatter.format(0.0000000000023423421d));
+ assertEquals("2.3423421E-11", NumberFormatter.format(0.000000000023423421d));
+ assertEquals("2.3423421E-10", NumberFormatter.format(0.00000000023423421d));
+ assertEquals("-2.3423421E-10", NumberFormatter.format(-0.00000000023423421d));
+ assertEquals("2.34234214E-12", NumberFormatter.format(0.00000000000234234214d));
+ assertEquals("2.342342156E-12", NumberFormatter.format(0.000000000002342342156d));
+ assertEquals("0.000000023423421", NumberFormatter.format(0.000000023423421d));
+ assertEquals("2.342342133E-07", NumberFormatter.format(0.0000002342342133d));
+ assertEquals("1.#INF", NumberFormatter.format(Double.POSITIVE_INFINITY));
+ assertEquals("-1.#INF", NumberFormatter.format(Double.NEGATIVE_INFINITY));
+ assertEquals("1.#QNAN", NumberFormatter.format(Double.NaN));
+ }
+
+ public void testFloatFormat() throws Exception
+ {
+ assertEquals("8949847", NumberFormatter.format(8949847f));
+ assertEquals("-8949847", NumberFormatter.format(-8949847f));
+ assertEquals("8949.847", NumberFormatter.format(8949.847f));
+ assertEquals("894984", NumberFormatter.format(894984f));
+ assertEquals("8949.84", NumberFormatter.format(8949.84f));
+ assertEquals("8.949856E+16", NumberFormatter.format(89498564737284944f));
+ assertEquals("-8.949856E+16", NumberFormatter.format(-89498564737284944f));
+ assertEquals("895649.9", NumberFormatter.format(895649.84737284944f));
+ assertEquals("300", NumberFormatter.format(300f));
+ assertEquals("-300", NumberFormatter.format(-300f));
+ assertEquals("0.3", NumberFormatter.format(0.3f));
+ assertEquals("0.1", NumberFormatter.format(0.1f));
+ assertEquals("2.342342E-12", NumberFormatter.format(0.0000000000023423421f));
+ assertEquals("2.342342E-11", NumberFormatter.format(0.000000000023423421f));
+ assertEquals("2.342342E-10", NumberFormatter.format(0.00000000023423421f));
+ assertEquals("-2.342342E-10", NumberFormatter.format(-0.00000000023423421f));
+ assertEquals("2.342342E-12", NumberFormatter.format(0.00000000000234234214f));
+ assertEquals("2.342342E-12", NumberFormatter.format(0.000000000002342342156f));
+ assertEquals("0.0000234", NumberFormatter.format(0.0000234f));
+ assertEquals("2.342E-05", NumberFormatter.format(0.00002342f));
+ assertEquals("1.#INF", NumberFormatter.format(Float.POSITIVE_INFINITY));
+ assertEquals("-1.#INF", NumberFormatter.format(Float.NEGATIVE_INFINITY));
+ assertEquals("1.#QNAN", NumberFormatter.format(Float.NaN));
+ }
+
+ public void testDecimalFormat() throws Exception
+ {
+ assertEquals("9874539485972.2342342234234", NumberFormatter.format(new BigDecimal("9874539485972.2342342234234")));
+ assertEquals("9874539485972.234234223423468", NumberFormatter.format(new BigDecimal("9874539485972.2342342234234678")));
+ assertEquals("-9874539485972.234234223423468", NumberFormatter.format(new BigDecimal("-9874539485972.2342342234234678")));
+ assertEquals("9.874539485972234234223423468E+31", NumberFormatter.format(new BigDecimal("98745394859722342342234234678000")));
+ assertEquals("9.874539485972234234223423468E+31", NumberFormatter.format(new BigDecimal("98745394859722342342234234678000")));
+ assertEquals("-9.874539485972234234223423468E+31", NumberFormatter.format(new BigDecimal("-98745394859722342342234234678000")));
+ assertEquals("300", NumberFormatter.format(new BigDecimal("300.0")));
+ assertEquals("-300", NumberFormatter.format(new BigDecimal("-300.000")));
+ assertEquals("0.3", NumberFormatter.format(new BigDecimal("0.3")));
+ assertEquals("0.1", NumberFormatter.format(new BigDecimal("0.1000")));
+ assertEquals("0.0000000000023423428930458", NumberFormatter.format(new BigDecimal("0.0000000000023423428930458")));
+ assertEquals("2.3423428930458389038451E-12", NumberFormatter.format(new BigDecimal("0.0000000000023423428930458389038451")));
+ assertEquals("2.342342893045838903845134766E-12", NumberFormatter.format(new BigDecimal("0.0000000000023423428930458389038451347656")));
+ }
+}