From 6153658f570d65c665af368948a0a81f428799fb Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 5 May 2024 07:42:53 +0800 Subject: [PATCH] Revert "[POI-Bug-67475] Better support for edge cases in TEXT function." --- main/SS/Formula/Eval/OperandResolver.cs | 133 +++++++---- main/SS/Formula/Eval/StringEval.cs | 14 +- main/SS/Formula/Functions/Text/Exact.cs | 4 +- main/SS/Formula/Functions/Text/Text.cs | 187 ++++++++------- .../SS/Formula/Functions/Text/TextFunction.cs | 12 +- main/SS/UserModel/DataFormatter.cs | 15 +- .../main/SS/Formula/Functions/TestText.cs | 226 ++---------------- 7 files changed, 238 insertions(+), 353 deletions(-) diff --git a/main/SS/Formula/Eval/OperandResolver.cs b/main/SS/Formula/Eval/OperandResolver.cs index 58fab3914..992f2dbe1 100644 --- a/main/SS/Formula/Eval/OperandResolver.cs +++ b/main/SS/Formula/Eval/OperandResolver.cs @@ -30,9 +30,9 @@ public class OperandResolver { // Based on regular expression defined in JavaDoc at {@link java.lang.Double#valueOf} // modified to remove support for NaN, Infinity, Hexadecimal support and floating type suffixes - private const string Digits = "\\d+"; - private const string Exp = "[eE][+-]?" + Digits; - private const string fpRegex = + private const String Digits = "\\d+"; + private const String Exp = "[eE][+-]?" + Digits; + private const String fpRegex = ("[\\x00-\\x20]*" + "[+-]?(" + "(((" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" + @@ -58,21 +58,21 @@ private OperandResolver() public static ValueEval GetSingleValue(ValueEval arg, int srcCellRow, int srcCellCol) { ValueEval result; - if (arg is RefEval rev) + if (arg is RefEval) { - result = ChooseSingleElementFromRef(rev); + result = ChooseSingleElementFromRef((RefEval)arg); } - else if (arg is AreaEval aev) + else if (arg is AreaEval) { - result = ChooseSingleElementFromArea(aev, srcCellRow, srcCellCol); + result = ChooseSingleElementFromArea((AreaEval)arg, srcCellRow, srcCellCol); } else { result = arg; } - if (result is ErrorEval eva) + if (result is ErrorEval) { - throw new EvaluationException(eva); + throw new EvaluationException((ErrorEval)result); } return result; } @@ -126,9 +126,9 @@ public static ValueEval ChooseSingleElementFromArea(AreaEval ae, { ValueEval result = ChooseSingleElementFromAreaInternal(ae, srcCellRow, srcCellCol); - if (result is ErrorEval eva) + if (result is ErrorEval) { - throw new EvaluationException(eva); + throw new EvaluationException((ErrorEval)result); } return result; @@ -236,14 +236,14 @@ public static double CoerceValueToDouble(ValueEval ev) { return 0.0; } - if (ev is NumericValueEval nve) + if (ev is NumericValueEval) { // this also handles bools - return nve.NumberValue; + return ((NumericValueEval)ev).NumberValue; } - if (ev is StringEval sev) + if (ev is StringEval) { - double dd = ParseDouble(sev.StringValue); + double dd = ParseDouble(((StringEval)ev).StringValue); if (double.IsNaN(dd)) { throw EvaluationException.InvalidValue(); @@ -269,29 +269,64 @@ public static double CoerceValueToDouble(ValueEval ev) * @param text * @return null if the specified text cannot be Parsed as a number */ - public static double ParseDouble(string pText) + public static double ParseDouble(String pText) { - try - { - double ret = double.Parse(pText, CultureInfo.CurrentCulture); - if (double.IsInfinity(ret)) - return double.NaN; - return ret; - } - catch + //if (Regex.Match(fpRegex, pText).Success) + try + { + double ret = double.Parse(pText, CultureInfo.CurrentCulture); + if (double.IsInfinity(ret)) + return double.NaN; + return ret; + } + catch + { + return Double.NaN; + } + //else { - return double.NaN; + //return Double.NaN; } + //String text = pText.Trim(); + //if (text.Length < 1) + //{ + // return double.NaN; + //} + //bool isPositive = true; + //if (text[0] == '-') + //{ + // isPositive = false; + // text = text.Substring(1).Trim(); + //} + + //if (text.Length == 0 || !Char.IsDigit(text[0])) + //{ + // // avoid using Exception to tell when string is not a number + // return double.NaN; + //} + //// TODO - support notation like '1E3' (==1000) + + //double val; + //try + //{ + // val = double.Parse(text); + //} + //catch + //{ + // return double.NaN; + //} + //return isPositive ? +val : -val; } /** * @param ve must be a NumberEval, StringEval, BoolEval, or BlankEval * @return the Converted string value. never null */ - public static string CoerceValueToString(ValueEval ve) + public static String CoerceValueToString(ValueEval ve) { - if (ve is StringValueEval sve) + if (ve is StringValueEval) { + StringValueEval sve = (StringValueEval)ve; return sve.StringValue; } @@ -301,12 +336,11 @@ public static string CoerceValueToString(ValueEval ve) } throw new ArgumentException("Unexpected eval class (" + ve.GetType().Name + ")"); } - /** - * @return null to represent blank values - * @throws EvaluationException if ve is an ErrorEval, or if a string value cannot be converted - */ - public static bool? CoerceValueToBoolean(ValueEval ve, bool stringsAreBlanks) + * @return null to represent blank values + * @throws EvaluationException if ve is an ErrorEval, or if a string value cannot be converted + */ + public static Boolean? CoerceValueToBoolean(ValueEval ve, bool stringsAreBlanks) { if (ve == null || ve == BlankEval.instance) @@ -314,18 +348,18 @@ public static string CoerceValueToString(ValueEval ve) // TODO - remove 've == null' condition once AreaEval is fixed return null; } - if (ve is BoolEval be) + if (ve is BoolEval) { - return be.BooleanValue; + return ((BoolEval)ve).BooleanValue; } - if (ve is StringEval se) + if (ve is StringEval) { if (stringsAreBlanks) { return null; } - string str = se.StringValue; + String str = ((StringEval)ve).StringValue; if (str.Equals("true", StringComparison.OrdinalIgnoreCase)) { return true; @@ -338,31 +372,32 @@ public static string CoerceValueToString(ValueEval ve) throw new EvaluationException(ErrorEval.VALUE_INVALID); } - if(ve is NumericValueEval ne) + if (ve is NumericValueEval) { + NumericValueEval ne = (NumericValueEval)ve; double d = ne.NumberValue; - if(double.IsNaN(d)) + if (Double.IsNaN(d)) { throw new EvaluationException(ErrorEval.VALUE_INVALID); } return d != 0; } - if (ve is ErrorEval ee) + if (ve is ErrorEval) { - throw new EvaluationException(ee); + throw new EvaluationException((ErrorEval)ve); } throw new InvalidOperationException("Unexpected eval (" + ve.GetType().Name + ")"); } - /** - * Retrieves a single value from an area evaluation utilizing the 2D indices of the cell - * within its own area reference to index the value in the area evaluation. - * - * @param ae area reference after evaluation - * @param cell the source cell of the formula that contains its 2D indices - * @return a NumberEval, StringEval, BoolEval or BlankEval. or ErrorEval - * Never null. - */ + * Retrieves a single value from an area evaluation utilizing the 2D indices of the cell + * within its own area reference to index the value in the area evaluation. + * + * @param ae area reference after evaluation + * @param cell the source cell of the formula that contains its 2D indices + * @return a NumberEval, StringEval, BoolEval or BlankEval. or ErrorEval + * Never null. + */ + public static ValueEval GetElementFromArray(AreaEval ae, IEvaluationCell cell) { CellRangeAddress range = cell.ArrayFormulaRange; diff --git a/main/SS/Formula/Eval/StringEval.cs b/main/SS/Formula/Eval/StringEval.cs index 85688b585..ef776c3ee 100644 --- a/main/SS/Formula/Eval/StringEval.cs +++ b/main/SS/Formula/Eval/StringEval.cs @@ -30,23 +30,27 @@ public class StringEval : StringValueEval { public static readonly StringEval EMPTY_INSTANCE = new StringEval(""); - private readonly string value; + private String value; public StringEval(Ptg ptg):this(((StringPtg)ptg).Value) { } - public StringEval(string value) + public StringEval(String value) { - this.value = value ?? throw new ArgumentException("value must not be null"); + if (value == null) + { + throw new ArgumentException("value must not be null"); + } + this.value = value; } - public string StringValue + public String StringValue { get { return value; } } - public override string ToString() + public override String ToString() { StringBuilder sb = new StringBuilder(64); sb.Append(GetType().Name).Append(" ["); diff --git a/main/SS/Formula/Functions/Text/Exact.cs b/main/SS/Formula/Functions/Text/Exact.cs index 40d132ea0..d91100f6f 100644 --- a/main/SS/Formula/Functions/Text/Exact.cs +++ b/main/SS/Formula/Functions/Text/Exact.cs @@ -38,8 +38,8 @@ public override ValueEval EvaluateFunc(ValueEval[] args, int srcCellRow, int src return ErrorEval.VALUE_INVALID; } - string s0 = EvaluateStringArg(args[0], srcCellRow, srcCellCol); - string s1 = EvaluateStringArg(args[1], srcCellRow, srcCellCol); + String s0 = EvaluateStringArg(args[0], srcCellRow, srcCellCol); + String s1 = EvaluateStringArg(args[1], srcCellRow, srcCellCol); return BoolEval.ValueOf(s0.Equals(s1)); } } diff --git a/main/SS/Formula/Functions/Text/Text.cs b/main/SS/Formula/Functions/Text/Text.cs index c57efa56f..d172c3543 100644 --- a/main/SS/Formula/Functions/Text/Text.cs +++ b/main/SS/Formula/Functions/Text/Text.cs @@ -14,99 +14,122 @@ namespace NPOI.SS.Formula.Functions */ public class Text : Fixed2ArgFunction { - public static DataFormatter Formatter { get; set; } = new(); - - /// - /// An implementation of the TEXT function
- /// TEXT returns a number value formatted with the given number formatting string.
- /// This function is not a complete implementation of the Excel function, but
- /// handles most of the common cases. All work is passed down to
- /// to be done, as this works much the same as the
- /// display focused work that does. - ///
- /// - /// - /// - /// - /// + public static DataFormatter Formatter = new DataFormatter(); public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { - ValueEval valueEval; - + if (arg0 is StringEval) return arg0; + + double s0; + String s1; try { - ValueEval valueVe = OperandResolver.GetSingleValue(arg0, srcRowIndex, srcColumnIndex); - ValueEval formatVe = OperandResolver.GetSingleValue(arg1, srcRowIndex, srcColumnIndex); - - try - { - double valueDouble = double.NaN; - string evaluated = null; - - if (valueVe == BlankEval.instance) - { - valueDouble = 0.0; - } - else if (valueVe is BoolEval boolEval) - { - evaluated = boolEval.StringValue; - } - else if (valueVe is NumericValueEval numericEval) - { - valueDouble = numericEval.NumberValue; - - } - else if (valueVe is StringEval stringEval) - { - evaluated = stringEval.StringValue; - valueDouble = OperandResolver.ParseDouble(evaluated); - } - - if(!double.IsNaN(valueDouble)) - { - string format = FormatPatternValueEval2String(formatVe); - evaluated = Formatter.FormatRawCellContents(valueDouble, -1, format); - } - - valueEval = new StringEval(evaluated); - } - catch(Exception) - { - valueEval = ErrorEval.VALUE_INVALID; - } + s0 = TextFunction.EvaluateDoubleArg(arg0, srcRowIndex, srcColumnIndex); + s1 = TextFunction.EvaluateStringArg(arg1, srcRowIndex, srcColumnIndex); } - catch(EvaluationException e) + catch (EvaluationException e) { - valueEval = e.GetErrorEval(); + return e.GetErrorEval(); } - - return valueEval; - } - - /// - /// Using it instead of in order to handle booleans differently. - /// - /// - /// Pattern value eval formatted to string - /// - private string FormatPatternValueEval2String(ValueEval ve) - { - string format; - - if (ve is not BoolEval && ve is StringValueEval sve) - { - format = sve.StringValue; - } - else if (ve == BlankEval.instance) + try { - format = ""; + // Ask DataFormatter to handle the String for us + String formattedStr = Formatter.FormatRawCellContents(s0, -1, s1); + return new StringEval(formattedStr); } - else + catch { - throw new ArgumentException("Unexpected eval class (" + ve.GetType().Name + ")"); + return ErrorEval.VALUE_INVALID; } - - return format; + //if (Regex.Match(s1, "[y|m|M|d|s|h]+").Success) + //{ + // //may be datetime string + // ValueEval result = TryParseDateTime(s0, s1); + // if (result != ErrorEval.VALUE_INVALID) + // return result; + //} + ////The regular expression needs ^ and $. + //if (Regex.Match(s1, @"^[\d,\#,\.,\$,\,]+$").Success) + //{ + // //TODO: simulate DecimalFormat class in java. + // FormatBase formatter = new DecimalFormat(s1); + // return new StringEval(formatter.Format(s0, CultureInfo.CurrentCulture)); + //} + //else if (s1.IndexOf("/", StringComparison.Ordinal) == s1.LastIndexOf("/", StringComparison.Ordinal) && s1.IndexOf("/", StringComparison.Ordinal) >= 0 && !s1.Contains("-")) + //{ + // double wholePart = Math.Floor(s0); + // double decPart = s0 - wholePart; + // if (wholePart * decPart == 0) + // { + // return new StringEval("0"); + // } + // String[] parts = s1.Split(' '); + // String[] fractParts; + // if (parts.Length == 2) + // { + // fractParts = parts[1].Split('/'); + // } + // else + // { + // fractParts = s1.Split('/'); + // } + + // if (fractParts.Length == 2) + // { + // double minVal = 1.0; + // double currDenom = Math.Pow(10, fractParts[1].Length) - 1d; + // double currNeum = 0; + // for (int i = (int)(Math.Pow(10, fractParts[1].Length) - 1d); i > 0; i--) + // { + // for (int i2 = (int)(Math.Pow(10, fractParts[1].Length) - 1d); i2 > 0; i2--) + // { + // if (minVal >= Math.Abs((double)i2 / (double)i - decPart)) + // { + // currDenom = i; + // currNeum = i2; + // minVal = Math.Abs((double)i2 / (double)i - decPart); + // } + // } + // } + // FormatBase neumFormatter = new DecimalFormat(fractParts[0]); + // FormatBase denomFormatter = new DecimalFormat(fractParts[1]); + // if (parts.Length == 2) + // { + // FormatBase wholeFormatter = new DecimalFormat(parts[0]); + // String result = wholeFormatter.Format(wholePart, CultureInfo.CurrentCulture) + " " + neumFormatter.Format(currNeum, CultureInfo.CurrentCulture) + "/" + denomFormatter.Format(currDenom, CultureInfo.CurrentCulture); + // return new StringEval(result); + // } + // else + // { + // String result = neumFormatter.Format(currNeum + (currDenom * wholePart), CultureInfo.CurrentCulture) + "/" + denomFormatter.Format(currDenom, CultureInfo.CurrentCulture); + // return new StringEval(result); + // } + // } + // else + // { + // return ErrorEval.VALUE_INVALID; + // } + //} + //else + //{ + // return TryParseDateTime(s0, s1); + //} } + //private ValueEval TryParseDateTime(double s0, string s1) + //{ + // try + // { + // FormatBase dateFormatter = new SimpleDateFormat(s1); + // //first month of java Gregorian Calendar month field is 0 + // DateTime dt = new DateTime(1899, 12, 30, 0, 0, 0); + // dt = dt.AddDays((int)Math.Floor(s0)); + // double dayFraction = s0 - Math.Floor(s0); + // dt = dt.AddMilliseconds((int)Math.Round(dayFraction * 24 * 60 * 60 * 1000)); + // return new StringEval(dateFormatter.Format(dt, CultureInfo.CurrentCulture)); + // } + // catch (Exception) + // { + // return ErrorEval.VALUE_INVALID; + // } + //} } } diff --git a/main/SS/Formula/Functions/Text/TextFunction.cs b/main/SS/Formula/Functions/Text/TextFunction.cs index 6489614de..bbbd0f264 100644 --- a/main/SS/Formula/Functions/Text/TextFunction.cs +++ b/main/SS/Formula/Functions/Text/TextFunction.cs @@ -36,10 +36,10 @@ public override ValueEval EvaluateFunc(ValueEval[] args, int srcCellRow, int src { return ErrorEval.VALUE_INVALID; } - string arg = EvaluateStringArg(args[0], srcCellRow, srcCellCol); + String arg = EvaluateStringArg(args[0], srcCellRow, srcCellCol); return Evaluate(arg); } - public abstract ValueEval Evaluate(string arg); + public abstract ValueEval Evaluate(String arg); } /** @@ -47,9 +47,10 @@ public override ValueEval EvaluateFunc(ValueEval[] args, int srcCellRow, int src */ public abstract class TextFunction : Function { - protected static string EMPTY_STRING = ""; - public static string EvaluateStringArg(ValueEval eval, int srcRow, int srcCol) + protected static String EMPTY_STRING = ""; + + public static String EvaluateStringArg(ValueEval eval, int srcRow, int srcCol) { ValueEval ve = OperandResolver.GetSingleValue(eval, srcRow, srcCol); return OperandResolver.CoerceValueToString(ve); @@ -77,7 +78,8 @@ public ValueEval Evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) } internal static bool IsPrintable(char c) { - return c >= 32; + int charCode = (int)c; + return charCode >= 32; } public abstract ValueEval EvaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol); diff --git a/main/SS/UserModel/DataFormatter.cs b/main/SS/UserModel/DataFormatter.cs index c9ebe1daf..3f1e340d7 100644 --- a/main/SS/UserModel/DataFormatter.cs +++ b/main/SS/UserModel/DataFormatter.cs @@ -947,10 +947,10 @@ public string FormatRawCellContents(double value, int formatIndex, string format { FormatBase dateFormat = GetFormat(value, formatIndex, formatString); - if (dateFormat is ExcelStyleDateFormatter excelStyleDateFormat) + if (dateFormat is ExcelStyleDateFormatter) { // Hint about the raw excel value - excelStyleDateFormat.SetDateToBeFormatted(value); + ((ExcelStyleDateFormatter)dateFormat).SetDateToBeFormatted(value); } DateTime d = DateUtil.GetJavaDate(value, use1904Windowing); @@ -982,15 +982,10 @@ public string FormatRawCellContents(double value, int formatIndex, string format } else { - var parsed = decimal.Parse(textValue, currentCulture); - result = numberFormat.Format(parsed); + result = numberFormat.Format(decimal.Parse(textValue)); } - - // If they requested a non-abbreviated Scientific format, - // and there's an E## (but not E-##), add the missing '+' for E+## - string fslc = formatString.ToLower(currentCulture); - if((fslc.Contains("general") || fslc.Contains("e+0")) && - result.Contains("E") && !result.Contains("E-")) + // Complete scientific notation by adding the missing +. + if (result.Contains("E") && !result.Contains("E-")) { result = result.Replace("E", "E+"); } diff --git a/testcases/main/SS/Formula/Functions/TestText.cs b/testcases/main/SS/Formula/Functions/TestText.cs index d7793d765..65cc5b280 100644 --- a/testcases/main/SS/Formula/Functions/TestText.cs +++ b/testcases/main/SS/Formula/Functions/TestText.cs @@ -24,8 +24,6 @@ namespace TestCases.SS.Formula.Functions using NPOI.SS.Util; using NPOI.SS.Formula.Functions; using System.Globalization; - using System.Collections.Generic; - using NPOI.SS.UserModel; /** * Test case for TEXT() @@ -35,44 +33,28 @@ namespace TestCases.SS.Formula.Functions [TestFixture] public class TestText { - private readonly List EXCEL_ERRORS = new(11) { - ErrorEval.NULL_INTERSECTION, - ErrorEval.DIV_ZERO, - ErrorEval.VALUE_INVALID, - ErrorEval.REF_INVALID, - ErrorEval.NAME_INVALID, - ErrorEval.NUM_ERROR, - ErrorEval.NA - }; - - private readonly CultureInfo _currentCulture = CultureInfo.InvariantCulture; - - [OneTimeSetUp] - public void SetUp() - { - Text.Formatter = new DataFormatter(_currentCulture); - } - //private static TextFunction T = null; [Test] public void TestTextWithStringFirstArg() { + ValueEval strArg = new StringEval("abc"); ValueEval formatArg = new StringEval("abc"); ValueEval[] args = { strArg, formatArg }; - ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1); - Assert.AreEqual(strArg.ToString(), result.ToString()); + ValueEval result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); + Assert.AreEqual(strArg, result); } - [Test] - public void TestTextWithDecimalFormatSecondArg() + public void TestTextWithDeciamlFormatSecondArg() { ValueEval numArg = new NumberEval(321321.321); ValueEval formatArg = new StringEval("#,###.00000"); ValueEval[] args = { numArg, formatArg }; - ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1); - - NumberFormatInfo fs = _currentCulture.NumberFormat; + ValueEval result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); + //char groupSeparator = new DecimalFormatSymbols(Locale.GetDefault()).GetGroupingSeparator(); + //char decimalSeparator = new DecimalFormatSymbols(Locale.GetDefault()).GetDecimalSeparator(); + + NumberFormatInfo fs = CultureInfo.GetCultureInfo("en-US").NumberFormat; string groupSeparator = fs.NumberGroupSeparator; string decimalSeparator = fs.NumberDecimalSeparator; ; @@ -88,227 +70,71 @@ public void TestTextWithDecimalFormatSecondArg() formatArg = new StringEval("$#.#"); args[1] = formatArg; - result = TextFunction.TEXT.Evaluate(args, -1, -1); + result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); testResult = new StringEval("$321" + decimalSeparator + "3"); Assert.AreEqual(testResult.ToString(), result.ToString()); } - [Test] public void TestTextWithFractionFormatSecondArg() { + ValueEval numArg = new NumberEval(321.321); ValueEval formatArg = new StringEval("# #/#"); ValueEval[] args = { numArg, formatArg }; - ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1); + ValueEval result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); ValueEval testResult = new StringEval("321 1/3"); - Assert.AreEqual(testResult.ToString(), result.ToString()); + Assert.AreEqual(testResult.ToString(), result.ToString()); //this bug is caused by DecimalFormat formatArg = new StringEval("# #/##"); args[1] = formatArg; - result = TextFunction.TEXT.Evaluate(args, -1, -1); + result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); testResult = new StringEval("321 26/81"); Assert.AreEqual(testResult.ToString(), result.ToString()); formatArg = new StringEval("#/##"); args[1] = formatArg; - result = TextFunction.TEXT.Evaluate(args, -1, -1); + result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); testResult = new StringEval("26027/81"); Assert.AreEqual(testResult.ToString(), result.ToString()); } - [Test] public void TestTextWithDateFormatSecondArg() { + // Test with Java style M=Month + System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("en-US"); ValueEval numArg = new NumberEval(321.321); ValueEval formatArg = new StringEval("dd:MM:yyyy hh:mm:ss"); ValueEval[] args = { numArg, formatArg }; - ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1); + ValueEval result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); ValueEval testResult = new StringEval("16:11:1900 07:42:14"); Assert.AreEqual(testResult.ToString(), result.ToString()); // Excel also supports "m before h is month" formatArg = new StringEval("dd:mm:yyyy hh:mm:ss"); args[1] = formatArg; - result = TextFunction.TEXT.Evaluate(args, -1, -1); + result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); testResult = new StringEval("16:11:1900 07:42:14"); - Assert.AreEqual(testResult.ToString(), result.ToString()); + //Assert.AreEqual(testResult.ToString(), result.ToString()); // this line is intended to compute how "November" would look like in the current locale - string november = new SimpleDateFormat("MMMM").Format(new DateTime(2010, 11, 15), CultureInfo.CurrentCulture); + String november = new SimpleDateFormat("MMMM").Format(new DateTime(2010, 11, 15), CultureInfo.CurrentCulture); // Again with Java style formatArg = new StringEval("MMMM dd, yyyy"); args[1] = formatArg; - - result = TextFunction.TEXT.Evaluate(args, -1, -1); + //fix error in non-en Culture + NPOI.SS.Formula.Functions.Text.Formatter = new NPOI.SS.UserModel.DataFormatter(CultureInfo.CurrentCulture); + result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); testResult = new StringEval(november + " 16, 1900"); Assert.AreEqual(testResult.ToString(), result.ToString()); // And Excel style formatArg = new StringEval("mmmm dd, yyyy"); args[1] = formatArg; - result = TextFunction.TEXT.Evaluate(args, -1, -1); + result = TextFunction.TEXT.Evaluate(args, -1, (short)-1); testResult = new StringEval(november + " 16, 1900"); Assert.AreEqual(testResult.ToString(), result.ToString()); } - - [Test] - public void TestTextWithISODateTimeFormatSecondArg() - { - ValueEval numArg = new NumberEval(321.321); - ValueEval formatArg = new StringEval("yyyy-mm-ddThh:MM:ss"); - ValueEval[] args = { numArg, formatArg }; - ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1); - ValueEval testResult = new StringEval("1900-11-16T07:42:14"); - Assert.AreEqual(testResult.ToString(), result.ToString()); - - // test milliseconds - formatArg = new StringEval("yyyy-mm-ddThh:MM:ss.000"); - args[1] = formatArg; - result = TextFunction.TEXT.Evaluate(args, -1, -1); - testResult = new StringEval("1900-11-16T07:42:14.400"); - Assert.AreEqual(testResult.ToString(), result.ToString()); - } - - // Test cases from the workbook attached to the bug 67475 which were OK - - [Test] - public void TestTextVariousValidNumberFormats() - { - // negative values: 3 decimals - Testtext(new NumberEval(-123456.789012345), new StringEval("#0.000"), "-123456.789"); - // no decimals - Testtext(new NumberEval(-123456.789012345), new StringEval("000000"), "-123457"); - // common format - more digits - Testtext(new NumberEval(-123456.789012345), new StringEval("00.0000"), "-123456.7890"); - // common format - less digits - Testtext(new NumberEval(-12.78), new StringEval("00000.000000"), "-00012.780000"); - // half up - Testtext(new NumberEval(-0.56789012375), new StringEval("#0.0000000000"), "-0.5678901238"); - // half up - Testtext(new NumberEval(-0.56789012385), new StringEval("#0.0000000000"), "-0.5678901239"); - // positive values: 3 decimals - Testtext(new NumberEval(123456.789012345), new StringEval("#0.000"), "123456.789"); - // no decimals - Testtext(new NumberEval(123456.789012345), new StringEval("000000"), "123457"); - // common format - more digits - Testtext(new NumberEval(123456.789012345), new StringEval("00.0000"), "123456.7890"); - // common format - less digits - Testtext(new NumberEval(12.78), new StringEval("00000.000000"), "00012.780000"); - // half up - Testtext(new NumberEval(0.56789012375), new StringEval("#0.0000000000"), "0.5678901238"); - // half up - Testtext(new NumberEval(0.56789012385), new StringEval("#0.0000000000"), "0.5678901239"); - } - - [Test] - public void testTextBlankTreatedAsZero() - { - Testtext(BlankEval.instance, new StringEval("#0.000"), "0.000"); - } - - [Test] - public void testTextStrangeFormat() - { - // number 0 - Testtext(new NumberEval(-123456.789012345), new NumberEval(0), "-123457"); - // negative number with few zeros - Testtext(new NumberEval(-123456.789012345), new NumberEval(-0.0001), "--123456.7891"); - // format starts with "." - Testtext(new NumberEval(0.0123), new StringEval(".000"), ".012"); - // one zero negative - Testtext(new NumberEval(1001.202), new NumberEval(-8808), "-8810018"); - // format contains 0 - Testtext(new NumberEval(43368.0), new NumberEval(909), "9433689"); - } - - [Test] - public void TestTextErrorAsFormat() - { - foreach(ErrorEval errorEval in EXCEL_ERRORS) - { - Testtext(new NumberEval(3.14), errorEval, errorEval); - Testtext(BoolEval.TRUE, errorEval, errorEval); - Testtext(BoolEval.FALSE, errorEval, errorEval); - } - } - - [Test] - public void TestTextErrorAsValue() - { - foreach(ErrorEval errorEval in EXCEL_ERRORS) - { - Testtext(errorEval, new StringEval("#0.000"), errorEval); - Testtext(errorEval, new StringEval("yyyymmmdd"), errorEval); - } - } - - // Test cases from the workbook attached to the bug 67475 which were failing and are fixed by the patch - - [Test] - public void TestTextEmptyStringWithDateFormat() - { - Testtext(new StringEval(""), new StringEval("yyyymmmdd"), ""); - } - - [Test] - public void TestTextAnyTextWithDateFormat() - { - Testtext(new StringEval("anyText"), new StringEval("yyyymmmdd"), "anyText"); - } - - [Test] - public void TestTextBooleanWithDateFormat() - { - Testtext(BoolEval.TRUE, new StringEval("yyyymmmdd"), BoolEval.TRUE.StringValue); - Testtext(BoolEval.FALSE, new StringEval("yyyymmmdd"), BoolEval.FALSE.StringValue); - } - - [Test] - public void TestTextNumberWithBooleanFormat() - { - Testtext(new NumberEval(43368), BoolEval.TRUE, ErrorEval.VALUE_INVALID); - Testtext(new NumberEval(43368), BoolEval.FALSE, ErrorEval.VALUE_INVALID); - - Testtext(new NumberEval(3.14), BoolEval.TRUE, ErrorEval.VALUE_INVALID); - Testtext(new NumberEval(3.14), BoolEval.FALSE, ErrorEval.VALUE_INVALID); - } - - [Test] - public void TestTextEmptyStringWithNumberFormat() - { - Testtext(new StringEval(""), new StringEval("#0.000"), ""); - } - - [Test] - public void TestTextAnyTextWithNumberFormat() - { - Testtext(new StringEval("anyText"), new StringEval("#0.000"), "anyText"); - } - - [Test] - public void TestTextBooleanWithNumberFormat() - { - Testtext(BoolEval.TRUE, new StringEval("#0.000"), BoolEval.TRUE.StringValue); - Testtext(BoolEval.FALSE, new StringEval("#0.000"), BoolEval.FALSE.StringValue); - } - - private static void Testtext(ValueEval valueArg, ValueEval formatArg, string expectedResult) - { - ValueEval[] args = { valueArg, formatArg }; - ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1); - - Assert.IsTrue(result is StringEval, "Expected StringEval got " + result.GetType().Name); - Assert.AreEqual(expectedResult, ((StringEval) result).StringValue); - } - - private static void Testtext(ValueEval valueArg, ValueEval formatArg, ErrorEval expectedResult) - { - ValueEval[] args = { valueArg, formatArg }; - ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1); - - Assert.IsTrue(result is ErrorEval, "Expected ErrorEval got " + result.GetType().Name); - Assert.AreEqual(expectedResult, result); - } } + } \ No newline at end of file