git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1406 f203690c-595d-4dc9-a70b-905162fa7fd2pull/13/head
@@ -131,7 +131,6 @@ | |||
<value>${jackcess.testFormats}</value> | |||
</property> | |||
</systemProperties> | |||
<argLine>-Duser.language=en -Duser.region=US</argLine> | |||
</configuration> | |||
</plugin> | |||
</plugins> |
@@ -4,6 +4,12 @@ | |||
<author email="javajedi@users.sf.net">Tim McCune</author> | |||
</properties> | |||
<body> | |||
<release version="4.0.6" date="TBD"> | |||
<action dev="jahlborn" type="update"> | |||
Make NumberFormatter locale aware, using the currently configured | |||
LocaleContext. | |||
</action> | |||
</release> | |||
<release version="4.0.5" date="2023-01-26"> | |||
<action dev="jahlborn" type="add" system="SourceForge2Features" | |||
issue="46"> |
@@ -16,9 +16,12 @@ limitations under the License. | |||
package com.healthmarketscience.jackcess.expr; | |||
import java.math.BigDecimal; | |||
import java.text.DecimalFormatSymbols; | |||
import java.util.Locale; | |||
import com.healthmarketscience.jackcess.impl.expr.FormatUtil; | |||
import com.healthmarketscience.jackcess.impl.expr.NumberFormatter; | |||
/** | |||
* A NumericConfig encapsulates number formatting options for expression | |||
@@ -44,6 +47,7 @@ public class NumericConfig | |||
private final boolean _useNegCurrencyParens; | |||
private final int _numGroupDigits; | |||
private final DecimalFormatSymbols _symbols; | |||
private final NumberFormatter _numFmt; | |||
private final String _currencyFormat; | |||
private final String _fixedFormat; | |||
private final String _standardFormat; | |||
@@ -60,6 +64,7 @@ public class NumericConfig | |||
_useNegCurrencyParens = useNegCurrencyParens; | |||
_numGroupDigits = numGroupDigits; | |||
_symbols = DecimalFormatSymbols.getInstance(locale); | |||
_numFmt = new NumberFormatter(_symbols); | |||
_currencyFormat = FormatUtil.createNumberFormatPattern( | |||
FormatUtil.NumPatternType.CURRENCY, _numDecDigits, _incLeadingDigit, | |||
@@ -123,4 +128,25 @@ public class NumericConfig | |||
public DecimalFormatSymbols getDecimalFormatSymbols() { | |||
return _symbols; | |||
} | |||
/** | |||
* @return the given float formatted according to the current locale config | |||
*/ | |||
public String format(float f) { | |||
return _numFmt.format(f); | |||
} | |||
/** | |||
* @return the given double formatted according to the current locale config | |||
*/ | |||
public String format(double d) { | |||
return _numFmt.format(d); | |||
} | |||
/** | |||
* @return the given BigDecimal formatted according to the current locale config | |||
*/ | |||
public String format(BigDecimal bd) { | |||
return _numFmt.format(bd); | |||
} | |||
} |
@@ -23,13 +23,11 @@ import java.time.LocalDateTime; | |||
import java.time.ZoneId; | |||
import java.time.format.DateTimeFormatter; | |||
import java.util.Collection; | |||
import java.util.Date; | |||
import java.util.EnumMap; | |||
import java.util.Map; | |||
import javax.script.Bindings; | |||
import com.healthmarketscience.jackcess.DataType; | |||
import com.healthmarketscience.jackcess.DateTimeType; | |||
import com.healthmarketscience.jackcess.JackcessException; | |||
import com.healthmarketscience.jackcess.expr.EvalContext; | |||
import com.healthmarketscience.jackcess.expr.EvalException; |
@@ -55,7 +55,7 @@ public class BigDecimalValue extends BaseNumericValue | |||
@Override | |||
public String getAsString(LocaleContext ctx) { | |||
return NumberFormatter.format(_val); | |||
return ctx.getNumericConfig().format(_val); | |||
} | |||
@Override |
@@ -65,6 +65,6 @@ public class DoubleValue extends BaseNumericValue | |||
@Override | |||
public String getAsString(LocaleContext ctx) { | |||
return NumberFormatter.format(_val); | |||
return ctx.getNumericConfig().format(_val); | |||
} | |||
} |
@@ -167,7 +167,7 @@ class ExpressionTokenizer | |||
} else { | |||
if(isDigit(c)) { | |||
Token numLit = maybeParseNumberLiteral(c, buf); | |||
Token numLit = maybeParseNumberLiteral(c, buf, context); | |||
if(numLit != null) { | |||
tokens.add(numLit); | |||
continue; | |||
@@ -362,7 +362,8 @@ class ExpressionTokenizer | |||
suffStr, 0, suffStrLen)); | |||
} | |||
private static Token maybeParseNumberLiteral(char firstChar, ExprBuf buf) { | |||
private static Token maybeParseNumberLiteral( | |||
char firstChar, ExprBuf buf, ParseContext context) { | |||
StringBuilder sb = buf.getScratchBuffer().append(firstChar); | |||
boolean hasDigit = isDigit(firstChar); | |||
@@ -370,6 +371,8 @@ class ExpressionTokenizer | |||
boolean foundNum = false; | |||
boolean isFp = false; | |||
int expPos = -1; | |||
char decimalSep = context.getNumericConfig().getDecimalFormatSymbols() | |||
.getDecimalSeparator(); | |||
try { | |||
@@ -379,9 +382,11 @@ class ExpressionTokenizer | |||
hasDigit = true; | |||
sb.append((char)c); | |||
buf.next(); | |||
} else if(c == '.') { | |||
} else if(c == decimalSep) { | |||
isFp = true; | |||
sb.append((char)c); | |||
// we handle a localized decimal separator for input, but the code | |||
// below will parse using non-locale symbols | |||
sb.append('.'); | |||
buf.next(); | |||
} else if(hasDigit && (expPos < 0) && ((c == 'e') || (c == 'E'))) { | |||
isFp = true; |
@@ -20,10 +20,12 @@ import java.math.BigDecimal; | |||
import java.math.MathContext; | |||
import java.math.RoundingMode; | |||
import java.text.DecimalFormat; | |||
import java.text.DecimalFormatSymbols; | |||
import java.text.FieldPosition; | |||
import java.text.NumberFormat; | |||
import java.text.ParsePosition; | |||
/** | |||
* | |||
* @author James Ahlborn | |||
@@ -83,33 +85,17 @@ public class NumberFormatter | |||
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); | |||
} | |||
private final TypeFormatter _fltFmt; | |||
private final TypeFormatter _dblFmt; | |||
private final TypeFormatter _decFmt; | |||
public static String format(BigDecimal bd) { | |||
return INSTANCE.get().formatImpl(bd); | |||
public NumberFormatter(DecimalFormatSymbols syms) { | |||
_fltFmt = new TypeFormatter(FLT_SIG_DIGITS, syms); | |||
_dblFmt = new TypeFormatter(DBL_SIG_DIGITS, syms); | |||
_decFmt = new TypeFormatter(DEC_SIG_DIGITS, syms); | |||
} | |||
private String formatImpl(float f) { | |||
public String format(float f) { | |||
if(Float.isNaN(f)) { | |||
return NAN_STR; | |||
@@ -121,7 +107,7 @@ public class NumberFormatter | |||
return _fltFmt.format(new BigDecimal(f, FLT_MATH_CONTEXT)); | |||
} | |||
private String formatImpl(double d) { | |||
public String format(double d) { | |||
if(Double.isNaN(d)) { | |||
return NAN_STR; | |||
@@ -133,12 +119,13 @@ public class NumberFormatter | |||
return _dblFmt.format(new BigDecimal(d, DBL_MATH_CONTEXT)); | |||
} | |||
private String formatImpl(BigDecimal bd) { | |||
public String format(BigDecimal bd) { | |||
return _decFmt.format(bd.round(DEC_MATH_CONTEXT)); | |||
} | |||
private static ScientificFormat createScientificFormat(int prec) { | |||
DecimalFormat df = new DecimalFormat("0.#E00"); | |||
private static ScientificFormat createScientificFormat( | |||
int prec, DecimalFormatSymbols syms) { | |||
DecimalFormat df = new DecimalFormat("0.#E00", syms); | |||
df.setMaximumIntegerDigits(1); | |||
df.setMaximumFractionDigits(prec); | |||
df.setRoundingMode(ROUND_MODE); | |||
@@ -147,16 +134,17 @@ public class NumberFormatter | |||
private static final class TypeFormatter | |||
{ | |||
private final DecimalFormat _df = new DecimalFormat("0.#"); | |||
private final DecimalFormat _df; | |||
private final ScientificFormat _dfS; | |||
private final int _prec; | |||
private TypeFormatter(int prec) { | |||
private TypeFormatter(int prec, DecimalFormatSymbols syms) { | |||
_prec = prec; | |||
_df = new DecimalFormat("0.#", syms); | |||
_df.setMaximumIntegerDigits(prec); | |||
_df.setMaximumFractionDigits(prec); | |||
_df.setRoundingMode(ROUND_MODE); | |||
_dfS = createScientificFormat(prec); | |||
_dfS = createScientificFormat(prec, syms); | |||
} | |||
public String format(BigDecimal bd) { |
@@ -580,37 +580,45 @@ public class ExpressionatorTest extends TestCase | |||
_thisVal = thisVal; | |||
} | |||
@Override | |||
public Value.Type getResultType() { | |||
return null; | |||
} | |||
@Override | |||
public TemporalConfig getTemporalConfig() { | |||
return TemporalConfig.US_TEMPORAL_CONFIG; | |||
} | |||
@Override | |||
public DateTimeFormatter createDateFormatter(String formatStr) { | |||
DateTimeFormatter dtf = DateTimeFormatter.ofPattern( | |||
formatStr, TemporalConfig.US_TEMPORAL_CONFIG.getLocale()); | |||
return dtf; | |||
} | |||
@Override | |||
public ZoneId getZoneId() { | |||
return TestUtil.TEST_TZ.toZoneId(); | |||
} | |||
@Override | |||
public NumericConfig getNumericConfig() { | |||
return NumericConfig.US_NUMERIC_CONFIG; | |||
} | |||
@Override | |||
public DecimalFormat createDecimalFormat(String formatStr) { | |||
return new DecimalFormat( | |||
formatStr, NumericConfig.US_NUMERIC_CONFIG.getDecimalFormatSymbols()); | |||
} | |||
@Override | |||
public FunctionLookup getFunctionLookup() { | |||
return DefaultFunctions.LOOKUP; | |||
} | |||
@Override | |||
public Value getThisColumnValue() { | |||
if(_thisVal == null) { | |||
throw new UnsupportedOperationException(); | |||
@@ -618,22 +626,27 @@ public class ExpressionatorTest extends TestCase | |||
return _thisVal; | |||
} | |||
@Override | |||
public Value getIdentifierValue(Identifier identifier) { | |||
throw new UnsupportedOperationException(); | |||
} | |||
@Override | |||
public float getRandom(Integer seed) { | |||
return _rndCtx.getRandom(seed); | |||
} | |||
@Override | |||
public Bindings getBindings() { | |||
return _bindings; | |||
} | |||
@Override | |||
public Object get(String key) { | |||
return _bindings.get(key); | |||
} | |||
@Override | |||
public void put(String key, Object value) { | |||
_bindings.put(key, value); | |||
} |
@@ -18,6 +18,7 @@ package com.healthmarketscience.jackcess.impl.expr; | |||
import java.math.BigDecimal; | |||
import com.healthmarketscience.jackcess.expr.NumericConfig; | |||
import static junit.framework.TestCase.assertEquals; | |||
import org.junit.Test; | |||
@@ -27,78 +28,80 @@ import org.junit.Test; | |||
*/ | |||
public class NumberFormatterTest | |||
{ | |||
private NumberFormatter _numFmt = new NumberFormatter( | |||
NumericConfig.US_NUMERIC_CONFIG.getDecimalFormatSymbols()); | |||
@Test | |||
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)); | |||
assertEquals("894984737284944", _numFmt.format(894984737284944d)); | |||
assertEquals("-894984737284944", _numFmt.format(-894984737284944d)); | |||
assertEquals("8949.84737284944", _numFmt.format(8949.84737284944d)); | |||
assertEquals("8949847372844", _numFmt.format(8949847372844d)); | |||
assertEquals("8949.847384944", _numFmt.format(8949.847384944d)); | |||
assertEquals("8.94985647372849E+16", _numFmt.format(89498564737284944d)); | |||
assertEquals("-8.94985647372849E+16", _numFmt.format(-89498564737284944d)); | |||
assertEquals("895649.847372849", _numFmt.format(895649.84737284944d)); | |||
assertEquals("300", _numFmt.format(300d)); | |||
assertEquals("-300", _numFmt.format(-300d)); | |||
assertEquals("0.3", _numFmt.format(0.3d)); | |||
assertEquals("0.1", _numFmt.format(0.1d)); | |||
assertEquals("2.3423421E-12", _numFmt.format(0.0000000000023423421d)); | |||
assertEquals("2.3423421E-11", _numFmt.format(0.000000000023423421d)); | |||
assertEquals("2.3423421E-10", _numFmt.format(0.00000000023423421d)); | |||
assertEquals("-2.3423421E-10", _numFmt.format(-0.00000000023423421d)); | |||
assertEquals("2.34234214E-12", _numFmt.format(0.00000000000234234214d)); | |||
assertEquals("2.342342156E-12", _numFmt.format(0.000000000002342342156d)); | |||
assertEquals("0.000000023423421", _numFmt.format(0.000000023423421d)); | |||
assertEquals("2.342342133E-07", _numFmt.format(0.0000002342342133d)); | |||
assertEquals("1.#INF", _numFmt.format(Double.POSITIVE_INFINITY)); | |||
assertEquals("-1.#INF", _numFmt.format(Double.NEGATIVE_INFINITY)); | |||
assertEquals("1.#QNAN", _numFmt.format(Double.NaN)); | |||
} | |||
@Test | |||
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)); | |||
assertEquals("8949847", _numFmt.format(8949847f)); | |||
assertEquals("-8949847", _numFmt.format(-8949847f)); | |||
assertEquals("8949.847", _numFmt.format(8949.847f)); | |||
assertEquals("894984", _numFmt.format(894984f)); | |||
assertEquals("8949.84", _numFmt.format(8949.84f)); | |||
assertEquals("8.949856E+16", _numFmt.format(89498564737284944f)); | |||
assertEquals("-8.949856E+16", _numFmt.format(-89498564737284944f)); | |||
assertEquals("895649.9", _numFmt.format(895649.84737284944f)); | |||
assertEquals("300", _numFmt.format(300f)); | |||
assertEquals("-300", _numFmt.format(-300f)); | |||
assertEquals("0.3", _numFmt.format(0.3f)); | |||
assertEquals("0.1", _numFmt.format(0.1f)); | |||
assertEquals("2.342342E-12", _numFmt.format(0.0000000000023423421f)); | |||
assertEquals("2.342342E-11", _numFmt.format(0.000000000023423421f)); | |||
assertEquals("2.342342E-10", _numFmt.format(0.00000000023423421f)); | |||
assertEquals("-2.342342E-10", _numFmt.format(-0.00000000023423421f)); | |||
assertEquals("2.342342E-12", _numFmt.format(0.00000000000234234214f)); | |||
assertEquals("2.342342E-12", _numFmt.format(0.000000000002342342156f)); | |||
assertEquals("0.0000234", _numFmt.format(0.0000234f)); | |||
assertEquals("2.342E-05", _numFmt.format(0.00002342f)); | |||
assertEquals("1.#INF", _numFmt.format(Float.POSITIVE_INFINITY)); | |||
assertEquals("-1.#INF", _numFmt.format(Float.NEGATIVE_INFINITY)); | |||
assertEquals("1.#QNAN", _numFmt.format(Float.NaN)); | |||
} | |||
@Test | |||
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"))); | |||
assertEquals("9874539485972.2342342234234", _numFmt.format(new BigDecimal("9874539485972.2342342234234"))); | |||
assertEquals("9874539485972.234234223423468", _numFmt.format(new BigDecimal("9874539485972.2342342234234678"))); | |||
assertEquals("-9874539485972.234234223423468", _numFmt.format(new BigDecimal("-9874539485972.2342342234234678"))); | |||
assertEquals("9.874539485972234234223423468E+31", _numFmt.format(new BigDecimal("98745394859722342342234234678000"))); | |||
assertEquals("9.874539485972234234223423468E+31", _numFmt.format(new BigDecimal("98745394859722342342234234678000"))); | |||
assertEquals("-9.874539485972234234223423468E+31", _numFmt.format(new BigDecimal("-98745394859722342342234234678000"))); | |||
assertEquals("300", _numFmt.format(new BigDecimal("300.0"))); | |||
assertEquals("-300", _numFmt.format(new BigDecimal("-300.000"))); | |||
assertEquals("0.3", _numFmt.format(new BigDecimal("0.3"))); | |||
assertEquals("0.1", _numFmt.format(new BigDecimal("0.1000"))); | |||
assertEquals("0.0000000000023423428930458", _numFmt.format(new BigDecimal("0.0000000000023423428930458"))); | |||
assertEquals("2.3423428930458389038451E-12", _numFmt.format(new BigDecimal("0.0000000000023423428930458389038451"))); | |||
assertEquals("2.342342893045838903845134766E-12", _numFmt.format(new BigDecimal("0.0000000000023423428930458389038451347656"))); | |||
} | |||
} |