From 672387c5a4499e6c104a1c09dc845a5cdb6c62f1 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Sat, 3 Feb 2024 18:17:33 +0000 Subject: [PATCH] fix NumberFormatter so that it formats according to the locale of the current db git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1406 f203690c-595d-4dc9-a70b-905162fa7fd2 --- pom.xml | 1 - src/changes/changes.xml | 6 + .../jackcess/expr/NumericConfig.java | 26 ++++ .../jackcess/impl/BaseEvalContext.java | 2 - .../jackcess/impl/expr/BigDecimalValue.java | 2 +- .../jackcess/impl/expr/DoubleValue.java | 2 +- .../impl/expr/ExpressionTokenizer.java | 13 +- .../jackcess/impl/expr/NumberFormatter.java | 50 +++----- .../impl/expr/ExpressionatorTest.java | 13 ++ .../impl/expr/NumberFormatterTest.java | 121 +++++++++--------- 10 files changed, 137 insertions(+), 99 deletions(-) diff --git a/pom.xml b/pom.xml index a7e7eeb..0b57a82 100644 --- a/pom.xml +++ b/pom.xml @@ -131,7 +131,6 @@ ${jackcess.testFormats} - -Duser.language=en -Duser.region=US diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 329a82f..7f19bd4 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -4,6 +4,12 @@ Tim McCune + + + Make NumberFormatter locale aware, using the currently configured + LocaleContext. + + diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/NumericConfig.java b/src/main/java/com/healthmarketscience/jackcess/expr/NumericConfig.java index d231522..7f662ca 100644 --- a/src/main/java/com/healthmarketscience/jackcess/expr/NumericConfig.java +++ b/src/main/java/com/healthmarketscience/jackcess/expr/NumericConfig.java @@ -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); + } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java index 36b32d8..80c63aa 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java @@ -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; diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BigDecimalValue.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BigDecimalValue.java index 56bc911..801c830 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BigDecimalValue.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BigDecimalValue.java @@ -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 diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DoubleValue.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DoubleValue.java index e00e24a..d766983 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DoubleValue.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DoubleValue.java @@ -65,6 +65,6 @@ public class DoubleValue extends BaseNumericValue @Override public String getAsString(LocaleContext ctx) { - return NumberFormatter.format(_val); + return ctx.getNumericConfig().format(_val); } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java index 544c08b..3770487 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java @@ -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; diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/NumberFormatter.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/NumberFormatter.java index 57a5e35..4f886ff 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/NumberFormatter.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/NumberFormatter.java @@ -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 INSTANCE = - new ThreadLocal() { - @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) { 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 67ad20b..ccdf087 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java @@ -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); } diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/expr/NumberFormatterTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/expr/NumberFormatterTest.java index acc2a1a..11a0adf 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/expr/NumberFormatterTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/expr/NumberFormatterTest.java @@ -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"))); } } -- 2.39.5