From fee7d2eacf7edf4f4836e94326e35e97c19a94c6 Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Thu, 14 Mar 2024 09:43:02 +0800 Subject: [PATCH 01/12] poi: As requested on StackOverflow, expose on CellStyle QuotePrefix/123Prefix --- main/HSSF/UserModel/HSSFCellStyle.cs | 18 +++++++++++++++++- main/SS/UserModel/CellStyle.cs | 8 ++++++++ ooxml/XSSF/UserModel/XSSFCellStyle.cs | 18 ++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/main/HSSF/UserModel/HSSFCellStyle.cs b/main/HSSF/UserModel/HSSFCellStyle.cs index 863d9e9fb..8d87aa5e3 100644 --- a/main/HSSF/UserModel/HSSFCellStyle.cs +++ b/main/HSSF/UserModel/HSSFCellStyle.cs @@ -200,7 +200,6 @@ public bool IsHidden } } - /// /// Get whether the cell's using this style are to be locked /// @@ -215,6 +214,23 @@ public bool IsLocked } } + /// + /// Turn on or off "Quote Prefix" or "123 Prefix" for the style, + /// which is used to tell Excel that the thing which looks like + /// a number or a formula shouldn't be treated as on. + /// + /// Is "Quote Prefix" or "123 Prefix" enabled for the cell? + public bool IsQuotePrefixed + { + get + { + return _format._123Prefix; + } + set + { + _format._123Prefix = value; + } + } /// /// Get the type of horizontal alignment for the cell /// diff --git a/main/SS/UserModel/CellStyle.cs b/main/SS/UserModel/CellStyle.cs index 0350b24ec..a6d902b31 100644 --- a/main/SS/UserModel/CellStyle.cs +++ b/main/SS/UserModel/CellStyle.cs @@ -73,6 +73,14 @@ public interface ICellStyle bool IsLocked { get; set; } + /// + /// Turn on or off "Quote Prefix" or "123 Prefix" for the style, + /// which is used to tell Excel that the thing which looks like + /// a number or a formula shouldn't be treated as on. + /// Turning this on is somewhat (but not completely, see {@link IgnoredErrorType}) + /// like prefixing the cell value with a ' in Excel + /// + bool IsQuotePrefixed { get; set; } /** * get the type of horizontal alignment for the cell diff --git a/ooxml/XSSF/UserModel/XSSFCellStyle.cs b/ooxml/XSSF/UserModel/XSSFCellStyle.cs index e393f89ae..947be17d5 100644 --- a/ooxml/XSSF/UserModel/XSSFCellStyle.cs +++ b/ooxml/XSSF/UserModel/XSSFCellStyle.cs @@ -822,6 +822,24 @@ public bool IsLocked } } + /// + /// Turn on or off "Quote Prefix" or "123 Prefix" for the style, + /// which is used to tell Excel that the thing which looks like + /// a number or a formula shouldn't be treated as on. + /// + /// Is "Quote Prefix" or "123 Prefix" enabled for the cell? + public bool IsQuotePrefixed + { + get + { + return _cellXf.quotePrefix; + } + set + { + _cellXf.quotePrefix = value; + } + } + /// /// Get the color to use for the right border /// From a326226f04cf82e8c28e4def4c7900d24b51ee08 Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Thu, 14 Mar 2024 10:20:34 +0800 Subject: [PATCH 02/12] poi: Add setFormattingRanges() to interface ConditionalFormatting, closes #42 --- main/HSSF/Record/CFHeaderBase.cs | 2 +- .../UserModel/HSSFConditionalFormatting.cs | 6 ++ main/SS/UserModel/ConditionalFormatting.cs | 6 +- .../UserModel/XSSFConditionalFormatting.cs | 25 ++++++ .../TestHSSFConditionalFormatting.cs | 2 +- .../BaseTestConditionalFormatting.cs | 82 +++++++++++++++++++ 6 files changed, 120 insertions(+), 3 deletions(-) diff --git a/main/HSSF/Record/CFHeaderBase.cs b/main/HSSF/Record/CFHeaderBase.cs index 5746110d1..ee823a06f 100644 --- a/main/HSSF/Record/CFHeaderBase.cs +++ b/main/HSSF/Record/CFHeaderBase.cs @@ -135,7 +135,7 @@ public CellRangeAddress[] CellRanges { if (value == null) { - throw new ArgumentException("cellRanges must not be null"); + throw new ArgumentNullException("cellRanges must not be null"); } CellRangeAddressList cral = new CellRangeAddressList(); CellRangeAddress enclosingRange = null; diff --git a/main/HSSF/UserModel/HSSFConditionalFormatting.cs b/main/HSSF/UserModel/HSSFConditionalFormatting.cs index 3ebb19e48..449badd05 100644 --- a/main/HSSF/UserModel/HSSFConditionalFormatting.cs +++ b/main/HSSF/UserModel/HSSFConditionalFormatting.cs @@ -97,6 +97,12 @@ public CellRangeAddress[] GetFormattingRanges() { return cfAggregate.Header.CellRanges; } + + public void SetFormattingRanges(CellRangeAddress[] ranges) + { + cfAggregate.Header.CellRanges = ranges; + } + /// /// Replaces an existing Conditional Formatting rule at position idx. /// Excel allows to Create up to 3 Conditional Formatting rules. diff --git a/main/SS/UserModel/ConditionalFormatting.cs b/main/SS/UserModel/ConditionalFormatting.cs index 214ebce78..dcd48bbcc 100644 --- a/main/SS/UserModel/ConditionalFormatting.cs +++ b/main/SS/UserModel/ConditionalFormatting.cs @@ -82,7 +82,11 @@ public interface IConditionalFormatting * @return array of CellRangeAddresss. Never null */ CellRangeAddress[] GetFormattingRanges(); - + /** + * Sets the cell ranges the rule conditional formatting must be applied to. + * @param ranges non-null array of CellRangeAddresss + */ + void SetFormattingRanges(CellRangeAddress[] ranges); /** * Replaces an existing Conditional Formatting rule at position idx. * Excel allows to create up to 3 Conditional Formatting rules. diff --git a/ooxml/XSSF/UserModel/XSSFConditionalFormatting.cs b/ooxml/XSSF/UserModel/XSSFConditionalFormatting.cs index c8050d9b4..00f535ee3 100644 --- a/ooxml/XSSF/UserModel/XSSFConditionalFormatting.cs +++ b/ooxml/XSSF/UserModel/XSSFConditionalFormatting.cs @@ -22,6 +22,8 @@ using System.Collections.Generic; using NPOI.SS.Util; using System; +using System.Text; + namespace NPOI.XSSF.UserModel { @@ -69,6 +71,29 @@ public CellRangeAddress[] GetFormattingRanges() return lst.ToArray(); } + public void SetFormattingRanges(CellRangeAddress[] ranges) + { + if (ranges == null) + { + throw new ArgumentNullException("cellRanges must not be null"); + } + StringBuilder sb = new StringBuilder(); + bool first = true; + foreach (CellRangeAddress range in ranges) + { + if (!first) + { + sb.Append(" "); + } + else + { + first = false; + } + sb.Append(range.FormatAsString()); + } + _cf.sqref = sb.ToString(); + //_cf.setSqref(Collections.singletonList(sb.ToString())); + } /** * Replaces an existing Conditional Formatting rule at position idx. * Excel allows to create up to 3 Conditional Formatting rules. diff --git a/testcases/main/HSSF/UserModel/TestHSSFConditionalFormatting.cs b/testcases/main/HSSF/UserModel/TestHSSFConditionalFormatting.cs index 973e8df5f..bfe921c83 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFConditionalFormatting.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFConditionalFormatting.cs @@ -126,7 +126,7 @@ private void WriteTemp53691(IWorkbook wb, String suffix) } [Test] - public void test52122() + public void Test52122() { IWorkbook workbook = new HSSFWorkbook(); ISheet sheet = workbook.CreateSheet("Conditional Formatting Test"); diff --git a/testcases/main/SS/UserModel/BaseTestConditionalFormatting.cs b/testcases/main/SS/UserModel/BaseTestConditionalFormatting.cs index a5320e585..2d2e6a884 100644 --- a/testcases/main/SS/UserModel/BaseTestConditionalFormatting.cs +++ b/testcases/main/SS/UserModel/BaseTestConditionalFormatting.cs @@ -1322,6 +1322,88 @@ public void TestBug55380() wb.Close(); } + + [Test] + public void TestSetCellRangeAddresswithSingleRange() + { + + IWorkbook wb = _testDataProvider.CreateWorkbook(); + ISheet sheet = wb.CreateSheet("S1"); + ISheetConditionalFormatting cf = sheet.SheetConditionalFormatting; + Assert.AreEqual(0, cf.NumConditionalFormattings); + IConditionalFormattingRule rule1 = cf.CreateConditionalFormattingRule("$A$1>0"); + cf.AddConditionalFormatting(new CellRangeAddress[] { + CellRangeAddress.ValueOf("A1:A5") + }, rule1); + + Assert.AreEqual(1, cf.NumConditionalFormattings); + IConditionalFormatting ReadCf = cf.GetConditionalFormattingAt(0); + CellRangeAddress[] formattingRanges = ReadCf.GetFormattingRanges(); + Assert.AreEqual(1, formattingRanges.Length); + CellRangeAddress formattingRange = formattingRanges[0]; + Assert.AreEqual("A1:A5", formattingRange.FormatAsString()); + + ReadCf.SetFormattingRanges(new CellRangeAddress[] { + CellRangeAddress.ValueOf("A1:A6") + }); + + ReadCf = cf.GetConditionalFormattingAt(0); + formattingRanges = ReadCf.GetFormattingRanges(); + Assert.AreEqual(1, formattingRanges.Length); + formattingRange = formattingRanges[0]; + Assert.AreEqual("A1:A6", formattingRange.FormatAsString()); + } + + [Test] + public void TestSetCellRangeAddressWithMultipleRanges() + { + + IWorkbook wb = _testDataProvider.CreateWorkbook(); + ISheet sheet = wb.CreateSheet("S1"); + ISheetConditionalFormatting cf = sheet.SheetConditionalFormatting; + Assert.AreEqual(0, cf.NumConditionalFormattings); + IConditionalFormattingRule rule1 = cf.CreateConditionalFormattingRule("$A$1>0"); + cf.AddConditionalFormatting(new CellRangeAddress[] { + CellRangeAddress.ValueOf("A1:A5") + }, rule1); + + Assert.AreEqual(1, cf.NumConditionalFormattings); + IConditionalFormatting ReadCf = cf.GetConditionalFormattingAt(0); + CellRangeAddress[] formattingRanges = ReadCf.GetFormattingRanges(); + Assert.AreEqual(1, formattingRanges.Length); + CellRangeAddress formattingRange = formattingRanges[0]; + Assert.AreEqual("A1:A5", formattingRange.FormatAsString()); + + ReadCf.SetFormattingRanges(new CellRangeAddress[] { + CellRangeAddress.ValueOf("A1:A6"), + CellRangeAddress.ValueOf("B1:B6") + }); + + ReadCf = cf.GetConditionalFormattingAt(0); + formattingRanges = ReadCf.GetFormattingRanges(); + Assert.AreEqual(2, formattingRanges.Length); + formattingRange = formattingRanges[0]; + Assert.AreEqual("A1:A6", formattingRange.FormatAsString()); + formattingRange = formattingRanges[1]; + Assert.AreEqual("B1:B6", formattingRange.FormatAsString()); + } + + [Test] + public void TestSetCellRangeAddressWithNullRanges() + { + IWorkbook wb = _testDataProvider.CreateWorkbook(); + ISheet sheet = wb.CreateSheet("S1"); + ISheetConditionalFormatting cf = sheet.SheetConditionalFormatting; + Assert.AreEqual(0, cf.NumConditionalFormattings); + IConditionalFormattingRule rule1 = cf.CreateConditionalFormattingRule("$A$1>0"); + cf.AddConditionalFormatting(new CellRangeAddress[] { + CellRangeAddress.ValueOf("A1:A5") + }, rule1); + + Assert.AreEqual(1, cf.NumConditionalFormattings); + IConditionalFormatting ReadCf = cf.GetConditionalFormattingAt(0); + Assert.Throws(() => ReadCf.SetFormattingRanges(null)); + } } } \ No newline at end of file From 49a868b81e12cadb476819f5c43a4aafe12edadb Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Thu, 14 Mar 2024 10:32:44 +0800 Subject: [PATCH 03/12] poi: Pull up one more test into BaseTestConditionalFormattin --- .../TestHSSFConditionalFormatting.cs | 76 +--------------- .../BaseTestConditionalFormatting.cs | 91 +++++++++++++++++-- .../TestXSSFConditionalFormatting.cs | 3 +- 3 files changed, 83 insertions(+), 87 deletions(-) diff --git a/testcases/main/HSSF/UserModel/TestHSSFConditionalFormatting.cs b/testcases/main/HSSF/UserModel/TestHSSFConditionalFormatting.cs index bfe921c83..b2f2c3a2a 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFConditionalFormatting.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFConditionalFormatting.cs @@ -57,7 +57,7 @@ protected override void AssertColour(String hexExpected, IColor actual) [Test] public void TestReadOffice2007() { - testReadOffice2007("NewStyleConditionalFormattings.xls"); + TestReadOffice2007("NewStyleConditionalFormattings.xls"); } [Test] @@ -124,79 +124,5 @@ private void WriteTemp53691(IWorkbook wb, String suffix) Assert.IsNotNull(wbBack); wbBack.Close(); } - - [Test] - public void Test52122() - { - IWorkbook workbook = new HSSFWorkbook(); - ISheet sheet = workbook.CreateSheet("Conditional Formatting Test"); - sheet.SetColumnWidth(0, 256 * 10); - sheet.SetColumnWidth(1, 256 * 10); - sheet.SetColumnWidth(2, 256 * 10); - // Create some content. - // row 0 - IRow row = sheet.CreateRow(0); - ICell cell0 = row.CreateCell(0); - cell0.SetCellType(CellType.Numeric); - cell0.SetCellValue(100); - ICell cell1 = row.CreateCell(1); - cell1.SetCellType(CellType.Numeric); - cell1.SetCellValue(120); - ICell cell2 = row.CreateCell(2); - cell2.SetCellType(CellType.Numeric); - cell2.SetCellValue(130); - // row 1 - row = sheet.CreateRow(1); - cell0 = row.CreateCell(0); - cell0.SetCellType(CellType.Numeric); - cell0.SetCellValue(200); - cell1 = row.CreateCell(1); - cell1.SetCellType(CellType.Numeric); - cell1.SetCellValue(220); - cell2 = row.CreateCell(2); - cell2.SetCellType(CellType.Numeric); - cell2.SetCellValue(230); - // row 2 - row = sheet.CreateRow(2); - cell0 = row.CreateCell(0); - cell0.SetCellType(CellType.Numeric); - cell0.SetCellValue(300); - cell1 = row.CreateCell(1); - cell1.SetCellType(CellType.Numeric); - cell1.SetCellValue(320); - cell2 = row.CreateCell(2); - cell2.SetCellType(CellType.Numeric); - cell2.SetCellValue(330); - // Create conditional formatting, CELL1 should be yellow if CELL0 is not blank. - ISheetConditionalFormatting formatting = sheet.SheetConditionalFormatting; - IConditionalFormattingRule rule = formatting.CreateConditionalFormattingRule("$A$1>75"); - IPatternFormatting pattern = rule.CreatePatternFormatting(); - pattern.FillBackgroundColor = IndexedColors.Blue.Index; - pattern.FillPattern = FillPattern.SolidForeground; - CellRangeAddress[] range = { CellRangeAddress.ValueOf("B2:C2") }; - CellRangeAddress[] range2 = { CellRangeAddress.ValueOf("B1:C1") }; - formatting.AddConditionalFormatting(range, rule); - formatting.AddConditionalFormatting(range2, rule); - // Write file. - /*FileOutputStream fos = new FileOutputStream("c:\\temp\\52122_conditional-sheet.xls"); - try { - workbook.write(fos); - } finally { - fos.Close(); - }*/ - IWorkbook wbBack = HSSFTestDataSamples.WriteOutAndReadBack((HSSFWorkbook)workbook); - ISheet sheetBack = wbBack.GetSheetAt(0); - ISheetConditionalFormatting sheetConditionalFormattingBack = sheetBack.SheetConditionalFormatting; - Assert.IsNotNull(sheetConditionalFormattingBack); - IConditionalFormatting formattingBack = sheetConditionalFormattingBack.GetConditionalFormattingAt(0); - Assert.IsNotNull(formattingBack); - IConditionalFormattingRule ruleBack = formattingBack.GetRule(0); - Assert.IsNotNull(ruleBack); - IPatternFormatting patternFormattingBack1 = ruleBack.PatternFormatting; - Assert.IsNotNull(patternFormattingBack1); - Assert.AreEqual(IndexedColors.Blue.Index, patternFormattingBack1.FillBackgroundColor); - Assert.AreEqual(FillPattern.SolidForeground, patternFormattingBack1.FillPattern); - } - } } \ No newline at end of file diff --git a/testcases/main/SS/UserModel/BaseTestConditionalFormatting.cs b/testcases/main/SS/UserModel/BaseTestConditionalFormatting.cs index 2d2e6a884..cae28031c 100644 --- a/testcases/main/SS/UserModel/BaseTestConditionalFormatting.cs +++ b/testcases/main/SS/UserModel/BaseTestConditionalFormatting.cs @@ -432,7 +432,7 @@ public void TestClone() { if (e.Message.IndexOf("needs to define a clone method") > 0) { - Assert.Fail("Indentified bug 45682"); + Assert.Fail("Identified bug 45682"); } throw; } @@ -588,12 +588,10 @@ protected void TestRead(string sampleFile) wb.Close(); } - public void testReadOffice2007(String filename) + public void TestReadOffice2007(String filename) { IWorkbook wb = _testDataProvider.OpenSampleWorkbook(filename); ISheet s = wb.GetSheet("CF"); - IConditionalFormatting cf = null; - IConditionalFormattingRule cr = null; // Sanity check data Assert.AreEqual("Values", s.GetRow(0).GetCell(0).ToString()); @@ -610,10 +608,10 @@ public void testReadOffice2007(String filename) int fCF = 0, fCF12 = 0, fCFEX = 0; for (int i = 0; i < sheetCF.NumConditionalFormattings; i++) { - cf = sheetCF.GetConditionalFormattingAt(i); - if (cf is HSSFConditionalFormatting) + IConditionalFormatting cf0 = sheetCF.GetConditionalFormattingAt(i); + if (cf0 is HSSFConditionalFormatting) { - String str = cf.ToString(); + String str = cf0.ToString(); if (str.Contains("[CF]")) fCF++; if (str.Contains("[CF12]")) @@ -623,7 +621,7 @@ public void testReadOffice2007(String filename) } else { - ConditionType type = cf.GetRule(cf.NumberOfRules - 1).ConditionType; + ConditionType type = cf0.GetRule(cf0.NumberOfRules - 1).ConditionType; if (type == ConditionType.CellValueIs || type == ConditionType.Formula) { @@ -644,12 +642,12 @@ public void testReadOffice2007(String filename) // Check the rules / values in detail // Highlight Positive values - Column C - cf = sheetCF.GetConditionalFormattingAt(0); + IConditionalFormatting cf = sheetCF.GetConditionalFormattingAt(0); Assert.AreEqual(1, cf.GetFormattingRanges().Length); Assert.AreEqual("C2:C17", cf.GetFormattingRanges()[0].FormatAsString()); Assert.AreEqual(1, cf.NumberOfRules); - cr = cf.GetRule(0); + IConditionalFormattingRule cr = cf.GetRule(0); Assert.AreEqual(ConditionType.CellValueIs, cr.ConditionType); Assert.AreEqual(ComparisonOperator.GreaterThan, cr.ComparisonOperation); Assert.AreEqual("0", cr.Formula1); @@ -1404,6 +1402,79 @@ public void TestSetCellRangeAddressWithNullRanges() IConditionalFormatting ReadCf = cf.GetConditionalFormattingAt(0); Assert.Throws(() => ReadCf.SetFormattingRanges(null)); } + + [Test] + public void Test52122() + { + IWorkbook workbook = _testDataProvider.CreateWorkbook(); + ISheet sheet = workbook.CreateSheet("Conditional Formatting Test"); + sheet.SetColumnWidth(0, 256 * 10); + sheet.SetColumnWidth(1, 256 * 10); + sheet.SetColumnWidth(2, 256 * 10); + // Create some content. + // row 0 + IRow row = sheet.CreateRow(0); + ICell cell0 = row.CreateCell(0); + cell0.SetCellType(CellType.Numeric); + cell0.SetCellValue(100); + ICell cell1 = row.CreateCell(1); + cell1.SetCellType(CellType.Numeric); + cell1.SetCellValue(120); + ICell cell2 = row.CreateCell(2); + cell2.SetCellType(CellType.Numeric); + cell2.SetCellValue(130); + // row 1 + row = sheet.CreateRow(1); + cell0 = row.CreateCell(0); + cell0.SetCellType(CellType.Numeric); + cell0.SetCellValue(200); + cell1 = row.CreateCell(1); + cell1.SetCellType(CellType.Numeric); + cell1.SetCellValue(220); + cell2 = row.CreateCell(2); + cell2.SetCellType(CellType.Numeric); + cell2.SetCellValue(230); + // row 2 + row = sheet.CreateRow(2); + cell0 = row.CreateCell(0); + cell0.SetCellType(CellType.Numeric); + cell0.SetCellValue(300); + cell1 = row.CreateCell(1); + cell1.SetCellType(CellType.Numeric); + cell1.SetCellValue(320); + cell2 = row.CreateCell(2); + cell2.SetCellType(CellType.Numeric); + cell2.SetCellValue(330); + // Create conditional formatting, CELL1 should be yellow if CELL0 is not blank. + ISheetConditionalFormatting formatting = sheet.SheetConditionalFormatting; + IConditionalFormattingRule rule = formatting.CreateConditionalFormattingRule("$A$1>75"); + IPatternFormatting pattern = rule.CreatePatternFormatting(); + pattern.FillBackgroundColor = IndexedColors.Blue.Index; + pattern.FillPattern = FillPattern.SolidForeground; + CellRangeAddress[] range = { CellRangeAddress.ValueOf("B2:C2") }; + CellRangeAddress[] range2 = { CellRangeAddress.ValueOf("B1:C1") }; + formatting.AddConditionalFormatting(range, rule); + formatting.AddConditionalFormatting(range2, rule); + // Write file. + /*FileOutputStream fos = new FileOutputStream("c:\\temp\\52122_conditional-sheet.xls"); + try { + workbook.write(fos); + } finally { + fos.Close(); + }*/ + IWorkbook wbBack = _testDataProvider.WriteOutAndReadBack(workbook); + ISheet sheetBack = wbBack.GetSheetAt(0); + ISheetConditionalFormatting sheetConditionalFormattingBack = sheetBack.SheetConditionalFormatting; + Assert.IsNotNull(sheetConditionalFormattingBack); + IConditionalFormatting formattingBack = sheetConditionalFormattingBack.GetConditionalFormattingAt(0); + Assert.IsNotNull(formattingBack); + IConditionalFormattingRule ruleBack = formattingBack.GetRule(0); + Assert.IsNotNull(ruleBack); + IPatternFormatting patternFormattingBack1 = ruleBack.PatternFormatting; + Assert.IsNotNull(patternFormattingBack1); + Assert.AreEqual(IndexedColors.Blue.Index, patternFormattingBack1.FillBackgroundColor); + Assert.AreEqual(FillPattern.SolidForeground, patternFormattingBack1.FillPattern); + } } } \ No newline at end of file diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFConditionalFormatting.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFConditionalFormatting.cs index 9f2042dda..4e770d56d 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFConditionalFormatting.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFConditionalFormatting.cs @@ -58,8 +58,7 @@ public void TestRead() public void TestReadOffice2007() { // TODO Bring the XSSF support up to the same level - testReadOffice2007("NewStyleConditionalFormattings.xlsx"); + TestReadOffice2007("NewStyleConditionalFormattings.xlsx"); } } - } \ No newline at end of file From 99b66cf82926f5865b71b0e476783a0ac0bc1e17 Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Thu, 14 Mar 2024 10:41:20 +0800 Subject: [PATCH 04/12] poi: #60331 - Remove deprecated classes (POI 3.16) - remove StylesTable.getNumberFormatAt(int) --- ooxml/XSSF/Model/StylesTable.cs | 12 +---- ooxml/XSSF/UserModel/XSSFDataFormat.cs | 21 +++++---- testcases/ooxml/XSSF/Model/TestStylesTable.cs | 44 +++++++++---------- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/ooxml/XSSF/Model/StylesTable.cs b/ooxml/XSSF/Model/StylesTable.cs index e2c612b6b..526264ddd 100644 --- a/ooxml/XSSF/Model/StylesTable.cs +++ b/ooxml/XSSF/Model/StylesTable.cs @@ -268,17 +268,7 @@ internal void ReadFrom(XmlDocument xmldoc) // =========================================================== // Start of style related Getters and Setters // =========================================================== - /** - * Get number format string given its id - * - * @param idx number format id - * @return number format code - */ - [Obsolete("deprecated POI 3.14-beta2. Use {@link #getNumberFormatAt(short)} instead.")] - public String GetNumberFormatAt(int idx) - { - return GetNumberFormatAt((short)idx); - } + /** * Get number format string given its id * diff --git a/ooxml/XSSF/UserModel/XSSFDataFormat.cs b/ooxml/XSSF/UserModel/XSSFDataFormat.cs index 4abfd76a6..184bcf90f 100644 --- a/ooxml/XSSF/UserModel/XSSFDataFormat.cs +++ b/ooxml/XSSF/UserModel/XSSFDataFormat.cs @@ -60,15 +60,6 @@ public short GetFormat(String format) * @return string represented at index of format or null if there is not a format at that index */ public String GetFormat(short index) - { - return GetFormat(index & 0xffff); - } - /** - * get the format string that matches the given format index - * @param index of a format - * @return string represented at index of format or null if there is not a format at that index - */ - public String GetFormat(int index) { // Indices used for built-in formats may be overridden with // custom formats, such as locale-specific currency. @@ -81,6 +72,18 @@ public String GetFormat(int index) return fmt; } + /** + * get the format string that matches the given format index + * @param index of a format + * @return string represented at index of format or null if there is not a format at that index + * + * @deprecated POI 3.16 beta 1 - use {@link #getFormat(short)} instead + */ + [Obsolete("use {@link #getFormat(short)} instead, schedule to remove at version 3.18")] + public String GetFormat(int index) + { + return GetFormat((short)index); + } /** * Add a number format with a specific ID into the number format style table. * If a format with the same ID already exists, overwrite the format code diff --git a/testcases/ooxml/XSSF/Model/TestStylesTable.cs b/testcases/ooxml/XSSF/Model/TestStylesTable.cs index fc3fa5219..172ea6173 100644 --- a/testcases/ooxml/XSSF/Model/TestStylesTable.cs +++ b/testcases/ooxml/XSSF/Model/TestStylesTable.cs @@ -210,35 +210,35 @@ public void ExceedNumberFormatLimit() } } - private static void assertNotContainsKey(SortedDictionary map, K key) + private static void AssertNotContainsKey(SortedDictionary map, K key) { Assert.IsFalse(map.ContainsKey(key)); } - private static void assertNotContainsValue(SortedDictionary map, V value) + private static void AssertNotContainsValue(SortedDictionary map, V value) { Assert.IsFalse(map.ContainsValue(value)); } [Test] - public void removeNumberFormat() + public void RemoveNumberFormat() { - XSSFWorkbook wb = new XSSFWorkbook(); + XSSFWorkbook wb1 = new XSSFWorkbook(); try { String fmt = customDataFormat; - short fmtIdx = (short)wb.GetStylesSource().PutNumberFormat(fmt); + short fmtIdx = (short)wb1.GetStylesSource().PutNumberFormat(fmt); - ICell cell = wb.CreateSheet("test").CreateRow(0).CreateCell(0); + ICell cell = wb1.CreateSheet("test").CreateRow(0).CreateCell(0); cell.SetCellValue(5.25); - ICellStyle style = wb.CreateCellStyle(); + ICellStyle style = wb1.CreateCellStyle(); style.DataFormat = fmtIdx; cell.CellStyle = style; Assert.AreEqual(fmt, cell.CellStyle.GetDataFormatString()); - Assert.AreEqual(fmt, wb.GetStylesSource().GetNumberFormatAt(fmtIdx)); + Assert.AreEqual(fmt, wb1.GetStylesSource().GetNumberFormatAt(fmtIdx)); // remove the number format from the workbook - wb.GetStylesSource().RemoveNumberFormat(fmt); + wb1.GetStylesSource().RemoveNumberFormat(fmt); // number format in CellStyles should be restored to default number format short defaultFmtIdx = 0; @@ -247,19 +247,19 @@ public void removeNumberFormat() Assert.AreEqual(defaultFmt, style.GetDataFormatString()); // The custom number format should be entirely removed from the workbook - SortedDictionary numberFormats = wb.GetStylesSource().GetNumberFormats() as SortedDictionary; - assertNotContainsKey(numberFormats, fmtIdx); - assertNotContainsValue(numberFormats, fmt); + SortedDictionary numberFormats = wb1.GetStylesSource().GetNumberFormats() as SortedDictionary; + AssertNotContainsKey(numberFormats, fmtIdx); + AssertNotContainsValue(numberFormats, fmt); // The default style shouldn't be added back to the styles source because it's built-in - Assert.AreEqual(0, wb.GetStylesSource().NumDataFormats); + Assert.AreEqual(0, wb1.GetStylesSource().NumDataFormats); cell = null; style = null; numberFormats = null; - wb = XSSFTestDataSamples.WriteOutCloseAndReadBack(wb); + XSSFWorkbook wb2 = XSSFTestDataSamples.WriteOutCloseAndReadBack(wb1); - cell = wb.GetSheet("test").GetRow(0).GetCell(0); + cell = wb2.GetSheet("test").GetRow(0).GetCell(0); style = cell.CellStyle; // number format in CellStyles should be restored to default number format @@ -267,25 +267,25 @@ public void removeNumberFormat() Assert.AreEqual(defaultFmt, style.GetDataFormatString()); // The custom number format should be entirely removed from the workbook - numberFormats = wb.GetStylesSource().GetNumberFormats() as SortedDictionary; - assertNotContainsKey(numberFormats, fmtIdx); - assertNotContainsValue(numberFormats, fmt); + numberFormats = wb2.GetStylesSource().GetNumberFormats() as SortedDictionary; + AssertNotContainsKey(numberFormats, fmtIdx); + AssertNotContainsValue(numberFormats, fmt); // The default style shouldn't be added back to the styles source because it's built-in - Assert.AreEqual(0, wb.GetStylesSource().NumDataFormats); + Assert.AreEqual(0, wb2.GetStylesSource().NumDataFormats); + wb2.Close(); } finally { - wb.Close(); + wb1.Close(); } } [Test] - public void maxNumberOfDataFormats() + public void MaxNumberOfDataFormats() { XSSFWorkbook wb = new XSSFWorkbook(); - try { StylesTable styles = wb.GetStylesSource(); From 29af5664478d4fe820a436d30df0bd0a334b1923 Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Thu, 14 Mar 2024 10:46:24 +0800 Subject: [PATCH 05/12] poi: bug 60343, return null if index is out of bounds in StylesTable's getStyleAt(idx) --- ooxml/XSSF/Model/StylesTable.cs | 10 +++++- .../ooxml/XSSF/UserModel/TestXSSFWorkbook.cs | 31 +++---------------- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/ooxml/XSSF/Model/StylesTable.cs b/ooxml/XSSF/Model/StylesTable.cs index 526264ddd..96bbe3aa6 100644 --- a/ooxml/XSSF/Model/StylesTable.cs +++ b/ooxml/XSSF/Model/StylesTable.cs @@ -448,12 +448,20 @@ public int PutFont(XSSFFont font) return PutFont(font, false); } + /** + * + * @param idx style index + * @return XSSFCellStyle or null if idx is out of bounds for xfs array + */ public XSSFCellStyle GetStyleAt(int idx) { int styleXfId = 0; - if (xfs.Count == 0) //in case there is no default style + if (idx < 0 || idx >= xfs.Count) + { + //BUG-60343 return null; + } // 0 is the empty default if (xfs[idx].xfId > 0) diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs index 7bb613037..0e29fd3da 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs @@ -454,12 +454,7 @@ public void Bug49702() // Should have one style Assert.AreEqual(1, wb1.NumCellStyles); wb1.GetCellStyleAt((short)0); - try - { - wb1.GetCellStyleAt((short)1); - Assert.Fail("Shouldn't be able to get style at 1 that doesn't exist"); - } - catch (ArgumentOutOfRangeException) { } + Assert.IsNull(wb1.GetCellStyleAt((short)1),"Shouldn't be able to get style at 0 that doesn't exist"); // Add another one ICellStyle cs = wb1.CreateCellStyle(); @@ -469,24 +464,14 @@ public void Bug49702() Assert.AreEqual(2, wb1.NumCellStyles); wb1.GetCellStyleAt((short)0); wb1.GetCellStyleAt((short)1); - try - { - wb1.GetCellStyleAt((short)2); - Assert.Fail("Shouldn't be able to get style at 2 that doesn't exist"); - } - catch (ArgumentOutOfRangeException) { } + Assert.IsNull(wb1.GetCellStyleAt((short)2), "Shouldn't be able to get style at 2 that doesn't exist"); // Save and reload XSSFWorkbook nwb = (XSSFWorkbook)XSSFTestDataSamples.WriteOutAndReadBack(wb1); Assert.AreEqual(2, nwb.NumCellStyles); nwb.GetCellStyleAt((short)0); nwb.GetCellStyleAt((short)1); - try - { - nwb.GetCellStyleAt((short)2); - Assert.Fail("Shouldn't be able to Get style at 2 that doesn't exist"); - } - catch (ArgumentOutOfRangeException) { } + Assert.IsNull(nwb.GetCellStyleAt((short)2), "Shouldn't be able to Get style at 2 that doesn't exist"); // Now with an existing file XSSFWorkbook wb2 = XSSFTestDataSamples.OpenSampleWorkbook("sample.xlsx"); @@ -494,16 +479,10 @@ public void Bug49702() wb2.GetCellStyleAt((short)0); wb2.GetCellStyleAt((short)1); wb2.GetCellStyleAt((short)2); - try - { - wb2.GetCellStyleAt((short)3); - Assert.Fail("Shouldn't be able to Get style at 3 that doesn't exist"); - } - catch (ArgumentOutOfRangeException) { } + Assert.IsNull(nwb.GetCellStyleAt((short)3), "Shouldn't be able to Get style at 3 that doesn't exist"); wb2.Close(); - wb1.Close(); - nwb.Close(); + wb1.Close(); nwb.Close(); } [Test] public void RecalcId() From 37af9b1bebdb84fd3201886b1c9b3558b49bf486 Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Thu, 14 Mar 2024 10:51:02 +0800 Subject: [PATCH 06/12] poi: bug 60416: Reduce Hyperlink memory consumption in SXSSF; patch from Dmitry Katsubo --- ooxml/XSSF/Streaming/SXSSFCell.cs | 10 +++++----- ooxml/XSSF/UserModel/XSSFHyperlink.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ooxml/XSSF/Streaming/SXSSFCell.cs b/ooxml/XSSF/Streaming/SXSSFCell.cs index bd501c51d..98ed7787a 100644 --- a/ooxml/XSSF/Streaming/SXSSFCell.cs +++ b/ooxml/XSSF/Streaming/SXSSFCell.cs @@ -284,8 +284,8 @@ public IHyperlink Hyperlink XSSFHyperlink xssfobj = (XSSFHyperlink)value; // Assign to us - CellReference reference = new CellReference(RowIndex, ColumnIndex); - xssfobj.GetCTHyperlink().@ref = reference.FormatAsString(); + CellReference ref1 = new CellReference(RowIndex, ColumnIndex); + xssfobj.SetCellReference(ref1); // Add to the lists ((SXSSFSheet)Sheet)._sh.AddHyperlink(xssfobj); @@ -691,7 +691,7 @@ private void EnsureFormulaType(CellType type) { if (_value.GetType() != CellType.Formula || ((FormulaValue)_value).GetFormulaType() != type) - setFormulaType(type); + SetFormulaType(type); } /* * Sets the cell type to type if it is different @@ -709,7 +709,7 @@ private void EnsureTypeOrFormulaType(CellType type) { if (((FormulaValue)_value).GetFormulaType() == type) return; - setFormulaType(type); // once a formula, always a formula + SetFormulaType(type); // once a formula, always a formula return; } SetType(type); @@ -771,7 +771,7 @@ private void SetType(CellType type) } } - private void setFormulaType(CellType type) + private void SetFormulaType(CellType type) { Value prevValue = _value; switch (type) diff --git a/ooxml/XSSF/UserModel/XSSFHyperlink.cs b/ooxml/XSSF/UserModel/XSSFHyperlink.cs index 63a57ee5c..ca99cec9c 100644 --- a/ooxml/XSSF/UserModel/XSSFHyperlink.cs +++ b/ooxml/XSSF/UserModel/XSSFHyperlink.cs @@ -285,7 +285,7 @@ public void SetCellReference(String ref1) _ctHyperlink.@ref = ref1; } - protected void SetCellReference(CellReference ref1) + public void SetCellReference(CellReference ref1) { SetCellReference(ref1.FormatAsString()); } From 88df52c82ded72361b7901ce56e49bc0992c0f87 Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Thu, 14 Mar 2024 10:51:47 +0800 Subject: [PATCH 07/12] Remove compile warning --- testcases/ooxml/XSSF/Model/TestStylesTable.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testcases/ooxml/XSSF/Model/TestStylesTable.cs b/testcases/ooxml/XSSF/Model/TestStylesTable.cs index 172ea6173..5aad286a1 100644 --- a/testcases/ooxml/XSSF/Model/TestStylesTable.cs +++ b/testcases/ooxml/XSSF/Model/TestStylesTable.cs @@ -200,7 +200,7 @@ public void ExceedNumberFormatLimit() } else { - throw e; + throw; } } } @@ -315,7 +315,7 @@ public void MaxNumberOfDataFormats() } else { - throw e; + throw; } } } @@ -349,7 +349,7 @@ public void addDataFormatsBeyondUpperLimit() } else { - throw e; + throw; } } } @@ -383,7 +383,7 @@ public void decreaseUpperLimitBelowCurrentNumDataFormats() } else { - throw e; + throw; } } } From 94f3abc2b8cdf86ea9556169c0051091f536550c Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Thu, 14 Mar 2024 11:08:42 +0800 Subject: [PATCH 08/12] poi: Bug 59200: Check for actual Excel limits on data validation title/text --- main/HSSF/Record/DVRecord.cs | 79 +++++++++++++++++-- main/HSSF/UserModel/HSSFDataValidation.cs | 20 ++++- ooxml/XSSF/UserModel/XSSFDataValidation.cs | 22 +++++- .../SS/UserModel/BaseTestBugzillaIssues.cs | 70 +++++++++++++++- 4 files changed, 179 insertions(+), 12 deletions(-) diff --git a/main/HSSF/Record/DVRecord.cs b/main/HSSF/Record/DVRecord.cs index bda26c9c8..4bd088b8f 100644 --- a/main/HSSF/Record/DVRecord.cs +++ b/main/HSSF/Record/DVRecord.cs @@ -45,13 +45,13 @@ public class DVRecord : StandardRecord, ICloneable public const short sid = 0x01BE; /** Option flags */ private int _option_flags; - /** Title of the prompt box */ + /** Title of the prompt box, cannot be longer than 32 chars */ private UnicodeString _promptTitle; - /** Title of the error box */ + /** Title of the error box, cannot be longer than 32 chars */ private UnicodeString _errorTitle; - /** Text of the prompt box */ + /** Text of the prompt box, cannot be longer than 255 chars */ private UnicodeString _promptText; - /** Text of the error box */ + /** Text of the error box, cannot be longer than 255 chars */ private UnicodeString _errorText; /** Not used - Excel seems to always write 0x3FE0 */ private short _not_used_1 = 0x3FE0; @@ -93,6 +93,24 @@ public DVRecord(int validationType, int operator1, int errorStyle, bool emptyCel Ptg[] formula1, Ptg[] formula2, CellRangeAddressList regions) { + // check length-limits + if (promptTitle != null && promptTitle.Length > 32) + { + throw new ArgumentOutOfRangeException("Prompt-title cannot be longer than 32 characters, but had: " + promptTitle); + } + if (promptText != null && promptText.Length > 255) + { + throw new ArgumentOutOfRangeException("Prompt-text cannot be longer than 255 characters, but had: " + promptText); + } + + if (errorTitle != null && errorTitle.Length > 32) + { + throw new ArgumentOutOfRangeException("Error-title cannot be longer than 32 characters, but had: " + errorTitle); + } + if (errorText != null && errorText.Length > 255) + { + throw new ArgumentOutOfRangeException("Error-text cannot be longer than 255 characters, but had: " + errorText); + } int flags = 0; flags = opt_data_type.SetValue(flags, validationType); @@ -361,12 +379,59 @@ public int OptionFlags public override String ToString() { - /* @todo DVRecord string representation */ - StringBuilder buffer = new StringBuilder(); + StringBuilder sb = new StringBuilder(); + sb.Append("[DV]\n"); + sb.Append(" options=").Append(HexDump.ToHex(_option_flags)); + sb.Append(" title-prompt=").Append(FormatTextTitle(_promptTitle)); + sb.Append(" title-error=").Append(FormatTextTitle(_errorTitle)); + sb.Append(" text-prompt=").Append(FormatTextTitle(_promptText)); + sb.Append(" text-error=").Append(FormatTextTitle(_errorText)); + sb.Append("\n"); + AppendFormula(sb, "Formula 1:", _formula1); + AppendFormula(sb, "Formula 2:", _formula2); + sb.Append("Regions: "); + int nRegions = _regions.CountRanges(); + for (int i = 0; i < nRegions; i++) + { + if (i > 0) + { + sb.Append(", "); + } + CellRangeAddress addr = _regions.GetCellRangeAddress(i); + sb.Append('(').Append(addr.FirstRow).Append(',').Append(addr.LastRow); + sb.Append(',').Append(addr.FirstColumn).Append(',').Append(addr.LastColumn).Append(')'); + } + sb.Append("\n"); + sb.Append("[/DV]"); + return sb.ToString(); + } - return buffer.ToString(); + private static String FormatTextTitle(UnicodeString us) + { + String str = us.String; + if (str.Length == 1 && str[0] == '\0') + { + return "'\\0'"; + } + return str; } + private static void AppendFormula(StringBuilder sb, String label, Formula f) + { + sb.Append(label); + + if (f == null) + { + sb.Append("\n"); + return; + } + Ptg[] ptgs = f.Tokens; + sb.Append('\n'); + foreach (Ptg ptg in ptgs) + { + sb.Append('\t').Append(ptg.ToString()).Append('\n'); + } + } public override void Serialize(ILittleEndianOutput out1) { diff --git a/main/HSSF/UserModel/HSSFDataValidation.cs b/main/HSSF/UserModel/HSSFDataValidation.cs index d2d383d9e..aeacc8c89 100644 --- a/main/HSSF/UserModel/HSSFDataValidation.cs +++ b/main/HSSF/UserModel/HSSFDataValidation.cs @@ -47,7 +47,9 @@ public class HSSFDataValidation : IDataValidation /** * Constructor which Initializes the cell range on which this object will be * applied - * @param constraint + * + * @param regions A list of regions where the constraint is validated. + * @param constraint The constraints to apply for this validation. */ public HSSFDataValidation(CellRangeAddressList regions, IDataValidationConstraint constraint) { @@ -153,6 +155,14 @@ public bool ShowErrorBox */ public void CreatePromptBox(String title, String text) { + if (title != null && title.Length > 32) + { + throw new ArgumentOutOfRangeException("Prompt-title cannot be longer than 32 characters, but had: " + title); + } + if (text != null && text.Length > 255) + { + throw new ArgumentOutOfRangeException("Prompt-text cannot be longer than 255 characters, but had: " + text); + } _prompt_title = title; _prompt_text = text; this.ShowPromptBox = (/*setter*/true); @@ -185,6 +195,14 @@ public String PromptBoxText */ public void CreateErrorBox(String title, String text) { + if (title != null && title.Length > 32) + { + throw new ArgumentOutOfRangeException("Error-title cannot be longer than 32 characters, but had: " + title); + } + if (text != null && text.Length > 255) + { + throw new ArgumentOutOfRangeException("Error-text cannot be longer than 255 characters, but had: " + text); + } _error_title = title; _error_text = text; this.ShowErrorBox = (/*setter*/true); diff --git a/ooxml/XSSF/UserModel/XSSFDataValidation.cs b/ooxml/XSSF/UserModel/XSSFDataValidation.cs index f27d7a5e2..8738e66e2 100644 --- a/ooxml/XSSF/UserModel/XSSFDataValidation.cs +++ b/ooxml/XSSF/UserModel/XSSFDataValidation.cs @@ -102,6 +102,15 @@ internal CT_DataValidation GetCTDataValidation() */ public void CreateErrorBox(String title, String text) { + // the spec does not specify a length-limit, however Excel reports files as "corrupt" if they exceed 255 bytes for these texts... + if (title != null && title.Length > 255) + { + throw new ArgumentOutOfRangeException("Error-title cannot be longer than 32 characters, but had: " + title); + } + if (text != null && text.Length > 255) + { + throw new ArgumentOutOfRangeException("Error-text cannot be longer than 255 characters, but had: " + text); + } ctDdataValidation.errorTitle = (title); ctDdataValidation.error = (text); } @@ -111,6 +120,15 @@ public void CreateErrorBox(String title, String text) */ public void CreatePromptBox(String title, String text) { + // the spec does not specify a length-limit, however Excel reports files as "corrupt" if they exceed 255 bytes for these texts... + if (title != null && title.Length > 255) + { + throw new ArgumentOutOfRangeException("Prompt-title cannot be longer than 32 characters, but had: " + title); + } + if (text != null && text.Length > 255) + { + throw new ArgumentOutOfRangeException("Prompt-text cannot be longer than 255 characters, but had: " + text); + } ctDdataValidation.promptTitle = (title); ctDdataValidation.prompt = (text); } @@ -253,15 +271,13 @@ public String PrettyPrint() private static XSSFDataValidationConstraint GetConstraint(CT_DataValidation ctDataValidation) { - XSSFDataValidationConstraint constraint = null; String formula1 = ctDataValidation.formula1; String formula2 = ctDataValidation.formula2; ST_DataValidationOperator operator1 = ctDataValidation.@operator; ST_DataValidationType type = ctDataValidation.type; int validationType = XSSFDataValidation.validationTypeReverseMappings[type]; int operatorType = XSSFDataValidation.operatorTypeReverseMappings[operator1]; - constraint = new XSSFDataValidationConstraint(validationType, operatorType, formula1, formula2); - return constraint; + return new XSSFDataValidationConstraint(validationType, operatorType, formula1, formula2); } } diff --git a/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs b/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs index b67028fb1..bcce4a14e 100644 --- a/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs +++ b/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs @@ -39,6 +39,9 @@ public abstract class BaseTestBugzillaIssues private ITestDataProvider _testDataProvider; private static int dpi = 96; + private static String TEST_32 = "Some text with 32 characters to "; + private static String TEST_255 = "Some very long text that is exactly 255 characters, which are allowed here, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla....."; + private static String TEST_256 = "Some very long text that is longer than the 255 characters allowed in HSSF here, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla1"; protected BaseTestBugzillaIssues(ITestDataProvider TestDataProvider) { @@ -1783,5 +1786,70 @@ public virtual void bug60197_NamedRangesReferToCorrectSheetWhenSheetOrderIsChang } } } + + [Test] + public void test59200() + { + IWorkbook wb = _testDataProvider.CreateWorkbook(); + ISheet sheet = wb.CreateSheet(); + + IDataValidation dataValidation; + CellRangeAddressList headerCell = new CellRangeAddressList(0, 1, 0, 1); + IDataValidationConstraint constraint = sheet.GetDataValidationHelper().CreateCustomConstraint("A1<>\"\""); + + dataValidation = sheet.GetDataValidationHelper().CreateValidation(constraint, headerCell); + + // HSSF has 32/255 limits as part of the Spec, XSSF has no limit in the spec, but Excel applies a 255 length limit! + // more than 255 fail for all + checkFailures(dataValidation, TEST_256, TEST_32, true); + checkFailures(dataValidation, TEST_32, TEST_256, true); + // more than 32 title fail for HSSFWorkbook + checkFailures(dataValidation, TEST_255, TEST_32, wb is HSSFWorkbook); + // 32 length title and 255 length text wrok for both + checkFailures(dataValidation, TEST_32, TEST_255, false); + + dataValidation.ShowErrorBox = false; + sheet.AddValidationData(dataValidation); + + // write out and read back in to trigger some more validation + IWorkbook wbBack = _testDataProvider.WriteOutAndReadBack(wb); + + ISheet sheetBack = wbBack.GetSheetAt(0); + List dataValidations = sheetBack.GetDataValidations(); + Assert.AreEqual(1, dataValidations.Count); + + /*String ext = (wb instanceof HSSFWorkbook) ? ".xls" : ".xlsx"; + OutputStream str = new FileOutputStream("C:\\temp\\59200" + ext); + try { + wb.write(str); + } finally { + str.close(); + }*/ + + wb.Close(); + } + + private void checkFailures(IDataValidation dataValidation, String title, String text, bool shouldFail) + { + try + { + dataValidation.CreatePromptBox(title, text); + Assert.IsFalse(shouldFail, "Should fail in a length-check, had " + title.Length + " and " + text.Length); + } + catch (ArgumentOutOfRangeException) + { + Assert.IsTrue(shouldFail, "Should not fail in a length-check, had " + title.Length + " and " + text.Length); + // expected here + } + try + { + dataValidation.CreateErrorBox(title, text); + Assert.IsFalse(shouldFail, "Should fail in a length-check, had " + title.Length + " and " + text.Length); + } + catch (ArgumentOutOfRangeException) + { + Assert.IsTrue(shouldFail, "Should not fail in a length-check, had " + title.Length + " and " + text.Length); + } + } } -} +} \ No newline at end of file From 945c487b3f81ed59c1e37851407443346256bd6c Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Thu, 14 Mar 2024 11:10:01 +0800 Subject: [PATCH 09/12] poi: Include actual entries when we do not find a Document-entry --- main/HSSF/UserModel/HSSFWorkbook.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/HSSF/UserModel/HSSFWorkbook.cs b/main/HSSF/UserModel/HSSFWorkbook.cs index 38f00fbca..f28abdf56 100644 --- a/main/HSSF/UserModel/HSSFWorkbook.cs +++ b/main/HSSF/UserModel/HSSFWorkbook.cs @@ -267,7 +267,7 @@ private static String GetWorkbookDirEntryName(DirectoryNode directory) } throw new ArgumentException("The supplied POIFSFileSystem does not contain a BIFF8 'Workbook' entry. " - + "Is it really an excel file?"); + + "Is it really an excel file? Had: " + string.Join("\n", directory.EntryNames)); } /// From da2e3fc2765f38a2f46aeae5e268a1159f9c498c Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Wed, 8 May 2024 19:54:36 +0800 Subject: [PATCH 10/12] Revert the removed obsolete public method to avoid breaking changes. Schedule to remove at NPOI 2.8. --- main/POIFS/FileSystem/EntryUtils.cs | 25 +++++++++++++++++++++++++ main/SS/Formula/FormulaShifter.cs | 17 +++++++++++++++++ ooxml/XSSF/Model/StylesTable.cs | 5 +++++ ooxml/XSSF/UserModel/XSSFDataFormat.cs | 2 +- 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/main/POIFS/FileSystem/EntryUtils.cs b/main/POIFS/FileSystem/EntryUtils.cs index 3ba1abc2a..ebbbc137e 100644 --- a/main/POIFS/FileSystem/EntryUtils.cs +++ b/main/POIFS/FileSystem/EntryUtils.cs @@ -83,6 +83,31 @@ public static void CopyNodes(FilteringDirectoryNode filteredSource, CopyNodes((DirectoryEntry)filteredSource, (DirectoryEntry)filteredTarget); } + /** + * Copies nodes from one Directory to the other minus the excepts + * + * @param sourceRoot + * is the source Directory to copy from + * @param targetRoot + * is the target Directory to copy to + * @param excepts + * is a list of Strings specifying what nodes NOT to copy + * @deprecated use {@link FilteringDirectoryNode} instead + */ + [Obsolete("To be removed NPOI 2.8.")] + public static void CopyNodes(DirectoryEntry sourceRoot, + DirectoryEntry targetRoot, List excepts) + { + IEnumerator entries = sourceRoot.Entries; + while(entries.MoveNext()) + { + Entry entry = (Entry)entries.Current; + if(!excepts.Contains(entry.Name)) + { + CopyNodeRecursively(entry, targetRoot); + } + } + } /** * Copies all nodes from one POIFS to the other diff --git a/main/SS/Formula/FormulaShifter.cs b/main/SS/Formula/FormulaShifter.cs index 86d9593d6..930602cfd 100644 --- a/main/SS/Formula/FormulaShifter.cs +++ b/main/SS/Formula/FormulaShifter.cs @@ -111,6 +111,23 @@ private FormulaShifter(int srcSheetIndex, int dstSheetIndex) _mode = ShiftMode.SheetMove; } + [Obsolete("To be removed NPOI 2.8. deprecated As of 3.14 beta 1 (November 2015), replaced by CreateForRowShift(int, String, int, int, int, SpreadsheetVersion)")] + public static FormulaShifter CreateForRowShift( + int externSheetIndex, + string sheetName, + int firstMovedRowIndex, + int lastMovedRowIndex, + int numberOfRowsToMove) + { + return CreateForRowShift( + externSheetIndex, + sheetName, + firstMovedRowIndex, + lastMovedRowIndex, + numberOfRowsToMove, + SpreadsheetVersion.EXCEL97); + } + public static FormulaShifter CreateForRowShift( int externSheetIndex, string sheetName, diff --git a/ooxml/XSSF/Model/StylesTable.cs b/ooxml/XSSF/Model/StylesTable.cs index 96bbe3aa6..2ed8ae2a3 100644 --- a/ooxml/XSSF/Model/StylesTable.cs +++ b/ooxml/XSSF/Model/StylesTable.cs @@ -269,6 +269,11 @@ internal void ReadFrom(XmlDocument xmldoc) // Start of style related Getters and Setters // =========================================================== + [Obsolete("To be removed NPOI 2.8. GetNumberFormatAt(short) instead.")] + public String GetNumberFormatAt(int idx) + { + return GetNumberFormatAt((short) idx); + } /** * Get number format string given its id * diff --git a/ooxml/XSSF/UserModel/XSSFDataFormat.cs b/ooxml/XSSF/UserModel/XSSFDataFormat.cs index 184bcf90f..65cd84b64 100644 --- a/ooxml/XSSF/UserModel/XSSFDataFormat.cs +++ b/ooxml/XSSF/UserModel/XSSFDataFormat.cs @@ -79,7 +79,7 @@ public String GetFormat(short index) * * @deprecated POI 3.16 beta 1 - use {@link #getFormat(short)} instead */ - [Obsolete("use {@link #getFormat(short)} instead, schedule to remove at version 3.18")] + [Obsolete("use GetFormat(short) instead, schedule to remove NPOI 2.8")] public String GetFormat(int index) { return GetFormat((short)index); From affde253810cd86f95057853018c12ffaa95e688 Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Mon, 20 May 2024 19:17:56 +0800 Subject: [PATCH 11/12] Ignore test that failed in CI --- testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs index 8d15d514f..cd16199be 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs @@ -883,6 +883,7 @@ public void SetFirstVisibleTab_57373() * Tests that we can save a workbook with macros and reload it. */ [Test] + [Ignore("TODO FIX CI TESTS")] public void TestSetVBAProject() { FileInfo file; From f5060b73ad13426d2f1b23c926a45148992cf9b1 Mon Sep 17 00:00:00 2001 From: Antony Liu Date: Tue, 21 May 2024 18:18:08 +0800 Subject: [PATCH 12/12] Remove Ignore attribute on TestSetVBAProject --- testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs index cd16199be..8d15d514f 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs @@ -883,7 +883,6 @@ public void SetFirstVisibleTab_57373() * Tests that we can save a workbook with macros and reload it. */ [Test] - [Ignore("TODO FIX CI TESTS")] public void TestSetVBAProject() { FileInfo file;