diff --git a/main/SS/UserModel/DataFormatter.cs b/main/SS/UserModel/DataFormatter.cs index 8a2b9102d..3f1e340d7 100644 --- a/main/SS/UserModel/DataFormatter.cs +++ b/main/SS/UserModel/DataFormatter.cs @@ -99,25 +99,25 @@ namespace NPOI.SS.UserModel */ public class DataFormatter { - private static String defaultFractionWholePartFormat = "#"; - private static String defaultFractionFractionPartFormat = "#/##"; + private static readonly string defaultFractionWholePartFormat = "#"; + private static readonly string defaultFractionFractionPartFormat = "#/##"; /** Pattern to find a number FormatBase: "0" or "#" */ - private static string numPattern = "[0#]+"; + private static readonly string numPattern = "[0#]+"; /** Pattern to find "AM/PM" marker */ - private static string amPmPattern = "((A|P)[M/P]*)"; + private static readonly string amPmPattern = "((A|P)[M/P]*)"; /** A regex to find patterns like [$$-1009] and [$�-452]. * Note that we don't currently process these into locales */ - private static string localePatternGroup = "(\\[\\$[^-\\]]*-[0-9A-Z]+\\])"; + private static readonly string localePatternGroup = "(\\[\\$[^-\\]]*-[0-9A-Z]+\\])"; /* * A regex to match the colour formattings rules. * Allowed colours are: Black, Blue, Cyan, Green, * Magenta, Red, White, Yellow, "Color n" (1<=n<=56) */ - private static Regex colorPattern = new Regex("(\\[BLACK\\])|(\\[BLUE\\])|(\\[CYAN\\])|(\\[GREEN\\])|" + + private static readonly Regex colorPattern = new Regex("(\\[BLACK\\])|(\\[BLUE\\])|(\\[CYAN\\])|(\\[GREEN\\])|" + "(\\[MAGENTA\\])|(\\[RED\\])|(\\[WHITE\\])|(\\[YELLOW\\])|" + "(\\[COLOR\\s*\\d\\])|(\\[COLOR\\s*[0-5]\\d\\])", RegexOptions.IgnoreCase | RegexOptions.Compiled); @@ -125,24 +125,25 @@ public class DataFormatter * A regex to identify a fraction pattern. * This requires that replaceAll("\\?", "#") has already been called */ - private static Regex fractionPattern = new Regex("(?:([#\\d]+)\\s+)?(#+)\\s*\\/\\s*([#\\d]+)", RegexOptions.Compiled); + private static readonly Regex fractionPattern = new Regex("(?:([#\\d]+)\\s+)?(#+)\\s*\\/\\s*([#\\d]+)", RegexOptions.Compiled); /** * A regex to strip junk out of fraction formats */ - private static Regex fractionStripper = new Regex("(\"[^\"]*\")|([^ \\?#\\d\\/]+)", RegexOptions.Compiled); + private static readonly Regex fractionStripper = new Regex("(\"[^\"]*\")|([^ \\?#\\d\\/]+)", RegexOptions.Compiled); /** * A regex to detect if an alternate grouping character is used * in a numeric format */ - private static Regex alternateGrouping = new Regex("([#0]([^.#0])[#0]{3})", RegexOptions.Compiled); + private static readonly Regex alternateGrouping = new Regex("([#0]([^.#0])[#0]{3})", RegexOptions.Compiled); /** * Cells formatted with a date or time format and which contain invalid date or time values * show 255 pound signs ("#"). */ - private static String invalidDateTimeString; + private static readonly string invalidDateTimeString; + static DataFormatter() { StringBuilder buf = new StringBuilder(); @@ -150,21 +151,20 @@ static DataFormatter() invalidDateTimeString = buf.ToString(); } - /** * The decimal symbols of the locale used for formatting values. */ - private NumberFormatInfo decimalSymbols; + private readonly NumberFormatInfo decimalSymbols; /** * The date symbols of the locale used for formatting values. */ - private DateTimeFormatInfo dateSymbols; + private readonly DateTimeFormatInfo dateSymbols; /** * A default date format, if no date format was given */ - private DateFormat defaultDateformat; + private readonly DateFormat defaultDateformat; /** General FormatBase for whole numbers. */ //private static DecimalFormat generalWholeNumFormat = new DecimalFormat("0"); @@ -176,22 +176,37 @@ static DataFormatter() /** A default FormatBase to use when a number pattern cannot be Parsed. */ private FormatBase defaultNumFormat; private CultureInfo currentCulture; + /* * A map to cache formats. * Map Formats */ - private Hashtable formats; - private bool emulateCSV = false; + private readonly Hashtable formats; + + /** whether CSV friendly adjustments should be made to the formatted text **/ + private readonly bool emulateCSV = false; /** For logging any problems we find */ - private static POILogger logger = POILogFactory.GetLogger(typeof(DataFormatter)); + private static readonly POILogger logger = POILogFactory.GetLogger(typeof(DataFormatter)); /** stores if the locale should change according to {@link LocaleUtil#getUserLocale()} */ - private bool localeIsAdapting; + private readonly bool localeIsAdapting; + + /** whether years in dates should be displayed with 4 digits even if the formatString specifies only 2 **/ + public bool Use4DigitYearsInAllDateFormats { get; set; } = false; + + /// + /// If set to true, when you do not provide a , + /// for cells with formulas, we will return the cached value for the cell (if available), + /// otherwise - we return the formula itself. + /// The default is false and this means we return the formula itself. + /// + public bool UseCachedValuesForFormulaCells { get; set; } = false; public DataFormatter() : this(false) { } + /** * Creates a formatter using the {@link Locale#getDefault() default locale}. * @@ -287,7 +302,7 @@ private FormatBase GetFormat(ICell cell) } int formatIndex = cell.CellStyle.DataFormat; - String formatStr = cell.CellStyle.GetDataFormatString(); + string formatStr = cell.CellStyle.GetDataFormatString(); if (formatStr == null || formatStr.Trim().Length == 0) { return null; @@ -295,15 +310,15 @@ private FormatBase GetFormat(ICell cell) return GetFormat(cell.NumericCellValue, formatIndex, formatStr); } - private FormatBase GetFormat(double cellValue, int formatIndex, String formatStrIn) + private FormatBase GetFormat(double cellValue, int formatIndex, string formatStrIn) { // // Might be better to separate out the n p and z formats, falling back to p when n and z are not set. // // That however would require other code to be re factored. // String[] formatBits = formatStrIn.split(";"); // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2; - // String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0]; + // string formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0]; - String formatStr = formatStrIn; + string formatStr = formatStrIn; // Excel supports 3+ part conditional data formats, eg positive/negative/zero, // or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds @@ -400,18 +415,14 @@ public FormatBase CreateFormat(ICell cell) { int formatIndex = cell.CellStyle.DataFormat; - String formatStr = cell.CellStyle.GetDataFormatString(); + string formatStr = cell.CellStyle.GetDataFormatString(); return CreateFormat(cell.NumericCellValue, formatIndex, formatStr); } - private static readonly Regex RegexDoubleBackslashAny = new Regex("\\\\.", RegexOptions.Compiled); - private static readonly Regex RegexContinueWs = new Regex("\\s", RegexOptions.Compiled); - private static readonly Regex RegexAnyInDoubleQuote = new Regex("\"[^\"]*\"", RegexOptions.Compiled); - - private FormatBase CreateFormat(double cellValue, int formatIndex, String sFormat) + private FormatBase CreateFormat(double cellValue, int formatIndex, string sFormat) { // remove color Formatting if present - String formatStr = colorPattern.Replace(sFormat, ""); + string formatStr = colorPattern.Replace(sFormat, ""); // Strip off the locale information, we use an instance-wide locale for everything MatchCollection matches = Regex.Matches(formatStr, localePatternGroup); @@ -420,7 +431,7 @@ private FormatBase CreateFormat(double cellValue, int formatIndex, String sForma string matchedstring = match.Value; int beginpos = matchedstring.IndexOf('$') + 1; int endpos = matchedstring.IndexOf('-'); - String symbol = matchedstring.Substring(beginpos, endpos - beginpos); + string symbol = matchedstring.Substring(beginpos, endpos - beginpos); if (symbol.IndexOf('$') > -1) { @@ -460,7 +471,7 @@ private FormatBase CreateFormat(double cellValue, int formatIndex, String sForma String[] chunks = formatStr.Split(";".ToCharArray()); for (int i = 0; i < chunks.Length; i++) { - String chunk = chunks[i].Replace("?", "#"); + string chunk = chunks[i].Replace("?", "#"); //Match matcher = fractionStripper.Match(chunk); //chunk = matcher.Replace(" "); chunk = fractionStripper.Replace(chunk, " "); @@ -469,7 +480,7 @@ private FormatBase CreateFormat(double cellValue, int formatIndex, String sForma //take the first match if (fractionMatcher.Success) { - String wholePart = (fractionMatcher.Groups[1] == null || !fractionMatcher.Groups[1].Success) ? "" : defaultFractionWholePartFormat; + string wholePart = (fractionMatcher.Groups[1] == null || !fractionMatcher.Groups[1].Success) ? "" : defaultFractionWholePartFormat; return new FractionFormat(wholePart, fractionMatcher.Groups[3].Value); } } @@ -492,22 +503,47 @@ private FormatBase CreateFormat(double cellValue, int formatIndex, String sForma // TODO - when does this occur? return null; } - private int IndexOfFraction(String format) - { - int i = format.IndexOf("#/#"); - int j = format.IndexOf("?/?"); - return i == -1 ? j : j == -1 ? i : Math.Min(i, j); - } - private int LastIndexOfFraction(String format) + string AdjustTo4DigitYearsIfConfigured(string format) { - int i = format.LastIndexOf("#/#"); - int j = format.LastIndexOf("?/?"); - return i == -1 ? j : j == -1 ? i : Math.Max(i, j); + if(Use4DigitYearsInAllDateFormats) + { + int ypos2 = format.IndexOf("yy"); + + if(ypos2 < 0) + { + return format; + } + else + { + int ypos3 = format.IndexOf("yyy"); + int ypos4 = format.IndexOf("yyyy"); + + if(ypos4 == ypos2) + { + string part1 = format.Substring(0, ypos2 + 4); + string part2 = format.Substring(ypos2 + 4); + return part1 + AdjustTo4DigitYearsIfConfigured(part2); + } + else if(ypos3 == ypos2) + { + return format; + } + else + { + string part1 = format.Substring(0, ypos2 + 2); + string part2 = format.Substring(ypos2 + 2); + + return part1 + "yy" + AdjustTo4DigitYearsIfConfigured(part2); + } + } + } + return format; } - private FormatBase CreateDateFormat(String pformatStr, double cellValue) + + private FormatBase CreateDateFormat(string pformatStr, double cellValue) { - String formatStr = pformatStr; + string formatStr = AdjustTo4DigitYearsIfConfigured(pformatStr); formatStr = formatStr.Replace("\\-", "-"); formatStr = formatStr.Replace("\\,", ","); formatStr = formatStr.Replace("\\.", "."); @@ -630,7 +666,7 @@ Excel displays the month instead of minutes." mIsMonth = true; ms.Clear(); } - else if (Char.IsLetter(c)) + else if (char.IsLetter(c)) { mIsMonth = true; ms.Clear(); @@ -669,7 +705,7 @@ Excel displays the month instead of minutes." } - private String cleanFormatForNumber(String formatStr) + private string cleanFormatForNumber(string formatStr) { StringBuilder sb = new StringBuilder(formatStr); @@ -765,9 +801,9 @@ private String cleanFormatForNumber(String formatStr) return sb.ToString(); } - private FormatBase CreateNumberFormat(String formatStr, double cellValue) + private FormatBase CreateNumberFormat(string formatStr, double cellValue) { - String format = cleanFormatForNumber(formatStr); + string format = cleanFormatForNumber(formatStr); NumberFormatInfo symbols = decimalSymbols; // Do we need to change the grouping character? @@ -783,8 +819,8 @@ private FormatBase CreateNumberFormat(String formatStr, double cellValue) { symbols = currentCulture.NumberFormat.Clone() as NumberFormatInfo; symbols.NumberGroupSeparator = grouping.ToString(); - String oldPart = agm.Groups[1].Value; - String newPart = oldPart.Replace(grouping, ','); + string oldPart = agm.Groups[1].Value; + string newPart = oldPart.Replace(grouping, ','); format = format.Replace(oldPart, newPart); } } @@ -833,12 +869,12 @@ private FormatBase GetDefaultFormat(double cellValue) * @param cell The cell * @return a Formatted date string */ - private String GetFormattedDateString(ICell cell) + private string GetFormattedDateString(ICell cell) { FormatBase dateFormat = GetFormat(cell); - if (dateFormat is ExcelStyleDateFormatter) { + if (dateFormat is ExcelStyleDateFormatter formatter) { // Hint about the raw excel value - ((ExcelStyleDateFormatter)dateFormat).SetDateToBeFormatted( + formatter.SetDateToBeFormatted( cell.NumericCellValue ); } @@ -857,7 +893,7 @@ private String GetFormattedDateString(ICell cell) * @param cell The cell * @return a Formatted number string */ - private String GetFormattedNumberString(ICell cell) + private string GetFormattedNumberString(ICell cell) { FormatBase numberFormat = GetFormat(cell); @@ -867,7 +903,7 @@ private String GetFormattedNumberString(ICell cell) return d.ToString(currentCulture); } //return numberFormat.Format(d, currentCulture); - String formatted = numberFormat.Format(d); + string formatted = numberFormat.Format(d); if (formatted.StartsWith(".")) formatted = "0" + formatted; if (formatted.StartsWith("-.")) @@ -881,7 +917,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 +925,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 +938,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)) @@ -938,8 +974,8 @@ 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); @@ -955,20 +991,20 @@ public String FormatRawCellContents(double value, int formatIndex, String format } return result; } - /** - * - * Returns the Formatted value of a cell as a String regardless - * of the cell type. If the Excel FormatBase pattern cannot be Parsed then the - * cell value will be Formatted using a default FormatBase. - * - * When passed a null or blank cell, this method will return an empty - * String (""). Formulas in formula type cells will not be evaluated. - * - * - * @param cell The cell - * @return the Formatted cell value as a String - */ - public String FormatCellValue(ICell cell) + + /// + /// Returns the Formatted value of a cell as a String regardless + /// of the cell type. If the Excel FormatBase pattern cannot be Parsed then the + /// cell value will be Formatted using a default FormatBase. + /// + /// When passed a null or blank cell, this method will return an empty + /// string (""). Formulas in formula type cells will not be evaluated. + /// controls how these cells are evaluated. + /// + /// + /// The cell + /// the Formatted cell value as a String + public string FormatCellValue(ICell cell) { return FormatCellValue(cell, null); } @@ -980,9 +1016,9 @@ public String FormatCellValue(ICell cell) * cell value will be Formatted using a default FormatBase. * * When passed a null or blank cell, this method will return an empty - * String (""). Formula cells will be evaluated using the given + * string (""). Formula cells will be evaluated using the given * {@link HSSFFormulaEvaluator} if the evaluator is non-null. If the - * evaluator is null, then the formula String will be returned. The caller + * evaluator is null, then the formula string will be returned. The caller * is responsible for setting the currentRow on the evaluator * * @@ -990,7 +1026,7 @@ public String FormatCellValue(ICell cell) * @param evaluator The HSSFFormulaEvaluator (can be null) * @return a string value of the cell */ - public String FormatCellValue(ICell cell, IFormulaEvaluator evaluator) + public string FormatCellValue(ICell cell, IFormulaEvaluator evaluator) { if (cell == null) @@ -999,13 +1035,31 @@ public String FormatCellValue(ICell cell, IFormulaEvaluator evaluator) } CellType cellType = cell.CellType; - if (evaluator != null && cellType == CellType.Formula) + + if (cellType == CellType.Formula) { if (evaluator == null) { - return cell.CellFormula; + if(UseCachedValuesForFormulaCells) + { + try + { + cellType = cell.GetCachedFormulaResultTypeEnum(); + } + catch(Exception) + { + return cell.CellFormula; + } + } + else + { + return cell.CellFormula; + } + } + else + { + cellType = evaluator.EvaluateFormulaCell(cell); } - cellType = evaluator.EvaluateFormulaCell(cell); } switch (cellType) { @@ -1077,7 +1131,7 @@ public void SetDefaultNumberFormat(FormatBase format) * @param excelformatStr The data FormatBase string * @param FormatBase A FormatBase instance */ - public void AddFormat(String excelformatStr, FormatBase format) + public void AddFormat(string excelformatStr, FormatBase format) { formats[excelformatStr] = format; } @@ -1092,7 +1146,7 @@ public void AddFormat(String excelformatStr, FormatBase format) */ public void Update(IObservable observable, object localeObj) { - if (!(localeObj is CultureInfo)) return; + if (localeObj is not CultureInfo) return; CultureInfo newLocale = (CultureInfo)localeObj; if (newLocale.Equals(currentCulture)) return; @@ -1127,15 +1181,15 @@ public void Update(IObservable observable, object localeObj) */ private class CellFormatResultWrapper : FormatBase { - private CellFormatResult result; - private bool emulateCSV; + private readonly CellFormatResult result; + private readonly bool emulateCSV; internal CellFormatResultWrapper(CellFormatResult result, bool emulateCSV) { this.emulateCSV = emulateCSV; this.result = result; } - protected override StringBuilder Format(Object obj, StringBuilder toAppendTo, int pos) + protected override StringBuilder Format(object obj, StringBuilder toAppendTo, int pos) { if (emulateCSV) { @@ -1152,7 +1206,7 @@ public override StringBuilder Format(object obj, StringBuilder toAppendTo, Cultu throw new NotImplementedException(); } - public override Object ParseObject(String source, int pos) + public override object ParseObject(string source, int pos) { return null; // Not supported } diff --git a/ooxml/XSSF/Extractor/XSSFExcelExtractor.cs b/ooxml/XSSF/Extractor/XSSFExcelExtractor.cs index f0ef47404..2f5bdd1df 100644 --- a/ooxml/XSSF/Extractor/XSSFExcelExtractor.cs +++ b/ooxml/XSSF/Extractor/XSSFExcelExtractor.cs @@ -19,7 +19,6 @@ limitations under the License. using NPOI.OpenXml4Net.OPC; using System.Text; using NPOI.SS.UserModel; -using System.Collections; using System.Globalization; using System.Collections.Generic; @@ -36,7 +35,8 @@ public class XSSFExcelExtractor : POIXMLTextExtractor, NPOI.SS.Extractor.IExcelE XSSFRelation.MACROS_WORKBOOK }; - private XSSFWorkbook workbook; + private readonly XSSFWorkbook workbook; + private readonly DataFormatter dataFormatter; private bool includeSheetNames = true; private bool formulasNotResults = false; private bool includeCellComments = false; @@ -53,6 +53,9 @@ public XSSFExcelExtractor(XSSFWorkbook workbook) { this.workbook = workbook; + dataFormatter = new DataFormatter { + UseCachedValuesForFormulaCells = true + }; } /// /// Should header and footer be included? Default is true @@ -61,11 +64,11 @@ public bool IncludeHeaderFooter { get { - return this.includeHeadersFooters; + return includeHeadersFooters; } set { - this.includeHeadersFooters = value; + includeHeadersFooters = value; } } /// @@ -76,11 +79,11 @@ public bool IncludeSheetNames { get { - return this.includeSheetNames; + return includeSheetNames; } set { - this.includeSheetNames = value; + includeSheetNames = value; } } /// @@ -92,11 +95,11 @@ public bool FormulasNotResults { get { - return this.formulasNotResults; + return formulasNotResults; } set { - this.formulasNotResults = value; + formulasNotResults = value; } } /// @@ -107,11 +110,11 @@ public bool IncludeCellComments { get { - return this.includeCellComments; + return includeCellComments; } set { - this.includeCellComments = value; + includeCellComments = value; } } @@ -173,7 +176,7 @@ public void SetLocale(CultureInfo locale) /** * Retreives the text contents of the file */ - public override String Text + public override string Text { get { @@ -212,7 +215,7 @@ public override String Text } // Rows and cells - foreach (Object rawR in sheet) + foreach (object rawR in sheet) { IRow row = (IRow)rawR; IEnumerator ri = row.GetEnumerator(); @@ -340,10 +343,20 @@ private void HandleNonStringCell(StringBuilder text, ICell cell, DataFormatter f } // No supported styling applies to this cell - XSSFCell xcell = (XSSFCell)cell; - text.Append(xcell.GetRawValue()); + string contents = dataFormatter.FormatCellValue(cell); + + if(contents != null) + { + if(type == CellType.Error) + { + // to match what XSSFEventBasedExcelExtractor does + contents = "ERROR:" + contents; + } + + text.Append(contents); + } } - private String ExtractHeaderFooter(IHeaderFooter hf) + private string ExtractHeaderFooter(IHeaderFooter hf) { return NPOI.HSSF.Extractor.ExcelExtractor.ExtractHeaderFooter(hf); } diff --git a/testcases/ooxml/XSSF/Extractor/TestXSSFExcelExtractor.cs b/testcases/ooxml/XSSF/Extractor/TestXSSFExcelExtractor.cs index aa2c5c07a..f329d2d7c 100644 --- a/testcases/ooxml/XSSF/Extractor/TestXSSFExcelExtractor.cs +++ b/testcases/ooxml/XSSF/Extractor/TestXSSFExcelExtractor.cs @@ -15,7 +15,6 @@ the License. You may obtain a copy of the License at limitations under the License. ==================================================================== */ -using System; using NUnit.Framework; using NPOI.HSSF.Extractor; using TestCases.HSSF; @@ -23,6 +22,7 @@ limitations under the License. using NPOI.XSSF.Extractor; using NPOI.XSSF; using NPOI; +using System; namespace TestCases.XSSF.Extractor { @@ -33,7 +33,7 @@ namespace TestCases.XSSF.Extractor [TestFixture] public class TestXSSFExcelExtractor { - protected XSSFExcelExtractor GetExtractor(String sampleName) + protected XSSFExcelExtractor GetExtractor(string sampleName) { return new XSSFExcelExtractor(XSSFTestDataSamples.OpenSampleWorkbook(sampleName)); } @@ -47,7 +47,7 @@ public void TestGetSimpleText() // a very simple file XSSFExcelExtractor extractor = GetExtractor("sample.xlsx"); - String text = extractor.Text; + string text = extractor.Text; Assert.IsTrue(text.Length > 0); // Check sheet names @@ -57,7 +57,7 @@ public void TestGetSimpleText() // Now without, will have text extractor.SetIncludeSheetNames(false); text = extractor.Text; - String CHUNK1 = + string CHUNK1 = "Lorem\t111\n" + "ipsum\t222\n" + "dolor\t333\n" + @@ -67,7 +67,7 @@ public void TestGetSimpleText() "adipiscing\t777\n" + "elit\t888\n" + "Nunc\t999\n"; - String CHUNK2 = + string CHUNK2 = "The quick brown fox jumps over the lazy dog\n\t" + "hello, xssf hello, xssf\n\t" + "hello, xssf hello, xssf\n\t" + @@ -106,7 +106,7 @@ public void TestGetComplexText() // A fairly complex file XSSFExcelExtractor extractor = GetExtractor("AverageTaxRates.xlsx"); - String text = extractor.Text; + string text = extractor.Text; Assert.IsTrue(text.Length > 0); // Might not have all formatting it should do! @@ -138,7 +138,7 @@ public void TestComparedToOLE2() { POITextExtractor extractor = extractors[i]; - String text = Regex.Replace(extractor.Text,"[\r\t]", ""); + string text = Regex.Replace(extractor.Text,"[\r\t]", ""); Assert.IsTrue(text.StartsWith("First Sheet\nTest spreadsheet\n2nd row2nd row 2nd column\n")); Regex pattern = new Regex(".*13(\\.0+)?\\s+Sheet3.*",RegexOptions.Compiled); Assert.IsTrue(pattern.IsMatch(text)); @@ -154,14 +154,15 @@ public void TestComparedToOLE2() [Test] public void TestHeaderFooter() { - String[] files = new String[] { - "45540_classic_Header.xlsx", "45540_form_Header.xlsx", - "45540_classic_Footer.xlsx", "45540_form_Footer.xlsx", - }; - foreach (String sampleName in files) + string[] files = new string[] { + "45540_classic_Header.xlsx", "45540_form_Header.xlsx", + "45540_classic_Footer.xlsx", "45540_form_Footer.xlsx", + }; + + foreach (string sampleName in files) { XSSFExcelExtractor extractor = GetExtractor(sampleName); - String text = extractor.Text; + string text = extractor.Text; Assert.IsTrue(text.Contains("testdoc"), "Unable to find expected word in text from " + sampleName + "\n" + text); Assert.IsTrue(text.Contains("test phrase"), "Unable to find expected word in text\n" + text); @@ -177,7 +178,7 @@ public void TestComments() { XSSFExcelExtractor extractor = GetExtractor("45544.xlsx"); - String text = extractor.Text; + string text = extractor.Text; // No comments there yet Assert.IsFalse(text.Contains("testdoc"), "Unable to find expected word in text\n" + text); @@ -195,7 +196,7 @@ public void TestInlineStrings() { XSSFExcelExtractor extractor = GetExtractor("InlineStrings.xlsx"); extractor.SetFormulasNotResults(true); - String text = extractor.Text; + string text = extractor.Text; // Numbers Assert.IsTrue(text.Contains("43"), "Unable to find expected word in text\n" + text); @@ -220,7 +221,7 @@ public void TestEmptyCells() { XSSFExcelExtractor extractor = GetExtractor("SimpleNormal.xlsx"); - String text = extractor.Text; + string text = extractor.Text; Assert.IsTrue(text.Length > 0); // This sheet demonstrates the preservation of empty cells, as @@ -257,7 +258,7 @@ public void TestTextBoxes() try { extractor.SetFormulasNotResults(true); - String text = extractor.Text; + string text = extractor.Text; Assert.IsTrue(text.IndexOf("Line 1") > -1); Assert.IsTrue(text.IndexOf("Line 2") > -1); Assert.IsTrue(text.IndexOf("Line 3") > -1); @@ -267,6 +268,32 @@ public void TestTextBoxes() extractor.Close(); } } + + [Test] + public void Test67784() + { + XSSFExcelExtractor extractor = GetExtractor("bug67784.xlsx"); + string text = extractor.Text.Replace("\r", ""); + string[] lines = text.Split('\n'); + Assert.AreEqual("FALSE", lines[2]); + Assert.AreEqual("TRUE", lines[3]); + Assert.AreEqual("ERROR:#DIV/0!", lines[4]); + extractor.Close(); + } + + [Test] + public void Test67784Formulas() + { + XSSFExcelExtractor extractor = GetExtractor("bug67784.xlsx"); + extractor.FormulasNotResults = true; + string text = extractor.Text.Replace("\r", ""); + string[] lines = text.Split('\n'); + Assert.AreEqual("(2 > 5)", lines[2]); + Assert.AreEqual("(2 < 4)", lines[3]); + Assert.AreEqual("10/0", lines[4]); + extractor.Close(); + } + } } diff --git a/testcases/test-data/spreadsheet/bug67784.xlsx b/testcases/test-data/spreadsheet/bug67784.xlsx new file mode 100644 index 000000000..df5c63bc5 Binary files /dev/null and b/testcases/test-data/spreadsheet/bug67784.xlsx differ