diff --git a/main/SS/Formula/Eval/OperandResolver.cs b/main/SS/Formula/Eval/OperandResolver.cs
index 992f2dbe1..58fab3914 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)
+ if (arg is RefEval rev)
{
- result = ChooseSingleElementFromRef((RefEval)arg);
+ result = ChooseSingleElementFromRef(rev);
}
- else if (arg is AreaEval)
+ else if (arg is AreaEval aev)
{
- result = ChooseSingleElementFromArea((AreaEval)arg, srcCellRow, srcCellCol);
+ result = ChooseSingleElementFromArea(aev, srcCellRow, srcCellCol);
}
else
{
result = arg;
}
- if (result is ErrorEval)
+ if (result is ErrorEval eva)
{
- throw new EvaluationException((ErrorEval)result);
+ throw new EvaluationException(eva);
}
return result;
}
@@ -126,9 +126,9 @@ public static ValueEval ChooseSingleElementFromArea(AreaEval ae,
{
ValueEval result = ChooseSingleElementFromAreaInternal(ae, srcCellRow, srcCellCol);
- if (result is ErrorEval)
+ if (result is ErrorEval eva)
{
- throw new EvaluationException((ErrorEval)result);
+ throw new EvaluationException(eva);
}
return result;
@@ -236,14 +236,14 @@ public static double CoerceValueToDouble(ValueEval ev)
{
return 0.0;
}
- if (ev is NumericValueEval)
+ if (ev is NumericValueEval nve)
{
// this also handles bools
- return ((NumericValueEval)ev).NumberValue;
+ return nve.NumberValue;
}
- if (ev is StringEval)
+ if (ev is StringEval sev)
{
- double dd = ParseDouble(((StringEval)ev).StringValue);
+ double dd = ParseDouble(sev.StringValue);
if (double.IsNaN(dd))
{
throw EvaluationException.InvalidValue();
@@ -269,64 +269,29 @@ 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)
{
- //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
+ try
{
- //return Double.NaN;
+ double ret = double.Parse(pText, CultureInfo.CurrentCulture);
+ if (double.IsInfinity(ret))
+ return double.NaN;
+ return ret;
+ }
+ catch
+ {
+ 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)
+ if (ve is StringValueEval sve)
{
- StringValueEval sve = (StringValueEval)ve;
return sve.StringValue;
}
@@ -336,11 +301,12 @@ 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 Boolean? 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 bool? CoerceValueToBoolean(ValueEval ve, bool stringsAreBlanks)
{
if (ve == null || ve == BlankEval.instance)
@@ -348,18 +314,18 @@ public static String CoerceValueToString(ValueEval ve)
// TODO - remove 've == null' condition once AreaEval is fixed
return null;
}
- if (ve is BoolEval)
+ if (ve is BoolEval be)
{
- return ((BoolEval)ve).BooleanValue;
+ return be.BooleanValue;
}
- if (ve is StringEval)
+ if (ve is StringEval se)
{
if (stringsAreBlanks)
{
return null;
}
- String str = ((StringEval)ve).StringValue;
+ string str = se.StringValue;
if (str.Equals("true", StringComparison.OrdinalIgnoreCase))
{
return true;
@@ -372,32 +338,31 @@ public static String CoerceValueToString(ValueEval ve)
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
- if (ve is NumericValueEval)
+ if(ve is NumericValueEval ne)
{
- 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)
+ if (ve is ErrorEval ee)
{
- throw new EvaluationException((ErrorEval)ve);
+ throw new EvaluationException(ee);
}
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 ef776c3ee..85688b585 100644
--- a/main/SS/Formula/Eval/StringEval.cs
+++ b/main/SS/Formula/Eval/StringEval.cs
@@ -30,27 +30,23 @@ public class StringEval : StringValueEval
{
public static readonly StringEval EMPTY_INSTANCE = new StringEval("");
- private String value;
+ private readonly string value;
public StringEval(Ptg ptg):this(((StringPtg)ptg).Value)
{
}
- public StringEval(String value)
+ public StringEval(string value)
{
- if (value == null)
- {
- throw new ArgumentException("value must not be null");
- }
- this.value = value;
+ this.value = value ?? throw new ArgumentException("value must not be null");
}
- 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 d91100f6f..40d132ea0 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 d172c3543..c57efa56f 100644
--- a/main/SS/Formula/Functions/Text/Text.cs
+++ b/main/SS/Formula/Functions/Text/Text.cs
@@ -14,122 +14,99 @@ namespace NPOI.SS.Formula.Functions
*/
public class Text : Fixed2ArgFunction
{
- public static DataFormatter Formatter = new DataFormatter();
+ 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 override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1)
{
- if (arg0 is StringEval) return arg0;
-
- double s0;
- String s1;
+ ValueEval valueEval;
+
try
{
- s0 = TextFunction.EvaluateDoubleArg(arg0, srcRowIndex, srcColumnIndex);
- s1 = TextFunction.EvaluateStringArg(arg1, srcRowIndex, srcColumnIndex);
+ 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;
+ }
}
- catch (EvaluationException e)
+ catch(EvaluationException e)
{
- return e.GetErrorEval();
+ valueEval = e.GetErrorEval();
}
- try
+
+ 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)
{
- // Ask DataFormatter to handle the String for us
- String formattedStr = Formatter.FormatRawCellContents(s0, -1, s1);
- return new StringEval(formattedStr);
+ format = "";
}
- catch
+ else
{
- return ErrorEval.VALUE_INVALID;
+ throw new ArgumentException("Unexpected eval class (" + ve.GetType().Name + ")");
}
- //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);
- //}
+
+ return format;
}
- //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 bbbd0f264..6489614de 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,10 +47,9 @@ public override ValueEval EvaluateFunc(ValueEval[] args, int srcCellRow, int src
*/
public abstract class TextFunction : Function
{
+ protected static string EMPTY_STRING = "";
- protected static String EMPTY_STRING = "";
-
- public static String EvaluateStringArg(ValueEval eval, int srcRow, int srcCol)
+ public static string EvaluateStringArg(ValueEval eval, int srcRow, int srcCol)
{
ValueEval ve = OperandResolver.GetSingleValue(eval, srcRow, srcCol);
return OperandResolver.CoerceValueToString(ve);
@@ -78,8 +77,7 @@ public ValueEval Evaluate(ValueEval[] args, int srcCellRow, int srcCellCol)
}
internal static bool IsPrintable(char c)
{
- int charCode = (int)c;
- return charCode >= 32;
+ return c >= 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 8a2b9102d..f6b1554ec 100644
--- a/main/SS/UserModel/DataFormatter.cs
+++ b/main/SS/UserModel/DataFormatter.cs
@@ -881,7 +881,7 @@ private String GetFormattedNumberString(ICell cell)
* FormatBase index and string, according to excel style rules.
* @see #FormatCellValue(Cell)
*/
- public String FormatRawCellContents(double value, int formatIndex, String formatString)
+ public string FormatRawCellContents(double value, int formatIndex, string formatString)
{
return FormatRawCellContents(value, formatIndex, formatString, false);
}
@@ -889,7 +889,7 @@ public String FormatRawCellContents(double value, int formatIndex, String format
* Performs Excel-style date formatting, using the
* supplied Date and format
*/
- private String PerformDateFormatting(DateTime d, FormatBase dateFormat)
+ private string PerformDateFormatting(DateTime d, FormatBase dateFormat)
{
if (dateFormat != null)
{
@@ -902,7 +902,7 @@ private String PerformDateFormatting(DateTime d, FormatBase dateFormat)
* format index and string, according to excel style rules.
* @see #formatCellValue(Cell)
*/
- public String FormatRawCellContents(double value, int formatIndex, String formatString, bool use1904Windowing)
+ public string FormatRawCellContents(double value, int formatIndex, string formatString, bool use1904Windowing)
{
// Is it a date?
if (DateUtil.IsADateFormat(formatIndex, formatString))
@@ -911,10 +911,10 @@ public String FormatRawCellContents(double value, int formatIndex, String format
{
FormatBase dateFormat = GetFormat(value, formatIndex, formatString);
- if (dateFormat is ExcelStyleDateFormatter)
+ if (dateFormat is ExcelStyleDateFormatter excelStyleDateFormat)
{
// Hint about the raw excel value
- ((ExcelStyleDateFormatter)dateFormat).SetDateToBeFormatted(value);
+ excelStyleDateFormat.SetDateToBeFormatted(value);
}
DateTime d = DateUtil.GetJavaDate(value, use1904Windowing);
@@ -938,18 +938,23 @@ public String FormatRawCellContents(double value, int formatIndex, String format
// previous versions). However, if the value contains E notation, this
// would expand the values, which we do not want, so revert to
// original method.
- String result;
- String textValue = NumberToTextConverter.ToText(value);
+ string result;
+ string textValue = NumberToTextConverter.ToText(value);
if (textValue.IndexOf('E') > -1)
{
result = numberFormat.Format(value);
}
else
{
- result = numberFormat.Format(decimal.Parse(textValue));
+ var parsed = decimal.Parse(textValue, currentCulture);
+ result = numberFormat.Format(parsed);
}
- // Complete scientific notation by adding the missing +.
- if (result.Contains("E") && !result.Contains("E-"))
+
+ // 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-"))
{
result = result.Replace("E", "E+");
}
diff --git a/testcases/main/SS/Formula/Functions/TestText.cs b/testcases/main/SS/Formula/Functions/TestText.cs
index 65cc5b280..d7793d765 100644
--- a/testcases/main/SS/Formula/Functions/TestText.cs
+++ b/testcases/main/SS/Formula/Functions/TestText.cs
@@ -24,6 +24,8 @@ 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()
@@ -33,28 +35,44 @@ 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, (short)-1);
- Assert.AreEqual(strArg, result);
+ ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1);
+ Assert.AreEqual(strArg.ToString(), result.ToString());
}
+
[Test]
- public void TestTextWithDeciamlFormatSecondArg()
+ public void TestTextWithDecimalFormatSecondArg()
{
ValueEval numArg = new NumberEval(321321.321);
ValueEval formatArg = new StringEval("#,###.00000");
ValueEval[] args = { numArg, formatArg };
- 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;
+ ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1);
+
+ NumberFormatInfo fs = _currentCulture.NumberFormat;
string groupSeparator = fs.NumberGroupSeparator;
string decimalSeparator = fs.NumberDecimalSeparator; ;
@@ -70,71 +88,227 @@ public void TestTextWithDeciamlFormatSecondArg()
formatArg = new StringEval("$#.#");
args[1] = formatArg;
- result = TextFunction.TEXT.Evaluate(args, -1, (short)-1);
+ result = TextFunction.TEXT.Evaluate(args, -1, -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, (short)-1);
+ ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -1);
ValueEval testResult = new StringEval("321 1/3");
- Assert.AreEqual(testResult.ToString(), result.ToString()); //this bug is caused by DecimalFormat
+ Assert.AreEqual(testResult.ToString(), result.ToString());
formatArg = new StringEval("# #/##");
args[1] = formatArg;
- result = TextFunction.TEXT.Evaluate(args, -1, (short)-1);
+ result = TextFunction.TEXT.Evaluate(args, -1, -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, (short)-1);
+ result = TextFunction.TEXT.Evaluate(args, -1, -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, (short)-1);
+ ValueEval result = TextFunction.TEXT.Evaluate(args, -1, -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, (short)-1);
+ result = TextFunction.TEXT.Evaluate(args, -1, -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;
- //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);
+
+ result = TextFunction.TEXT.Evaluate(args, -1, -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, (short)-1);
+ result = TextFunction.TEXT.Evaluate(args, -1, -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