From eabc4f33dd7b9fcbeff5f45068646107bbc8e817 Mon Sep 17 00:00:00 2001 From: houlongchao Date: Fri, 10 Sep 2021 17:52:33 +0800 Subject: [PATCH 001/159] Add null check --- main/SS/Formula/Functions/Sumifs.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/SS/Formula/Functions/Sumifs.cs b/main/SS/Formula/Functions/Sumifs.cs index 6b1649eed..ff3eb51ed 100644 --- a/main/SS/Formula/Functions/Sumifs.cs +++ b/main/SS/Formula/Functions/Sumifs.cs @@ -140,7 +140,7 @@ internal static double CalcMatchingCells(AreaEval[] ranges, IMatchPredicate[] pr AreaEval aeRange = ranges[i]; IMatchPredicate mp = predicates[i]; - if (!mp.Matches(aeRange.GetRelativeValue(r, c))) + if (mp == null || !mp.Matches(aeRange.GetRelativeValue(r, c))) { matches = false; break; @@ -187,4 +187,4 @@ internal static AreaEval ConvertRangeArg(ValueEval eval) } -} \ No newline at end of file +} From b261afeb6340174bcb3dbd134c5856b1519f8ad8 Mon Sep 17 00:00:00 2001 From: KnyazSh Date: Mon, 29 Nov 2021 21:27:21 +0300 Subject: [PATCH 002/159] Add minimal support Pivot tables, Fix #419, Fix #262 --- OpenXmlFormats/Drawing/SpreadsheetDrawing.cs | 22 + .../PivotTable/CT_PivotCacheDefinition.cs | 1106 +++++++++++------ .../PivotTable/CT_PivotCacheRecords.cs | 164 ++- .../PivotTable/CT_PivotTableDefinition.cs | 749 +++++++---- OpenXmlFormats/Spreadsheet/Sheet.cs | 26 +- OpenXmlFormats/Vml/SpreadsheetDrawing.cs | 23 +- openxml4Net/Util/XmlHelper.cs | 29 +- 7 files changed, 1364 insertions(+), 755 deletions(-) diff --git a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs index d6b6ac89b..b42b0a5f9 100644 --- a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs +++ b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs @@ -1807,6 +1807,20 @@ public CT_Marker to set { toField = value; } } + private Vml.Spreadsheet.CT_AlternateContent alternateContentField = null; + + public Vml.Spreadsheet.CT_AlternateContent alternateContent + { + get + { + return alternateContentField; + } + set + { + this.alternateContentField = value; + } + } + #region Choice - one of CT_Shape, CT_GroupShape, CT_GraphicalObjectFrame, CT_Connector or CT_Picture [XmlElement] @@ -1863,6 +1877,10 @@ public void Write(StreamWriter sw) this.graphicalObjectField.Write(sw, "graphicFrame"); else if (this.pictureField != null) this.picture.Write(sw, "pic"); + if (this.alternateContent != null) + { + this.alternateContent.Write(sw, "AlternateContent"); + } if (this.clientData != null) { this.clientData.Write(sw, "clientData"); @@ -1906,6 +1924,10 @@ internal static CT_TwoCellAnchor Parse(XmlNode node, XmlNamespaceManager namespa { twoCellAnchor.graphicFrame = CT_GraphicalObjectFrame.Parse(childNode, namespaceManager); } + else if (childNode.LocalName == "AlternateContent") + { + twoCellAnchor.alternateContent = Vml.Spreadsheet.CT_AlternateContent.Parse(childNode, namespaceManager); + } else if (childNode.LocalName == "clientData") { twoCellAnchor.clientData = CT_AnchorClientData.Parse(childNode, namespaceManager); diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs index 0798c8948..5ae7c9b19 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs @@ -213,14 +213,14 @@ public void Save(Stream stream) public CT_PivotCacheDefinition() { this.extLstField = new CT_ExtensionList(); - this.mapsField = new CT_MeasureDimensionMaps(); - this.measureGroupsField = new CT_MeasureGroups(); - this.dimensionsField = new CT_Dimensions(); - this.calculatedMembersField = new CT_CalculatedMembers(); - this.calculatedItemsField = new CT_CalculatedItems(); - this.tupleCacheField = new CT_TupleCache(); - this.kpisField = new CT_PCDKPIs(); - this.cacheHierarchiesField = new CT_CacheHierarchies(); + //this.mapsField = new CT_MeasureDimensionMaps(); + //this.measureGroupsField = new CT_MeasureGroups(); + //this.dimensionsField = new CT_Dimensions(); + //this.calculatedMembersField = new CT_CalculatedMembers(); + //this.calculatedItemsField = new CT_CalculatedItems(); + //this.tupleCacheField = new CT_TupleCache(); + //this.kpisField = new CT_PCDKPIs(); + //this.cacheHierarchiesField = new CT_CacheHierarchies(); this.cacheFieldsField = new CT_CacheFields(); this.cacheSourceField = new CT_CacheSource(); this.invalidField = false; @@ -264,7 +264,7 @@ public CT_CacheFields cacheFields } } - [XmlElement(Order = 2)] + [XmlElement(Order = 2, IsNullable = true)] public CT_CacheHierarchies cacheHierarchies { get @@ -277,7 +277,7 @@ public CT_CacheHierarchies cacheHierarchies } } - [XmlElement(Order = 3)] + [XmlElement(Order = 3, IsNullable = true)] public CT_PCDKPIs kpis { get @@ -290,7 +290,7 @@ public CT_PCDKPIs kpis } } - [XmlElement(Order = 4)] + [XmlElement(Order = 4, IsNullable = true)] public CT_TupleCache tupleCache { get @@ -303,7 +303,7 @@ public CT_TupleCache tupleCache } } - [XmlElement(Order = 5)] + [XmlElement(Order = 5, IsNullable = true)] public CT_CalculatedItems calculatedItems { get @@ -316,7 +316,7 @@ public CT_CalculatedItems calculatedItems } } - [XmlElement(Order = 6)] + [XmlElement(Order = 6, IsNullable = true)] public CT_CalculatedMembers calculatedMembers { get @@ -329,7 +329,7 @@ public CT_CalculatedMembers calculatedMembers } } - [XmlElement(Order = 7)] + [XmlElement(Order = 7, IsNullable = true)] public CT_Dimensions dimensions { get @@ -342,7 +342,7 @@ public CT_Dimensions dimensions } } - [XmlElement(Order = 8)] + [XmlElement(Order = 8, IsNullable = true)] public CT_MeasureGroups measureGroups { get @@ -355,7 +355,7 @@ public CT_MeasureGroups measureGroups } } - [XmlElement(Order = 9)] + [XmlElement(Order = 9, IsNullable = true)] public CT_MeasureDimensionMaps maps { get @@ -736,15 +736,25 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.page != null) + + if (this.page == null || this.page.Count == 0) { - foreach (CT_PCDSCPage x in this.page) + sw.Write("/>"); + } + else + { + sw.Write(">"); + + if (this.page != null && this.page.Count > 0) { - x.Write(sw, "page"); + foreach (CT_PCDSCPage x in this.page) + { + x.Write(sw, "page"); + } } + + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List pageField; @@ -827,15 +837,25 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.pageItem != null) + + if (this.pageItem == null || this.pageItem.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_PageItem x in this.pageItem) + sw.Write(">"); + + if (this.pageItem != null && this.pageItem.Count > 0) { - x.Write(sw, "pageItem"); + foreach (CT_PageItem x in this.pageItem) + { + x.Write(sw, "pageItem"); + } } + + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List pageItemField; @@ -911,8 +931,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "name", this.name); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private string nameField; @@ -960,15 +979,25 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.rangeSet != null) + + if (this.rangeSet == null || this.rangeSet.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_RangeSet x in this.rangeSet) + sw.Write(">"); + + if (this.rangeSet != null && this.rangeSet.Count > 0) { - x.Write(sw, "rangeSet"); + foreach (CT_RangeSet x in this.rangeSet) + { + x.Write(sw, "rangeSet"); + } } + + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List rangeSetField; @@ -1062,8 +1091,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "name", this.name); XmlHelper.WriteAttribute(sw, "sheet", this.sheet); XmlHelper.WriteAttribute(sw, "r:id", this.id); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private uint i1Field; @@ -1276,12 +1304,20 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "autoPage", this.autoPage); - sw.Write(">"); - if (this.pages != null) - this.pages.Write(sw, "pages"); - if (this.rangeSets != null) - this.rangeSets.Write(sw, "rangeSets"); - sw.Write(string.Format("", nodeName)); + + if (this.pages == null && this.rangeSets == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.pages != null) + this.pages.Write(sw, "pages"); + if (this.rangeSets != null) + this.rangeSets.Write(sw, "rangeSets"); + sw.Write(string.Format("", nodeName)); + } } private CT_Pages pagesField; @@ -1366,8 +1402,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "name", this.name); XmlHelper.WriteAttribute(sw, "sheet", this.sheet); XmlHelper.WriteAttribute(sw, "r:id", this.id); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private string refField; @@ -1486,18 +1521,24 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "type", this.type.ToString()); XmlHelper.WriteAttribute(sw, "connectionId", this.connectionId); - sw.Write(">"); - if (this.worksheetSource != null) - this.worksheetSource.Write(sw, "worksheetSource"); - if (this.consolidation != null) - this.consolidation.Write(sw, "consolidation"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + if (this.worksheetSource == null && this.consolidation == null && this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.worksheetSource != null) + this.worksheetSource.Write(sw, "worksheetSource"); + if (this.consolidation != null) + this.consolidation.Write(sw, "consolidation"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } - private object itemField; private ST_SourceType typeField; @@ -1628,15 +1669,26 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.cacheField != null) + + if (this.cacheField == null || this.cacheField.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_CacheField x in this.cacheField) + sw.Write(">"); + + if (this.cacheField != null && this.cacheField.Count > 0) { - x.Write(sw, "cacheField"); + + foreach (CT_CacheField x in this.cacheField) + { + x.Write(sw, "cacheField"); + } } + + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List cacheFieldField; @@ -1785,39 +1837,53 @@ public static CT_SharedItems Parse(XmlNode node, XmlNamespaceManager namespaceMa internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "containsSemiMixedTypes", this.containsSemiMixedTypes); - XmlHelper.WriteAttribute(sw, "containsNonDate", this.containsNonDate); - XmlHelper.WriteAttribute(sw, "containsDate", this.containsDate); - XmlHelper.WriteAttribute(sw, "containsString", this.containsString); - XmlHelper.WriteAttribute(sw, "containsBlank", this.containsBlank); - XmlHelper.WriteAttribute(sw, "containsMixedTypes", this.containsMixedTypes); - XmlHelper.WriteAttribute(sw, "containsNumber", this.containsNumber); - XmlHelper.WriteAttribute(sw, "containsInteger", this.containsInteger); + if (!this.containsSemiMixedTypes) + XmlHelper.WriteAttribute(sw, "containsSemiMixedTypes", this.containsSemiMixedTypes); + if (!this.containsNonDate) + XmlHelper.WriteAttribute(sw, "containsNonDate", this.containsNonDate); + XmlHelper.WriteAttribute(sw, "containsDate", this.containsDate, false); + if (!this.containsString) + XmlHelper.WriteAttribute(sw, "containsString", this.containsString); + XmlHelper.WriteAttribute(sw, "containsBlank", this.containsBlank, false); + XmlHelper.WriteAttribute(sw, "containsMixedTypes", this.containsMixedTypes, false); + XmlHelper.WriteAttribute(sw, "containsNumber", this.containsNumber, false); + XmlHelper.WriteAttribute(sw, "containsInteger", this.containsInteger, false); XmlHelper.WriteAttribute(sw, "minValue", this.minValue); XmlHelper.WriteAttribute(sw, "maxValue", this.maxValue); XmlHelper.WriteAttribute(sw, "minDate", this.minDate); XmlHelper.WriteAttribute(sw, "maxDate", this.maxDate); XmlHelper.WriteAttribute(sw, "count", this.count); - XmlHelper.WriteAttribute(sw, "longText", this.longText); - sw.Write(">"); + XmlHelper.WriteAttribute(sw, "longText", this.longText, false); - foreach (object o in this.Items) + if (this.Items == null || this.Items.Count == 0) { - if (o is CT_Number) - ((CT_Number)o).Write(sw, "n"); - else if (o is CT_Boolean) - ((CT_Boolean)o).Write(sw, "b"); - else if (o is CT_DateTime) - ((CT_DateTime)o).Write(sw, "d"); - else if (o is CT_Error) - ((CT_Error)o).Write(sw, "e"); - else if (o is CT_Missing) - ((CT_Missing)o).Write(sw, "m"); - else if (o is CT_String) - ((CT_String)o).Write(sw, "s"); + sw.Write("/>"); } + else + { + sw.Write(">"); + + if (this.Items != null && this.Items.Count > 0) + { + foreach (object o in this.Items) + { + if (o is CT_Number) + ((CT_Number)o).Write(sw, "n"); + else if (o is CT_Boolean) + ((CT_Boolean)o).Write(sw, "b"); + else if (o is CT_DateTime) + ((CT_DateTime)o).Write(sw, "d"); + else if (o is CT_Error) + ((CT_Error)o).Write(sw, "e"); + else if (o is CT_Missing) + ((CT_Missing)o).Write(sw, "m"); + else if (o is CT_String) + ((CT_String)o).Write(sw, "s"); + } + } - sw.Write(string.Format("", nodeName)); + sw.Write(string.Format("", nodeName)); + } } private List itemsField; @@ -2206,31 +2272,44 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "name", this.name); XmlHelper.WriteAttribute(sw, "caption", this.caption); XmlHelper.WriteAttribute(sw, "propertyName", this.propertyName); - XmlHelper.WriteAttribute(sw, "serverField", this.serverField); - XmlHelper.WriteAttribute(sw, "uniqueList", this.uniqueList); - XmlHelper.WriteAttribute(sw, "numFmtId", this.numFmtId); + XmlHelper.WriteAttribute(sw, "serverField", this.serverField, false); + if (!this.uniqueList) + XmlHelper.WriteAttribute(sw, "uniqueList", this.uniqueList); + XmlHelper.WriteAttribute(sw, "numFmtId", this.numFmtId, true); XmlHelper.WriteAttribute(sw, "formula", this.formula); XmlHelper.WriteAttribute(sw, "sqlType", this.sqlType); XmlHelper.WriteAttribute(sw, "hierarchy", this.hierarchy); XmlHelper.WriteAttribute(sw, "level", this.level); - XmlHelper.WriteAttribute(sw, "databaseField", this.databaseField); + if (!this.databaseField) + XmlHelper.WriteAttribute(sw, "databaseField", this.databaseField); XmlHelper.WriteAttribute(sw, "mappingCount", this.mappingCount); - XmlHelper.WriteAttribute(sw, "memberPropertyField", this.memberPropertyField); - sw.Write(">"); - if (this.sharedItems != null) - this.sharedItems.Write(sw, "sharedItems"); - if (this.fieldGroup != null) - this.fieldGroup.Write(sw, "fieldGroup"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - if (this.mpMap != null) + XmlHelper.WriteAttribute(sw, "memberPropertyField", this.memberPropertyField, false); + + if (this.sharedItems == null + && this.fieldGroup == null + && this.extLst == null + && (this.mpMap == null || this.mpMap.Count == 0)) + { + sw.Write("/>"); + } + else { - foreach (CT_X x in this.mpMap) + sw.Write(">"); + if (this.sharedItems != null) + this.sharedItems.Write(sw, "sharedItems"); + if (this.fieldGroup != null) + this.fieldGroup.Write(sw, "fieldGroup"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + if (this.mpMap != null && this.mpMap.Count > 0) { - x.Write(sw, "mpMap"); + foreach (CT_X x in this.mpMap) + { + x.Write(sw, "mpMap"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private CT_SharedItems sharedItemsField; @@ -2273,10 +2352,10 @@ internal void Write(StreamWriter sw, string nodeName) public CT_CacheField() { - this.extLstField = new CT_ExtensionList(); + //this.extLstField = new CT_ExtensionList(); this.mpMapField = new List(); - this.fieldGroupField = new CT_FieldGroup(); - this.sharedItemsField = new CT_SharedItems(); + //this.fieldGroupField = new CT_FieldGroup(); + //this.sharedItemsField = new CT_SharedItems(); this.serverFieldField = false; this.uniqueListField = true; this.sqlTypeField = 0; @@ -2299,7 +2378,7 @@ public CT_SharedItems sharedItems } } - [XmlElement(Order = 1)] + [XmlElement(Order = 1, IsNullable = true)] public CT_FieldGroup fieldGroup { get @@ -2325,7 +2404,7 @@ public List mpMap } } - [XmlElement(Order = 3)] + [XmlElement(Order = 3, IsNullable = true)] public CT_ExtensionList extLst { get @@ -2576,15 +2655,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.cacheHierarchy != null) + + if (this.cacheHierarchy == null || this.cacheHierarchy.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_CacheHierarchy x in this.cacheHierarchy) + sw.Write(">"); + if (this.cacheHierarchy != null && this.cacheHierarchy.Count > 0) { - x.Write(sw, "cacheHierarchy"); + foreach (CT_CacheHierarchy x in this.cacheHierarchy) + { + x.Write(sw, "cacheHierarchy"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List cacheHierarchyField; @@ -2673,20 +2760,28 @@ public static CT_Boolean Parse(XmlNode node, XmlNamespaceManager namespaceManage internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "v", this.v); - XmlHelper.WriteAttribute(sw, "u", this.u); - XmlHelper.WriteAttribute(sw, "f", this.f); - XmlHelper.WriteAttribute(sw, "c", this.c); - XmlHelper.WriteAttribute(sw, "cp", this.cp); - sw.Write(">"); - if (this.x != null) + XmlHelper.WriteAttribute(sw, "v", this.v, true); + XmlHelper.WriteAttribute(sw, "u", this.u, false); + XmlHelper.WriteAttribute(sw, "f", this.f, false); + XmlHelper.WriteAttribute(sw, "c", this.c, false); + XmlHelper.WriteAttribute(sw, "cp", this.cp, false); + + if (this.x == null || this.x.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_X x in this.x) + sw.Write(">"); + if (this.x != null && this.x.Count > 0) { - x.Write(sw, "x"); + foreach (CT_X x in this.x) + { + x.Write(sw, "x"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List xField; @@ -2874,11 +2969,9 @@ public static CT_X Parse(XmlNode node, XmlNamespaceManager namespaceManager) internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "v", this.v); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "v", this.v, true); + sw.Write("/>"); } - } @@ -2917,19 +3010,27 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "v", this.v); - XmlHelper.WriteAttribute(sw, "u", this.u); - XmlHelper.WriteAttribute(sw, "f", this.f); - XmlHelper.WriteAttribute(sw, "c", this.c); - XmlHelper.WriteAttribute(sw, "cp", this.cp); - sw.Write(">"); - if (this.x != null) + XmlHelper.WriteAttribute(sw, "u", this.u, false); + XmlHelper.WriteAttribute(sw, "f", this.f, false); + XmlHelper.WriteAttribute(sw, "c", this.c, false); + XmlHelper.WriteAttribute(sw, "cp", this.cp, false); + + if (this.x == null || this.x.Count == 0) { - foreach (CT_X x in this.x) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.x != null && this.x.Count > 0) { - x.Write(sw, "x"); + foreach (CT_X x in this.x) + { + x.Write(sw, "x"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List xField; @@ -3406,31 +3507,38 @@ public static CT_Error Parse(XmlNode node, XmlNamespaceManager namespaceManager) internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "v", this.v); - XmlHelper.WriteAttribute(sw, "u", this.u); - XmlHelper.WriteAttribute(sw, "f", this.f); - XmlHelper.WriteAttribute(sw, "c", this.c); - XmlHelper.WriteAttribute(sw, "cp", this.cp); - XmlHelper.WriteAttribute(sw, "in", this.@in); + XmlHelper.WriteAttribute(sw, "v", this.v, true); + XmlHelper.WriteAttribute(sw, "u", this.u, false); + XmlHelper.WriteAttribute(sw, "f", this.f, false); + XmlHelper.WriteAttribute(sw, "c", this.c, false); + XmlHelper.WriteAttribute(sw, "cp", this.cp, false); + XmlHelper.WriteAttribute(sw, "in", this.@in, false); XmlHelper.WriteAttribute(sw, "bc", this.bc); XmlHelper.WriteAttribute(sw, "fc", this.fc); - XmlHelper.WriteAttribute(sw, "i", this.i); - XmlHelper.WriteAttribute(sw, "un", this.un); - XmlHelper.WriteAttribute(sw, "st", this.st); - XmlHelper.WriteAttribute(sw, "b", this.b); - sw.Write(">"); - if (this.tpls != null) - this.tpls.Write(sw, "tpls"); - if (this.x != null) + XmlHelper.WriteAttribute(sw, "i", this.i, false); + XmlHelper.WriteAttribute(sw, "un", this.un, false); + XmlHelper.WriteAttribute(sw, "st", this.st, false); + XmlHelper.WriteAttribute(sw, "b", this.b, false); + + if (this.tpls == null && (this.x == null || this.x.Count == 0)) + { + sw.Write("/>"); + } + else { - foreach (CT_X x in this.x) + sw.Write(">"); + if (this.tpls != null) + this.tpls.Write(sw, "tpls"); + if (this.x != null && this.x.Count > 0) { - x.Write(sw, "x"); + foreach (CT_X x in this.x) + { + x.Write(sw, "x"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } - } @@ -3513,15 +3621,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "c", this.c); - sw.Write(">"); - if (this.tpl != null) + + if (this.tpl == null || this.tpl.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_Tuple x in this.tpl) + sw.Write(">"); + if (this.tpl != null && this.tpl.Count > 0) { - x.Write(sw, "tpl"); + foreach (CT_Tuple x in this.tpl) + { + x.Write(sw, "tpl"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } } @@ -3631,8 +3747,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "fld", this.fld); XmlHelper.WriteAttribute(sw, "hier", this.hier); XmlHelper.WriteAttribute(sw, "item", this.item); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } } @@ -3955,33 +4070,41 @@ public static CT_Missing Parse(XmlNode node, XmlNamespaceManager namespaceManage internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "u", this.u); - XmlHelper.WriteAttribute(sw, "f", this.f); - XmlHelper.WriteAttribute(sw, "c", this.c); - XmlHelper.WriteAttribute(sw, "cp", this.cp); - XmlHelper.WriteAttribute(sw, "in", this.@in); + XmlHelper.WriteAttribute(sw, "u", this.u, false); + XmlHelper.WriteAttribute(sw, "f", this.f, false); + XmlHelper.WriteAttribute(sw, "c", this.c, false); + XmlHelper.WriteAttribute(sw, "cp", this.cp, false); + XmlHelper.WriteAttribute(sw, "in", this.@in, false); XmlHelper.WriteAttribute(sw, "bc", this.bc); XmlHelper.WriteAttribute(sw, "fc", this.fc); - XmlHelper.WriteAttribute(sw, "i", this.i); - XmlHelper.WriteAttribute(sw, "un", this.un); - XmlHelper.WriteAttribute(sw, "st", this.st); - XmlHelper.WriteAttribute(sw, "b", this.b); - sw.Write(">"); - if (this.tpls != null) + XmlHelper.WriteAttribute(sw, "i", this.i, false); + XmlHelper.WriteAttribute(sw, "un", this.un, false); + XmlHelper.WriteAttribute(sw, "st", this.st, false); + XmlHelper.WriteAttribute(sw, "b", this.b, false); + + if ((this.tpls == null || this.tpls.Count == 0) && (this.x == null || this.x.Count == 0)) { - foreach (CT_Tuples x in this.tpls) - { - x.Write(sw, "tpls"); - } + sw.Write("/>"); } - if (this.x != null) + else { - foreach (CT_X x in this.x) + sw.Write(">"); + if (this.tpls != null && this.tpls.Count > 0) + { + foreach (CT_Tuples x in this.tpls) + { + x.Write(sw, "tpls"); + } + } + if (this.x != null && this.x.Count > 0) { - x.Write(sw, "x"); + foreach (CT_X x in this.x) + { + x.Write(sw, "x"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } } @@ -4316,46 +4439,48 @@ public static CT_Number Parse(XmlNode node, XmlNamespaceManager namespaceManager return ctObj; } - - internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "v", this.v); - XmlHelper.WriteAttribute(sw, "u", this.u); - XmlHelper.WriteAttribute(sw, "f", this.f); - XmlHelper.WriteAttribute(sw, "c", this.c); - XmlHelper.WriteAttribute(sw, "cp", this.cp); - XmlHelper.WriteAttribute(sw, "in", this.@in); + XmlHelper.WriteAttribute(sw, "u", this.u, false); + XmlHelper.WriteAttribute(sw, "f", this.f, false); + XmlHelper.WriteAttribute(sw, "c", this.c, false); + XmlHelper.WriteAttribute(sw, "cp", this.cp, false); + XmlHelper.WriteAttribute(sw, "in", this.@in, false); XmlHelper.WriteAttribute(sw, "bc", this.bc); XmlHelper.WriteAttribute(sw, "fc", this.fc); - XmlHelper.WriteAttribute(sw, "i", this.i); - XmlHelper.WriteAttribute(sw, "un", this.un); - XmlHelper.WriteAttribute(sw, "st", this.st); - XmlHelper.WriteAttribute(sw, "b", this.b); - sw.Write(">"); - if (this.tpls != null) + XmlHelper.WriteAttribute(sw, "i", this.i, false); + XmlHelper.WriteAttribute(sw, "un", this.un, false); + XmlHelper.WriteAttribute(sw, "st", this.st, false); + XmlHelper.WriteAttribute(sw, "b", this.b, false); + + if ((this.tpls == null || this.tpls.Count == 0) && (this.x == null || this.x.Count == 0)) { - foreach (CT_Tuples x in this.tpls) - { - x.Write(sw, "tpls"); - } + sw.Write("/>"); } - if (this.x != null) + else { - foreach (CT_X x in this.x) + sw.Write(">"); + if (this.tpls != null && this.tpls.Count > 0) { - x.Write(sw, "x"); + foreach (CT_Tuples x in this.tpls) + { + x.Write(sw, "tpls"); + } } + if (this.x != null && this.x.Count > 0) + { + foreach (CT_X x in this.x) + { + x.Write(sw, "x"); + } + } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } - } - - - [XmlType(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main", IsNullable = true)] public partial class CT_String @@ -4688,33 +4813,41 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "v", this.v); - XmlHelper.WriteAttribute(sw, "u", this.u); - XmlHelper.WriteAttribute(sw, "f", this.f); - XmlHelper.WriteAttribute(sw, "c", this.c); - XmlHelper.WriteAttribute(sw, "cp", this.cp); - XmlHelper.WriteAttribute(sw, "in", this.@in); + XmlHelper.WriteAttribute(sw, "u", this.u, false); + XmlHelper.WriteAttribute(sw, "f", this.f, false); + XmlHelper.WriteAttribute(sw, "c", this.c, false); + XmlHelper.WriteAttribute(sw, "cp", this.cp, false); + XmlHelper.WriteAttribute(sw, "in", this.@in, false); XmlHelper.WriteAttribute(sw, "bc", this.bc); XmlHelper.WriteAttribute(sw, "fc", this.fc); - XmlHelper.WriteAttribute(sw, "i", this.i); - XmlHelper.WriteAttribute(sw, "un", this.un); - XmlHelper.WriteAttribute(sw, "st", this.st); - XmlHelper.WriteAttribute(sw, "b", this.b); - sw.Write(">"); - if (this.tpls != null) + XmlHelper.WriteAttribute(sw, "i", this.i, false); + XmlHelper.WriteAttribute(sw, "un", this.un, false); + XmlHelper.WriteAttribute(sw, "st", this.st, false); + XmlHelper.WriteAttribute(sw, "b", this.b, false); + + if ((this.tpls == null || this.tpls.Count == 0) && (this.x == null || this.x.Count == 0)) { - foreach (CT_Tuples x in this.tpls) - { - x.Write(sw, "tpls"); - } + sw.Write("/>"); } - if (this.x != null) + else { - foreach (CT_X x in this.x) + sw.Write(">"); + if (this.tpls != null && this.tpls.Count > 0) + { + foreach (CT_Tuples x in this.tpls) + { + x.Write(sw, "tpls"); + } + } + if (this.x != null && this.x.Count > 0) { - x.Write(sw, "x"); + foreach (CT_X x in this.x) + { + x.Write(sw, "x"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } } @@ -4754,14 +4887,22 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "par", this.par); XmlHelper.WriteAttribute(sw, "base", this.@base); - sw.Write(">"); - if (this.rangePr != null) - this.rangePr.Write(sw, "rangePr"); - if (this.discretePr != null) - this.discretePr.Write(sw, "discretePr"); - if (this.groupItems != null) - this.groupItems.Write(sw, "groupItems"); - sw.Write(string.Format("", nodeName)); + + if (this.rangePr == null && this.discretePr == null && this.groupItems == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.rangePr != null) + this.rangePr.Write(sw, "rangePr"); + if (this.discretePr != null) + this.discretePr.Write(sw, "discretePr"); + if (this.groupItems != null) + this.groupItems.Write(sw, "groupItems"); + sw.Write(string.Format("", nodeName)); + } } private CT_RangePr rangePrField; @@ -4921,8 +5062,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "startDate", this.startDate); XmlHelper.WriteAttribute(sw, "endDate", this.endDate); XmlHelper.WriteAttribute(sw, "groupInterval", this.groupInterval); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private bool autoStartField; @@ -5179,15 +5319,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.x != null) + + if (this.x == null || this.x.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_Index x in this.x) + sw.Write(">"); + if (this.x != null && this.x.Count > 0) { - x.Write(sw, "x"); + foreach (CT_Index x in this.x) + { + x.Write(sw, "x"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List xField; @@ -5292,23 +5440,30 @@ public static CT_GroupItems Parse(XmlNode node, XmlNamespaceManager namespaceMan internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - sw.Write(">"); - foreach (object o in this.Items) + if (this.Items == null || this.Items.Count == 0) { - if (o is CT_Error) - ((CT_Error)o).Write(sw, "e"); - else if (o is CT_Boolean) - ((CT_Boolean)o).Write(sw, "b"); - else if (o is CT_DateTime) - ((CT_DateTime)o).Write(sw, "d"); - else if (o is CT_Number) - ((CT_Number)o).Write(sw, "n"); - else if (o is CT_Missing) - ((CT_Missing)o).Write(sw, "m"); - else if (o is CT_String) - ((CT_String)o).Write(sw, "s"); + sw.Write("/>"); + } + else + { + sw.Write(">"); + foreach (object o in this.Items) + { + if (o is CT_Error) + ((CT_Error)o).Write(sw, "e"); + else if (o is CT_Boolean) + ((CT_Boolean)o).Write(sw, "b"); + else if (o is CT_DateTime) + ((CT_DateTime)o).Write(sw, "d"); + else if (o is CT_Number) + ((CT_Number)o).Write(sw, "n"); + else if (o is CT_Missing) + ((CT_Missing)o).Write(sw, "m"); + else if (o is CT_String) + ((CT_String)o).Write(sw, "s"); + } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List itemsField; @@ -5396,15 +5551,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.fieldUsage != null) + + if (this.fieldUsage == null || this.fieldUsage.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_FieldUsage x in this.fieldUsage) + sw.Write(">"); + if (this.fieldUsage != null && this.fieldUsage.Count > 0) { - x.Write(sw, "fieldUsage"); + foreach (CT_FieldUsage x in this.fieldUsage) + { + x.Write(sw, "fieldUsage"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List fieldUsageField; @@ -5480,9 +5643,8 @@ public static CT_FieldUsage Parse(XmlNode node, XmlNamespaceManager namespaceMan internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "x", this.x); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "x", this.x, true); + sw.Write("/>"); } private int xField; @@ -5530,15 +5692,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.groupLevel != null) + + if (this.groupLevel == null || this.groupLevel.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_GroupLevel x in this.groupLevel) + sw.Write(">"); + if (this.groupLevel != null && this.groupLevel.Count > 0) { - x.Write(sw, "groupLevel"); + foreach (CT_GroupLevel x in this.groupLevel) + { + x.Write(sw, "groupLevel"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List groupLevelField; @@ -5629,12 +5799,20 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "caption", this.caption); XmlHelper.WriteAttribute(sw, "user", this.user); XmlHelper.WriteAttribute(sw, "customRollUp", this.customRollUp); - sw.Write(">"); - if (this.groups != null) - this.groups.Write(sw, "groups"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + + if (this.groups == null && this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.groups != null) + this.groups.Write(sw, "groups"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_Groups groupsField; @@ -5767,15 +5945,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.group != null) + + if (this.group == null || this.group.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_LevelGroup x in this.group) + sw.Write(">"); + if (this.group != null && this.group.Count > 0) { - x.Write(sw, "group"); + foreach (CT_LevelGroup x in this.group) + { + x.Write(sw, "group"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List groupField; @@ -5865,10 +6051,18 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "caption", this.caption); XmlHelper.WriteAttribute(sw, "uniqueParent", this.uniqueParent); XmlHelper.WriteAttribute(sw, "id", this.id); - sw.Write(">"); - if (this.groupMembers != null) - this.groupMembers.Write(sw, "groupMembers"); - sw.Write(string.Format("", nodeName)); + + if (this.groupMembers == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.groupMembers != null) + this.groupMembers.Write(sw, "groupMembers"); + sw.Write(string.Format("", nodeName)); + } } private CT_GroupMembers groupMembersField; @@ -6011,15 +6205,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.groupMember != null) + + if (this.groupMember == null || this.groupMember.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_GroupMember x in this.groupMember) + sw.Write(">"); + if (this.groupMember != null && this.groupMember.Count > 0) { - x.Write(sw, "groupMember"); + foreach (CT_GroupMember x in this.groupMember) + { + x.Write(sw, "groupMember"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List groupMemberField; @@ -6098,8 +6300,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "uniqueName", this.uniqueName); XmlHelper.WriteAttribute(sw, "group", this.group); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private string uniqueNameField; @@ -6205,7 +6406,7 @@ public static CT_CacheHierarchy Parse(XmlNode node, XmlNamespaceManager namespac internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "uniqueName", this.uniqueName); + XmlHelper.WriteAttribute(sw, "uniqueName", this.uniqueName, true); XmlHelper.WriteAttribute(sw, "caption", this.caption); XmlHelper.WriteAttribute(sw, "measure", this.measure); XmlHelper.WriteAttribute(sw, "set", this.set); @@ -6221,20 +6422,28 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "displayFolder", this.displayFolder); XmlHelper.WriteAttribute(sw, "measureGroup", this.measureGroup); XmlHelper.WriteAttribute(sw, "measures", this.measures); - XmlHelper.WriteAttribute(sw, "count", this.count); + XmlHelper.WriteAttribute(sw, "count", this.count, true); XmlHelper.WriteAttribute(sw, "oneField", this.oneField); XmlHelper.WriteAttribute(sw, "memberValueDatatype", this.memberValueDatatype); XmlHelper.WriteAttribute(sw, "unbalanced", this.unbalanced); XmlHelper.WriteAttribute(sw, "unbalancedGroup", this.unbalancedGroup); XmlHelper.WriteAttribute(sw, "hidden", this.hidden); - sw.Write(">"); - if (this.fieldsUsage != null) - this.fieldsUsage.Write(sw, "fieldsUsage"); - if (this.groupLevels != null) - this.groupLevels.Write(sw, "groupLevels"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + + if (this.fieldsUsage == null && this.groupLevels == null && this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.fieldsUsage != null) + this.fieldsUsage.Write(sw, "fieldsUsage"); + if (this.groupLevels != null) + this.groupLevels.Write(sw, "groupLevels"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_FieldsUsage fieldsUsageField; @@ -6297,9 +6506,9 @@ internal void Write(StreamWriter sw, string nodeName) public CT_CacheHierarchy() { - this.extLstField = new CT_ExtensionList(); - this.groupLevelsField = new CT_GroupLevels(); - this.fieldsUsageField = new CT_FieldsUsage(); + //this.extLstField = new CT_ExtensionList(); + //this.groupLevelsField = new CT_GroupLevels(); + //this.fieldsUsageField = new CT_FieldsUsage(); this.measureField = false; this.setField = false; this.iconSetField = 0; @@ -6727,15 +6936,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.kpi != null) + if (this.kpi == null || this.kpi.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_PCDKPI x in this.kpi) + sw.Write(">"); + if (this.kpi != null && this.kpi.Count > 0) { - x.Write(sw, "kpi"); + foreach (CT_PCDKPI x in this.kpi) + { + x.Write(sw, "kpi"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List kpiField; @@ -6831,8 +7047,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "trend", this.trend); XmlHelper.WriteAttribute(sw, "weight", this.weight); XmlHelper.WriteAttribute(sw, "time", this.time); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private string uniqueNameField; @@ -7034,18 +7249,29 @@ public static CT_TupleCache Parse(XmlNode node, XmlNamespaceManager namespaceMan internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - sw.Write(">"); - if (this.entries != null) - this.entries.Write(sw, "entries"); - if (this.sets != null) - this.sets.Write(sw, "sets"); - if (this.queryCache != null) - this.queryCache.Write(sw, "queryCache"); - if (this.serverFormats != null) - this.serverFormats.Write(sw, "serverFormats"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + if (this.entries == null + && this.sets == null + && this.queryCache == null + && this.serverFormats == null + && this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.entries != null) + this.entries.Write(sw, "entries"); + if (this.sets != null) + this.sets.Write(sw, "sets"); + if (this.queryCache != null) + this.queryCache.Write(sw, "queryCache"); + if (this.serverFormats != null) + this.serverFormats.Write(sw, "serverFormats"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_PCDSDTCEntries entriesField; @@ -7174,19 +7400,26 @@ public static CT_PCDSDTCEntries Parse(XmlNode node, XmlNamespaceManager namespac internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - sw.Write(">"); - foreach (object o in this.Items) + if (this.Items == null || this.Items.Count == 0) + { + sw.Write("/>"); + } + else { - if (o is CT_Missing) - ((CT_Missing)o).Write(sw, "m"); - else if (o is CT_Number) - ((CT_Number)o).Write(sw, "n"); - else if (o is CT_Error) - ((CT_Error)o).Write(sw, "e"); - else if (o is CT_String) - ((CT_String)o).Write(sw, "s"); + sw.Write(">"); + foreach (object o in this.Items) + { + if (o is CT_Missing) + ((CT_Missing)o).Write(sw, "m"); + else if (o is CT_Number) + ((CT_Number)o).Write(sw, "n"); + else if (o is CT_Error) + ((CT_Error)o).Write(sw, "e"); + else if (o is CT_String) + ((CT_String)o).Write(sw, "s"); + } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List itemsField; @@ -7272,15 +7505,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.set != null) + + if (this.set == null || this.set.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_Set x in this.set) + sw.Write(">"); + if (this.set != null && this.set.Count > 0) { - x.Write(sw, "set"); + foreach (CT_Set x in this.set) + { + x.Write(sw, "set"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List setField; @@ -7376,17 +7617,25 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "setDefinition", this.setDefinition); XmlHelper.WriteAttribute(sw, "sortType", this.sortType.ToString()); XmlHelper.WriteAttribute(sw, "queryFailed", this.queryFailed); - sw.Write(">"); - if (this.sortByTuple != null) - this.sortByTuple.Write(sw, "sortByTuple"); - if (this.tpls != null) + + if (this.sortByTuple == null && (this.tpls == null || this.tpls.Count == 0)) + { + sw.Write("/>"); + } + else { - foreach (CT_Tuples x in this.tpls) + sw.Write(">"); + if (this.sortByTuple != null) + this.sortByTuple.Write(sw, "sortByTuple"); + if (this.tpls != null && this.tpls.Count > 0) { - x.Write(sw, "tpls"); + foreach (CT_Tuples x in this.tpls) + { + x.Write(sw, "tpls"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List tplsField; @@ -7578,15 +7827,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.query != null) + if (this.query == null || this.query.Count == 0) { - foreach (CT_Query x in this.query) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.query != null && this.query.Count > 0) { - x.Write(sw, "query"); + foreach (CT_Query x in this.query) + { + x.Write(sw, "query"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List queryField; @@ -7667,10 +7923,17 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "mdx", this.mdx); - sw.Write(">"); - if (this.tpls != null) - this.tpls.Write(sw, "tpls"); - sw.Write(string.Format("", nodeName)); + if (this.tpls == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.tpls != null) + this.tpls.Write(sw, "tpls"); + sw.Write(string.Format("", nodeName)); + } } private CT_Tuples tplsField; @@ -7738,15 +8001,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.serverFormat != null) + + if (this.serverFormat == null || this.serverFormat.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_ServerFormat x in this.serverFormat) + sw.Write(">"); + if (this.serverFormat != null && this.serverFormat.Count > 0) { - x.Write(sw, "serverFormat"); + foreach (CT_ServerFormat x in this.serverFormat) + { + x.Write(sw, "serverFormat"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List serverFormatField; @@ -7824,8 +8095,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "culture", this.culture); XmlHelper.WriteAttribute(sw, "format", this.format); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private string cultureField; @@ -7888,15 +8158,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.calculatedItem != null) + if (this.calculatedItem == null || this.calculatedItem.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_CalculatedItem x in this.calculatedItem) + sw.Write(">"); + if (this.calculatedItem != null && this.calculatedItem.Count > 0) { - x.Write(sw, "calculatedItem"); + foreach (CT_CalculatedItem x in this.calculatedItem) + { + x.Write(sw, "calculatedItem"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List calculatedItemField; @@ -7982,12 +8259,19 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "field", this.field); XmlHelper.WriteAttribute(sw, "formula", this.formula); - sw.Write(">"); - if (this.pivotArea != null) - this.pivotArea.Write(sw, "pivotArea"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + if (this.pivotArea == null && this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.pivotArea != null) + this.pivotArea.Write(sw, "pivotArea"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_PivotArea pivotAreaField; @@ -8101,15 +8385,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.calculatedMember != null) + + if (this.calculatedMember == null || this.calculatedMember.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_CalculatedMember x in this.calculatedMember) + sw.Write(">"); + if (this.calculatedMember != null && this.calculatedMember.Count > 0) { - x.Write(sw, "calculatedMember"); + foreach (CT_CalculatedMember x in this.calculatedMember) + { + x.Write(sw, "calculatedMember"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List calculatedMemberField; @@ -8204,10 +8496,18 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "parent", this.parent); XmlHelper.WriteAttribute(sw, "solveOrder", this.solveOrder); XmlHelper.WriteAttribute(sw, "set", this.set); - sw.Write(">"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + + if (this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_ExtensionList extLstField; @@ -8369,15 +8669,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.dimension != null) + if (this.dimension == null || this.dimension.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_PivotDimension x in this.dimension) + sw.Write(">"); + if (this.dimension != null && this.dimension.Count > 0) { - x.Write(sw, "dimension"); + foreach (CT_PivotDimension x in this.dimension) + { + x.Write(sw, "dimension"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List dimensionField; @@ -8460,8 +8767,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "name", this.name); XmlHelper.WriteAttribute(sw, "uniqueName", this.uniqueName); XmlHelper.WriteAttribute(sw, "caption", this.caption); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private bool measureField; @@ -8560,15 +8866,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.measureGroup != null) + if (this.measureGroup == null || this.measureGroup.Count == 0) { - foreach (CT_MeasureGroup x in this.measureGroup) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.measureGroup != null && this.measureGroup.Count > 0) { - x.Write(sw, "measureGroup"); + foreach (CT_MeasureGroup x in this.measureGroup) + { + x.Write(sw, "measureGroup"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List measureGroupField; @@ -8646,8 +8959,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "name", this.name); XmlHelper.WriteAttribute(sw, "caption", this.caption); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private string nameField; @@ -8710,15 +9022,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.map != null) + + if (this.map == null || this.map.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_MeasureDimensionMap x in this.map) + sw.Write(">"); + if (this.map != null && this.map.Count > 0) { - x.Write(sw, "map"); + foreach (CT_MeasureDimensionMap x in this.map) + { + x.Write(sw, "map"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List mapField; diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs index c5bae177b..1efcd6f02 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs @@ -19,45 +19,21 @@ public static CT_PivotCacheRecords Parse(XmlNode node, XmlNamespaceManager names CT_PivotCacheRecords ctObj = new CT_PivotCacheRecords(); if (node.Attributes["count"] != null) ctObj.count = XmlHelper.ReadUInt(node.Attributes["count"]); - ctObj.r = new List(); + ctObj.r = new List(); foreach (XmlNode childNode in node.ChildNodes) { if (childNode.LocalName == "extLst") - ctObj.extLst = CT_ExtensionList.Parse(childNode, namespaceManager); - else if (childNode.LocalName == "n") - { - ctObj.r.Add(CT_Number.Parse(childNode, namespaceManager)); - } - else if (childNode.LocalName == "b") - { - ctObj.r.Add(CT_Boolean.Parse(childNode, namespaceManager)); - } - else if (childNode.LocalName == "d") - { - ctObj.r.Add(CT_DateTime.Parse(childNode, namespaceManager)); - } - else if (childNode.LocalName == "e") { - ctObj.r.Add(CT_Error.Parse(childNode, namespaceManager)); - } - else if (childNode.LocalName == "m") - { - ctObj.r.Add(CT_Missing.Parse(childNode, namespaceManager)); - } - else if (childNode.LocalName == "s") - { - ctObj.r.Add(CT_String.Parse(childNode, namespaceManager)); + ctObj.extLst = CT_ExtensionList.Parse(childNode, namespaceManager); } - else if (childNode.LocalName == "x") + else if (childNode.LocalName == "r") { - ctObj.r.Add(CT_Index.Parse(childNode, namespaceManager)); + ctObj.rField.Add(CT_PivotCacheRecord.Parse(childNode, namespaceManager)); } } return ctObj; } - - internal void Write(StreamWriter sw) { sw.Write(""); @@ -68,24 +44,11 @@ internal void Write(StreamWriter sw) sw.Write(">"); if (this.extLst != null) this.extLst.Write(sw, "extLst"); - foreach (object o in this.r) + foreach (CT_PivotCacheRecord o in this.r) { - if (o is CT_Number) - ((CT_Number)o).Write(sw, "n"); - else if (o is CT_Boolean) - ((CT_Boolean)o).Write(sw, "b"); - else if (o is CT_DateTime) - ((CT_DateTime)o).Write(sw, "d"); - else if (o is CT_Error) - ((CT_Error)o).Write(sw, "e"); - else if (o is CT_Missing) - ((CT_Missing)o).Write(sw, "m"); - else if (o is CT_String) - ((CT_String)o).Write(sw, "s"); - else if (o is CT_Index) - ((CT_Index)o).Write(sw, "x"); + o.Write(sw); } - sw.Write(string.Format("")); + sw.Write(""); } public void Save(Stream stream) @@ -96,7 +59,8 @@ public void Save(Stream stream) this.Write(sw); } } - private List rField; + + private List rField; private CT_ExtensionList extLstField; @@ -104,21 +68,8 @@ public void Save(Stream stream) private bool countFieldSpecified; - public CT_PivotCacheRecords() - { - this.extLstField = new CT_ExtensionList(); - this.rField = new List(); - } - [System.Xml.Serialization.XmlArrayAttribute(Order = 0)] - [System.Xml.Serialization.XmlArrayItemAttribute("b", typeof(CT_Boolean), IsNullable = false)] - [System.Xml.Serialization.XmlArrayItemAttribute("d", typeof(CT_DateTime), IsNullable = false)] - [System.Xml.Serialization.XmlArrayItemAttribute("e", typeof(CT_Error), IsNullable = false)] - [System.Xml.Serialization.XmlArrayItemAttribute("m", typeof(CT_Missing), IsNullable = false)] - [System.Xml.Serialization.XmlArrayItemAttribute("n", typeof(CT_Number), IsNullable = false)] - [System.Xml.Serialization.XmlArrayItemAttribute("s", typeof(CT_String), IsNullable = false)] - [System.Xml.Serialization.XmlArrayItemAttribute("x", typeof(CT_Index), IsNullable = false)] - public List r + public List r { get { @@ -169,5 +120,98 @@ public bool countSpecified } } } - + + [XmlType(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main")] + [XmlRoot(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main", IsNullable = true)] + public partial class CT_PivotCacheRecord + { + private List items; + + public static CT_PivotCacheRecord Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + CT_PivotCacheRecord ctObj = new CT_PivotCacheRecord(); + ctObj.fields = new List(); + + foreach (XmlNode childNode in node.ChildNodes) + { + if (childNode.LocalName == "n") + { + ctObj.fields.Add(CT_Number.Parse(childNode, namespaceManager)); + } + else if (childNode.LocalName == "b") + { + ctObj.fields.Add(CT_Boolean.Parse(childNode, namespaceManager)); + } + else if (childNode.LocalName == "d") + { + ctObj.fields.Add(CT_DateTime.Parse(childNode, namespaceManager)); + } + else if (childNode.LocalName == "e") + { + ctObj.fields.Add(CT_Error.Parse(childNode, namespaceManager)); + } + else if (childNode.LocalName == "m") + { + ctObj.fields.Add(CT_Missing.Parse(childNode, namespaceManager)); + } + else if (childNode.LocalName == "s") + { + ctObj.fields.Add(CT_String.Parse(childNode, namespaceManager)); + } + else if (childNode.LocalName == "x") + { + ctObj.fields.Add(CT_Index.Parse(childNode, namespaceManager)); + } + } + + return ctObj; + } + + [System.Xml.Serialization.XmlArrayItemAttribute("b", typeof(CT_Boolean), IsNullable = false)] + [System.Xml.Serialization.XmlArrayItemAttribute("d", typeof(CT_DateTime), IsNullable = false)] + [System.Xml.Serialization.XmlArrayItemAttribute("e", typeof(CT_Error), IsNullable = false)] + [System.Xml.Serialization.XmlArrayItemAttribute("m", typeof(CT_Missing), IsNullable = false)] + [System.Xml.Serialization.XmlArrayItemAttribute("n", typeof(CT_Number), IsNullable = false)] + [System.Xml.Serialization.XmlArrayItemAttribute("s", typeof(CT_String), IsNullable = false)] + [System.Xml.Serialization.XmlArrayItemAttribute("x", typeof(CT_Index), IsNullable = false)] + public List fields + { + get + { + return this.items; + } + set + { + this.items = value; + } + } + + internal void Write(StreamWriter sw) + { + if (this.fields != null && fields.Count > 0) + { + sw.Write(""); + + foreach (object o in this.fields) + { + if (o is CT_Number) + ((CT_Number)o).Write(sw, "n"); + else if (o is CT_Boolean) + ((CT_Boolean)o).Write(sw, "b"); + else if (o is CT_DateTime) + ((CT_DateTime)o).Write(sw, "d"); + else if (o is CT_Error) + ((CT_Error)o).Write(sw, "e"); + else if (o is CT_Missing) + ((CT_Missing)o).Write(sw, "m"); + else if (o is CT_String) + ((CT_String)o).Write(sw, "s"); + else if (o is CT_Index) + ((CT_Index)o).Write(sw, "x"); + } + + sw.Write(""); + } + } + } } diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs index ea305a444..b25bf9edd 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs @@ -296,7 +296,7 @@ internal void Write(StreamWriter sw) this.colHierarchiesUsage.Write(sw, "colHierarchiesUsage"); if (this.extLst != null) this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("")); + sw.Write(""); } public void Save(Stream stream) { @@ -1892,14 +1892,13 @@ public static CT_Location Parse(XmlNode node, XmlNamespaceManager namespaceManag internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "ref", this.@ref); - XmlHelper.WriteAttribute(sw, "firstHeaderRow", this.firstHeaderRow); - XmlHelper.WriteAttribute(sw, "firstDataRow", this.firstDataRow); - XmlHelper.WriteAttribute(sw, "firstDataCol", this.firstDataCol); + XmlHelper.WriteAttribute(sw, "ref", this.@ref, true); + XmlHelper.WriteAttribute(sw, "firstHeaderRow", this.firstHeaderRow, true); + XmlHelper.WriteAttribute(sw, "firstDataRow", this.firstDataRow, true); + XmlHelper.WriteAttribute(sw, "firstDataCol", this.firstDataCol, true); XmlHelper.WriteAttribute(sw, "rowPageCount", this.rowPageCount); XmlHelper.WriteAttribute(sw, "colPageCount", this.colPageCount); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private string refField; @@ -2030,15 +2029,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.pivotField != null) + if (this.pivotField == null || this.pivotField.Count == 0) { - foreach (CT_PivotField x in this.pivotField) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.pivotField != null && this.pivotField.Count > 0) { - x.Write(sw, "pivotField"); + foreach (CT_PivotField x in this.pivotField) + { + x.Write(sw, "pivotField"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List pivotFieldField; @@ -2243,61 +2249,70 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "name", this.name); - XmlHelper.WriteAttribute(sw, "axis", this.axis.ToString()); - XmlHelper.WriteAttribute(sw, "dataField", this.dataField); + if (this.axis != null) + XmlHelper.WriteAttribute(sw, "axis", this.axis.ToString()); + XmlHelper.WriteAttribute(sw, "dataField", this.dataField, true); XmlHelper.WriteAttribute(sw, "subtotalCaption", this.subtotalCaption); - XmlHelper.WriteAttribute(sw, "showDropDowns", this.showDropDowns); - XmlHelper.WriteAttribute(sw, "hiddenLevel", this.hiddenLevel); - XmlHelper.WriteAttribute(sw, "uniqueMemberProperty", this.uniqueMemberProperty); - XmlHelper.WriteAttribute(sw, "compact", this.compact); - XmlHelper.WriteAttribute(sw, "allDrilled", this.allDrilled); - XmlHelper.WriteAttribute(sw, "numFmtId", this.numFmtId); - XmlHelper.WriteAttribute(sw, "outline", this.outline); + XmlHelper.WriteAttribute(sw, "showDropDowns", this.showDropDowns, false, true); + XmlHelper.WriteAttribute(sw, "hiddenLevel", this.hiddenLevel, false); + XmlHelper.WriteAttribute(sw, "uniqueMemberProperty", this.uniqueMemberProperty, false); + XmlHelper.WriteAttribute(sw, "compact", this.compact, false, true); + XmlHelper.WriteAttribute(sw, "allDrilled", this.allDrilled, false); + XmlHelper.WriteAttribute(sw, "numFmtId", this.numFmtId, false); + XmlHelper.WriteAttribute(sw, "outline", this.outline, false, true); XmlHelper.WriteAttribute(sw, "subtotalTop", this.subtotalTop); - XmlHelper.WriteAttribute(sw, "dragToRow", this.dragToRow); - XmlHelper.WriteAttribute(sw, "dragToCol", this.dragToCol); - XmlHelper.WriteAttribute(sw, "multipleItemSelectionAllowed", this.multipleItemSelectionAllowed); - XmlHelper.WriteAttribute(sw, "dragToPage", this.dragToPage); - XmlHelper.WriteAttribute(sw, "dragToData", this.dragToData); - XmlHelper.WriteAttribute(sw, "dragOff", this.dragOff); + XmlHelper.WriteAttribute(sw, "dragToRow", this.dragToRow, false, true); + XmlHelper.WriteAttribute(sw, "dragToCol", this.dragToCol, false, true); + XmlHelper.WriteAttribute(sw, "multipleItemSelectionAllowed", this.multipleItemSelectionAllowed, false); + XmlHelper.WriteAttribute(sw, "dragToPage", this.dragToPage, false, true); + XmlHelper.WriteAttribute(sw, "dragToData", this.dragToData, false, true); + XmlHelper.WriteAttribute(sw, "dragOff", this.dragOff, false, true); XmlHelper.WriteAttribute(sw, "showAll", this.showAll); - XmlHelper.WriteAttribute(sw, "insertBlankRow", this.insertBlankRow); - XmlHelper.WriteAttribute(sw, "serverField", this.serverField); - XmlHelper.WriteAttribute(sw, "insertPageBreak", this.insertPageBreak); - XmlHelper.WriteAttribute(sw, "autoShow", this.autoShow); - XmlHelper.WriteAttribute(sw, "topAutoShow", this.topAutoShow); - XmlHelper.WriteAttribute(sw, "hideNewItems", this.hideNewItems); - XmlHelper.WriteAttribute(sw, "measureFilter", this.measureFilter); - XmlHelper.WriteAttribute(sw, "includeNewItemsInFilter", this.includeNewItemsInFilter); - XmlHelper.WriteAttribute(sw, "itemPageCount", this.itemPageCount); - XmlHelper.WriteAttribute(sw, "sortType", this.sortType.ToString()); - XmlHelper.WriteAttribute(sw, "dataSourceSort", this.dataSourceSort); - XmlHelper.WriteAttribute(sw, "nonAutoSortDefault", this.nonAutoSortDefault); - XmlHelper.WriteAttribute(sw, "rankBy", this.rankBy); + XmlHelper.WriteAttribute(sw, "insertBlankRow", this.insertBlankRow, false); + XmlHelper.WriteAttribute(sw, "serverField", this.serverField, false); + XmlHelper.WriteAttribute(sw, "insertPageBreak", this.insertPageBreak, false); + XmlHelper.WriteAttribute(sw, "autoShow", this.autoShow, false); + XmlHelper.WriteAttribute(sw, "topAutoShow", this.topAutoShow, false, true); + XmlHelper.WriteAttribute(sw, "hideNewItems", this.hideNewItems, false); + XmlHelper.WriteAttribute(sw, "measureFilter", this.measureFilter, false); + XmlHelper.WriteAttribute(sw, "includeNewItemsInFilter", this.includeNewItemsInFilter, false); + XmlHelper.WriteAttribute(sw, "itemPageCount", this.itemPageCount, false); + if (this.sortType != ST_FieldSortType.manual) + XmlHelper.WriteAttribute(sw, "sortType", this.sortType.ToString()); + XmlHelper.WriteAttribute(sw, "dataSourceSort", this.dataSourceSort, false); + XmlHelper.WriteAttribute(sw, "nonAutoSortDefault", this.nonAutoSortDefault, false); + XmlHelper.WriteAttribute(sw, "rankBy", this.rankBy, false); XmlHelper.WriteAttribute(sw, "defaultSubtotal", this.defaultSubtotal); - XmlHelper.WriteAttribute(sw, "sumSubtotal", this.sumSubtotal); - XmlHelper.WriteAttribute(sw, "countASubtotal", this.countASubtotal); - XmlHelper.WriteAttribute(sw, "avgSubtotal", this.avgSubtotal); - XmlHelper.WriteAttribute(sw, "maxSubtotal", this.maxSubtotal); - XmlHelper.WriteAttribute(sw, "minSubtotal", this.minSubtotal); - XmlHelper.WriteAttribute(sw, "productSubtotal", this.productSubtotal); - XmlHelper.WriteAttribute(sw, "countSubtotal", this.countSubtotal); - XmlHelper.WriteAttribute(sw, "stdDevSubtotal", this.stdDevSubtotal); - XmlHelper.WriteAttribute(sw, "stdDevPSubtotal", this.stdDevPSubtotal); - XmlHelper.WriteAttribute(sw, "varSubtotal", this.varSubtotal); - XmlHelper.WriteAttribute(sw, "varPSubtotal", this.varPSubtotal); - XmlHelper.WriteAttribute(sw, "showPropCell", this.showPropCell); - XmlHelper.WriteAttribute(sw, "showPropTip", this.showPropTip); - XmlHelper.WriteAttribute(sw, "showPropAsCaption", this.showPropAsCaption); - XmlHelper.WriteAttribute(sw, "defaultAttributeDrillState", this.defaultAttributeDrillState); - sw.Write(">"); - if (this.items != null) - this.items.Write(sw, "items"); - if (this.autoSortScope != null) - this.autoSortScope.Write(sw, "autoSortScope"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "sumSubtotal", this.sumSubtotal, false); + XmlHelper.WriteAttribute(sw, "countASubtotal", this.countASubtotal, false); + XmlHelper.WriteAttribute(sw, "avgSubtotal", this.avgSubtotal, false); + XmlHelper.WriteAttribute(sw, "maxSubtotal", this.maxSubtotal, false); + XmlHelper.WriteAttribute(sw, "minSubtotal", this.minSubtotal, false); + XmlHelper.WriteAttribute(sw, "productSubtotal", this.productSubtotal, false); + XmlHelper.WriteAttribute(sw, "countSubtotal", this.countSubtotal, false); + XmlHelper.WriteAttribute(sw, "stdDevSubtotal", this.stdDevSubtotal, false); + XmlHelper.WriteAttribute(sw, "stdDevPSubtotal", this.stdDevPSubtotal, false); + XmlHelper.WriteAttribute(sw, "varSubtotal", this.varSubtotal, false); + XmlHelper.WriteAttribute(sw, "varPSubtotal", this.varPSubtotal, false); + XmlHelper.WriteAttribute(sw, "showPropCell", this.showPropCell, false); + XmlHelper.WriteAttribute(sw, "showPropTip", this.showPropTip, false); + XmlHelper.WriteAttribute(sw, "showPropAsCaption", this.showPropAsCaption, false); + XmlHelper.WriteAttribute(sw, "defaultAttributeDrillState", this.defaultAttributeDrillState, false); + if (this.items == null && this.autoSortScope == null && this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.items != null) + this.items.Write(sw, "items"); + if (this.autoSortScope != null) + this.autoSortScope.Write(sw, "autoSortScope"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_Items itemsField; @@ -2308,7 +2323,7 @@ internal void Write(StreamWriter sw, string nodeName) private string nameField; - private ST_Axis axisField; + private ST_Axis? axisField; private bool axisFieldSpecified; @@ -2412,9 +2427,9 @@ internal void Write(StreamWriter sw, string nodeName) public CT_PivotField() { - this.extLstField = new CT_ExtensionList(); - this.autoSortScopeField = new CT_AutoSortScope(); - this.itemsField = new CT_Items(); + //this.extLstField = new CT_ExtensionList(); + //this.autoSortScopeField = new CT_AutoSortScope(); + //this.itemsField = new CT_Items(); this.dataFieldField = false; this.showDropDownsField = true; this.hiddenLevelField = false; @@ -2511,7 +2526,7 @@ public string name } [System.Xml.Serialization.XmlAttributeAttribute()] - public ST_Axis axis + public ST_Axis? axis { get { @@ -3250,15 +3265,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.item != null) + if (this.item == null || this.item.Count == 0) { - foreach (CT_Item x in this.item) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.item != null && this.item.Count > 0) { - x.Write(sw, "item"); + foreach (CT_Item x in this.item) + { + x.Write(sw, "item"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List itemField; @@ -3369,19 +3391,19 @@ public static CT_Item Parse(XmlNode node, XmlNamespaceManager namespaceManager) internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "n", this.n); - XmlHelper.WriteAttribute(sw, "t", this.t.ToString()); - XmlHelper.WriteAttribute(sw, "h", this.h); - XmlHelper.WriteAttribute(sw, "s", this.s); - XmlHelper.WriteAttribute(sw, "sd", this.sd); - XmlHelper.WriteAttribute(sw, "f", this.f); - XmlHelper.WriteAttribute(sw, "m", this.m); - XmlHelper.WriteAttribute(sw, "c", this.c); - XmlHelper.WriteAttribute(sw, "x", this.x); - XmlHelper.WriteAttribute(sw, "d", this.d); - XmlHelper.WriteAttribute(sw, "e", this.e); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "n", this.n, false); + if (this.t != ST_ItemType.data) + XmlHelper.WriteAttribute(sw, "t", this.t.ToString()); + XmlHelper.WriteAttribute(sw, "h", this.h, false); + XmlHelper.WriteAttribute(sw, "s", this.s, false); + XmlHelper.WriteAttribute(sw, "sd", this.sd, false, true); + XmlHelper.WriteAttribute(sw, "f", this.f, false); + XmlHelper.WriteAttribute(sw, "m", this.m, false); + XmlHelper.WriteAttribute(sw, "c", this.c, false); + XmlHelper.WriteAttribute(sw, "x", this.x, true); + XmlHelper.WriteAttribute(sw, "d", this.d, false); + XmlHelper.WriteAttribute(sw, "e", this.e, false, true); + sw.Write("/>"); } private string nField; @@ -3665,10 +3687,17 @@ public static CT_AutoSortScope Parse(XmlNode node, XmlNamespaceManager namespace internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - sw.Write(">"); - if (this.pivotArea != null) - this.pivotArea.Write(sw, "pivotArea"); - sw.Write(string.Format("", nodeName)); + if (this.pivotArea == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.pivotArea != null) + this.pivotArea.Write(sw, "pivotArea"); + sw.Write(string.Format("", nodeName)); + } } private CT_PivotArea pivotAreaField; @@ -3738,15 +3767,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.field != null) + if (this.field == null || this.field.Count == 0) { - foreach (CT_Field x in this.field) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.field != null && this.field.Count > 0) { - x.Write(sw, "field"); + foreach (CT_Field x in this.field) + { + x.Write(sw, "field"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List fieldField; @@ -3831,9 +3867,8 @@ public static CT_Field Parse(XmlNode node, XmlNamespaceManager namespaceManager) internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "x", this.x); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "x", this.x, true); + sw.Write("/>"); } private int xField; @@ -3881,15 +3916,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.i != null) + if (this.i == null || this.i.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_I x in this.i) + sw.Write(">"); + if (this.i != null && this.i.Count > 0) { - x.Write(sw, "i"); + foreach (CT_I x in this.i) + { + x.Write(sw, "i"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List iField; @@ -3975,18 +4017,27 @@ public static CT_I Parse(XmlNode node, XmlNamespaceManager namespaceManager) internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "t", this.t.ToString()); + if (this.t != ST_ItemType.data) + XmlHelper.WriteAttribute(sw, "t", this.t.ToString()); XmlHelper.WriteAttribute(sw, "r", this.r); XmlHelper.WriteAttribute(sw, "i", this.i); - sw.Write(">"); - if (this.x != null) + + if (this.x == null || this.x.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_X x in this.x) + sw.Write(">"); + if (this.x != null && this.x.Count > 0) { - x.Write(sw, "x"); + foreach (CT_X x in this.x) + { + x.Write(sw, "x"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List xField; @@ -4090,15 +4141,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.field != null) + + if (this.field == null || this.field.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_Field x in this.field) + sw.Write(">"); + if (this.field != null && this.field.Count > 0) { - x.Write(sw, "field"); + foreach (CT_Field x in this.field) + { + x.Write(sw, "field"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List fieldField; @@ -4184,15 +4243,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.i != null) + + if (this.i == null || this.i.Count == 0) { - foreach (CT_I x in this.i) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.i != null && this.i.Count > 0) { - x.Write(sw, "i"); + foreach (CT_I x in this.i) + { + x.Write(sw, "i"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List iField; @@ -4275,15 +4342,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.pageField != null) + + if (this.pageField == null || this.pageField.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_PageField x in this.pageField) + sw.Write(">"); + if (this.pageField != null && this.pageField.Count > 0) { - x.Write(sw, "pageField"); + foreach (CT_PageField x in this.pageField) + { + x.Write(sw, "pageField"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List pageFieldField; @@ -4386,15 +4461,23 @@ public static CT_PageField Parse(XmlNode node, XmlNamespaceManager namespaceMana internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "fld", this.fld); - XmlHelper.WriteAttribute(sw, "item", this.item); - XmlHelper.WriteAttribute(sw, "hier", this.hier); - XmlHelper.WriteAttribute(sw, "name", this.name); - XmlHelper.WriteAttribute(sw, "cap", this.cap); - sw.Write(">"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "fld", this.fld, true); + XmlHelper.WriteAttribute(sw, "item", this.item, false); + XmlHelper.WriteAttribute(sw, "hier", this.hier, false); + XmlHelper.WriteAttribute(sw, "name", this.name, false); + XmlHelper.WriteAttribute(sw, "cap", this.cap, false); + + if (this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_ExtensionList extLstField; @@ -4552,15 +4635,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.dataField != null) + + if (this.dataField == null || this.dataField.Count == 0) { - foreach (CT_DataField x in this.dataField) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.dataField != null && this.dataField.Count > 0) { - x.Write(sw, "dataField"); + foreach (CT_DataField x in this.dataField) + { + x.Write(sw, "dataField"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List dataFieldField; @@ -4667,16 +4758,24 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "name", this.name); - XmlHelper.WriteAttribute(sw, "fld", this.fld); - XmlHelper.WriteAttribute(sw, "subtotal", this.subtotal.ToString()); - XmlHelper.WriteAttribute(sw, "showDataAs", this.showDataAs.ToString()); - XmlHelper.WriteAttribute(sw, "baseField", this.baseField); - XmlHelper.WriteAttribute(sw, "baseItem", this.baseItem); - XmlHelper.WriteAttribute(sw, "numFmtId", this.numFmtId); - sw.Write(">"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "fld", this.fld, true); + XmlHelper.WriteAttribute(sw, "subtotal", this.subtotal.ToString(), false); + XmlHelper.WriteAttribute(sw, "showDataAs", this.showDataAs.ToString(), false); + XmlHelper.WriteAttribute(sw, "baseField", this.baseField, true); + XmlHelper.WriteAttribute(sw, "baseItem", this.baseItem, true); + XmlHelper.WriteAttribute(sw, "numFmtId", this.numFmtId, false); + + if (this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_ExtensionList extLstField; @@ -4699,7 +4798,7 @@ internal void Write(StreamWriter sw, string nodeName) public CT_DataField() { - this.extLstField = new CT_ExtensionList(); + //this.extLstField = new CT_ExtensionList(); this.subtotalField = ST_DataConsolidateFunction.sum; this.showDataAsField = ST_ShowDataAs.normal; this.baseFieldField = -1; @@ -4892,15 +4991,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.format != null) + if (this.format == null || this.format.Count == 0) { - foreach (CT_Format x in this.format) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.format != null && this.format.Count > 0) { - x.Write(sw, "format"); + foreach (CT_Format x in this.format) + { + x.Write(sw, "format"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List formatField; @@ -4974,12 +5080,19 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "action", this.action.ToString()); XmlHelper.WriteAttribute(sw, "dxfId", this.dxfId); - sw.Write(">"); - if (this.pivotArea != null) - this.pivotArea.Write(sw, "pivotArea"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + if (this.pivotArea == null && this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.pivotArea != null) + this.pivotArea.Write(sw, "pivotArea"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_PivotArea pivotAreaField; @@ -5115,15 +5228,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.conditionalFormat != null) + + if (this.conditionalFormat == null || this.conditionalFormat.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_ConditionalFormat x in this.conditionalFormat) + sw.Write(">"); + if (this.conditionalFormat != null && this.conditionalFormat.Count > 0) { - x.Write(sw, "conditionalFormat"); + foreach (CT_ConditionalFormat x in this.conditionalFormat) + { + x.Write(sw, "conditionalFormat"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List conditionalFormatField; @@ -5200,12 +5321,20 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "scope", this.scope.ToString()); XmlHelper.WriteAttribute(sw, "type", this.type.ToString()); XmlHelper.WriteAttribute(sw, "priority", this.priority); - sw.Write(">"); - if (this.pivotAreas != null) - this.pivotAreas.Write(sw, "pivotAreas"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + + if (this.pivotAreas == null && this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.pivotAreas != null) + this.pivotAreas.Write(sw, "pivotAreas"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_PivotAreas pivotAreasField; @@ -5220,7 +5349,6 @@ internal void Write(StreamWriter sw, string nodeName) public CT_ConditionalFormat() { - this.extLstField = new CT_ExtensionList(); this.pivotAreasField = new CT_PivotAreas(); this.scopeField = ST_Scope.selection; this.typeField = ST_Type.none; @@ -5323,15 +5451,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.pivotArea != null) + + if (this.pivotArea == null || this.pivotArea.Count == 0) { - foreach (CT_PivotArea x in this.pivotArea) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.pivotArea != null && this.pivotArea.Count > 0) { - x.Write(sw, "pivotArea"); + foreach (CT_PivotArea x in this.pivotArea) + { + x.Write(sw, "pivotArea"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List pivotAreaField; @@ -5451,15 +5587,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.chartFormat != null) + + if (this.chartFormat == null || this.chartFormat.Count == 0) { - foreach (CT_ChartFormat x in this.chartFormat) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.chartFormat != null && this.chartFormat.Count > 0) { - x.Write(sw, "chartFormat"); + foreach (CT_ChartFormat x in this.chartFormat) + { + x.Write(sw, "chartFormat"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List chartFormatField; @@ -5531,13 +5675,21 @@ public static CT_ChartFormat Parse(XmlNode node, XmlNamespaceManager namespaceMa internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "chart", this.chart); - XmlHelper.WriteAttribute(sw, "format", this.format); + XmlHelper.WriteAttribute(sw, "chart", this.chart, true); + XmlHelper.WriteAttribute(sw, "format", this.format, true); XmlHelper.WriteAttribute(sw, "series", this.series); - sw.Write(">"); - if (this.pivotArea != null) - this.pivotArea.Write(sw, "pivotArea"); - sw.Write(string.Format("", nodeName)); + + if (this.pivotArea == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.pivotArea != null) + this.pivotArea.Write(sw, "pivotArea"); + sw.Write(string.Format("", nodeName)); + } } private CT_PivotArea pivotAreaField; @@ -5637,15 +5789,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.pivotHierarchy != null) + if (this.pivotHierarchy == null || this.pivotHierarchy.Count == 0) { - foreach (CT_PivotHierarchy x in this.pivotHierarchy) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.pivotHierarchy != null && this.pivotHierarchy.Count > 0) { - x.Write(sw, "pivotHierarchy"); + foreach (CT_PivotHierarchy x in this.pivotHierarchy) + { + x.Write(sw, "pivotHierarchy"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List pivotHierarchyField; @@ -5750,30 +5909,38 @@ public static CT_PivotHierarchy Parse(XmlNode node, XmlNamespaceManager namespac internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "outline", this.outline); - XmlHelper.WriteAttribute(sw, "multipleItemSelectionAllowed", this.multipleItemSelectionAllowed); - XmlHelper.WriteAttribute(sw, "subtotalTop", this.subtotalTop); - XmlHelper.WriteAttribute(sw, "showInFieldList", this.showInFieldList); - XmlHelper.WriteAttribute(sw, "dragToRow", this.dragToRow); - XmlHelper.WriteAttribute(sw, "dragToCol", this.dragToCol); - XmlHelper.WriteAttribute(sw, "dragToPage", this.dragToPage); - XmlHelper.WriteAttribute(sw, "dragToData", this.dragToData); - XmlHelper.WriteAttribute(sw, "dragOff", this.dragOff); - XmlHelper.WriteAttribute(sw, "includeNewItemsInFilter", this.includeNewItemsInFilter); + XmlHelper.WriteAttribute(sw, "outline", this.outline, false); + XmlHelper.WriteAttribute(sw, "multipleItemSelectionAllowed", this.multipleItemSelectionAllowed, false); + XmlHelper.WriteAttribute(sw, "subtotalTop", this.subtotalTop, false); + XmlHelper.WriteAttribute(sw, "showInFieldList", this.showInFieldList, false, true); + XmlHelper.WriteAttribute(sw, "dragToRow", this.dragToRow, false, true); + XmlHelper.WriteAttribute(sw, "dragToCol", this.dragToCol, false, true); + XmlHelper.WriteAttribute(sw, "dragToPage", this.dragToPage, false, true); + XmlHelper.WriteAttribute(sw, "dragToData", this.dragToData, false); + XmlHelper.WriteAttribute(sw, "dragOff", this.dragOff, false, true); + XmlHelper.WriteAttribute(sw, "includeNewItemsInFilter", this.includeNewItemsInFilter, false); XmlHelper.WriteAttribute(sw, "caption", this.caption); - sw.Write(">"); - if (this.mps != null) - this.mps.Write(sw, "mps"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - if (this.members != null) + + if (this.mps == null && this.extLst == null && (this.members == null || this.members.Count == 0)) + { + sw.Write("/>"); + } + else { - foreach (CT_Members x in this.members) + sw.Write(">"); + if (this.mps != null) + this.mps.Write(sw, "mps"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + if (this.members != null && this.members.Count > 0) { - x.Write(sw, "members"); + foreach (CT_Members x in this.members) + { + x.Write(sw, "members"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private CT_MemberProperties mpsField; @@ -5806,9 +5973,9 @@ internal void Write(StreamWriter sw, string nodeName) public CT_PivotHierarchy() { - this.extLstField = new CT_ExtensionList(); - this.membersField = new List(); - this.mpsField = new CT_MemberProperties(); + //this.extLstField = new CT_ExtensionList(); + //this.membersField = new List(); + //this.mpsField = new CT_MemberProperties(); this.outlineField = false; this.multipleItemSelectionAllowedField = false; this.subtotalTopField = false; @@ -6043,15 +6210,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.mp != null) + + if (this.mp == null || this.mp.Count == 0) { - foreach (CT_MemberProperty x in this.mp) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.mp != null && this.mp.Count > 0) { - x.Write(sw, "mp"); + foreach (CT_MemberProperty x in this.mp) + { + x.Write(sw, "mp"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List mpField; @@ -6151,8 +6326,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "pLen", this.pLen); XmlHelper.WriteAttribute(sw, "level", this.level); XmlHelper.WriteAttribute(sw, "field", this.field); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private string nameField; @@ -6393,15 +6567,22 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); XmlHelper.WriteAttribute(sw, "level", this.level); - sw.Write(">"); - if (this.member != null) + if (this.member == null || this.member.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_Member x in this.member) + sw.Write(">"); + if (this.member != null && this.member.Count > 0) { - x.Write(sw, "member"); + foreach (CT_Member x in this.member) + { + x.Write(sw, "member"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List memberField; @@ -6507,8 +6688,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "name", this.name); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private string nameField; @@ -6559,13 +6739,12 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "name", this.name); - XmlHelper.WriteAttribute(sw, "showRowHeaders", this.showRowHeaders); - XmlHelper.WriteAttribute(sw, "showColHeaders", this.showColHeaders); - XmlHelper.WriteAttribute(sw, "showRowStripes", this.showRowStripes); - XmlHelper.WriteAttribute(sw, "showColStripes", this.showColStripes); - XmlHelper.WriteAttribute(sw, "showLastColumn", this.showLastColumn); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "showRowHeaders", this.showRowHeaders, false); + XmlHelper.WriteAttribute(sw, "showColHeaders", this.showColHeaders, false); + XmlHelper.WriteAttribute(sw, "showRowStripes", this.showRowStripes, false); + XmlHelper.WriteAttribute(sw, "showColStripes", this.showColStripes, false); + XmlHelper.WriteAttribute(sw, "showLastColumn", this.showLastColumn, false); + sw.Write("/>"); } private string nameField; @@ -6763,15 +6942,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.filter != null) + if (this.filter == null || this.filter.Count == 0) { - foreach (CT_PivotFilter x in this.filter) + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.filter != null && this.filter.Count > 0) { - x.Write(sw, "filter"); + foreach (CT_PivotFilter x in this.filter) + { + x.Write(sw, "filter"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List filterField; @@ -6857,23 +7043,30 @@ public static CT_PivotFilter Parse(XmlNode node, XmlNamespaceManager namespaceMa internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "fld", this.fld); - XmlHelper.WriteAttribute(sw, "mpFld", this.mpFld); - XmlHelper.WriteAttribute(sw, "type", this.type.ToString()); - XmlHelper.WriteAttribute(sw, "evalOrder", this.evalOrder); - XmlHelper.WriteAttribute(sw, "id", this.id); - XmlHelper.WriteAttribute(sw, "iMeasureHier", this.iMeasureHier); - XmlHelper.WriteAttribute(sw, "iMeasureFld", this.iMeasureFld); - XmlHelper.WriteAttribute(sw, "name", this.name); - XmlHelper.WriteAttribute(sw, "description", this.description); - XmlHelper.WriteAttribute(sw, "stringValue1", this.stringValue1); - XmlHelper.WriteAttribute(sw, "stringValue2", this.stringValue2); - sw.Write(">"); - if (this.autoFilter != null) - this.autoFilter.Write(sw, "autoFilter"); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "fld", this.fld, true); + XmlHelper.WriteAttribute(sw, "mpFld", this.mpFld, false); + XmlHelper.WriteAttribute(sw, "type", this.type.ToString(), true); + XmlHelper.WriteAttribute(sw, "evalOrder", this.evalOrder, false); + XmlHelper.WriteAttribute(sw, "id", this.id, true); + XmlHelper.WriteAttribute(sw, "iMeasureHier", this.iMeasureHier, false); + XmlHelper.WriteAttribute(sw, "iMeasureFld", this.iMeasureFld, false); + XmlHelper.WriteAttribute(sw, "name", this.name, false); + XmlHelper.WriteAttribute(sw, "description", this.description, false); + XmlHelper.WriteAttribute(sw, "stringValue1", this.stringValue1, false); + XmlHelper.WriteAttribute(sw, "stringValue2", this.stringValue2, false); + if (this.autoFilter == null && this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.autoFilter != null) + this.autoFilter.Write(sw, "autoFilter"); + if (this.extLst != null) + this.extLst.Write(sw, "extLst"); + sw.Write(string.Format("", nodeName)); + } } private CT_AutoFilter autoFilterField; @@ -6910,7 +7103,6 @@ internal void Write(StreamWriter sw, string nodeName) public CT_PivotFilter() { - this.extLstField = new CT_ExtensionList(); this.autoFilterField = new CT_AutoFilter(); this.evalOrderField = 0; } @@ -7360,15 +7552,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.rowHierarchyUsage != null) + if (this.rowHierarchyUsage == null || this.rowHierarchyUsage.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_HierarchyUsage x in this.rowHierarchyUsage) + sw.Write(">"); + if (this.rowHierarchyUsage != null && this.rowHierarchyUsage.Count > 0) { - x.Write(sw, "rowHierarchyUsage"); + foreach (CT_HierarchyUsage x in this.rowHierarchyUsage) + { + x.Write(sw, "rowHierarchyUsage"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List rowHierarchyUsageField; @@ -7444,9 +7643,8 @@ public static CT_HierarchyUsage Parse(XmlNode node, XmlNamespaceManager namespac internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "hierarchyUsage", this.hierarchyUsage); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "hierarchyUsage", this.hierarchyUsage, true); + sw.Write("/>"); } private int hierarchyUsageField; @@ -7494,15 +7692,22 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "count", this.count); - sw.Write(">"); - if (this.colHierarchyUsage != null) + if (this.colHierarchyUsage == null || this.colHierarchyUsage.Count == 0) + { + sw.Write("/>"); + } + else { - foreach (CT_HierarchyUsage x in this.colHierarchyUsage) + sw.Write(">"); + if (this.colHierarchyUsage != null && this.colHierarchyUsage.Count > 0) { - x.Write(sw, "colHierarchyUsage"); + foreach (CT_HierarchyUsage x in this.colHierarchyUsage) + { + x.Write(sw, "colHierarchyUsage"); + } } + sw.Write(string.Format("", nodeName)); } - sw.Write(string.Format("", nodeName)); } private List colHierarchyUsageField; diff --git a/OpenXmlFormats/Spreadsheet/Sheet.cs b/OpenXmlFormats/Spreadsheet/Sheet.cs index b47649b68..c400e90ed 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet.cs @@ -2090,8 +2090,8 @@ internal void Write(StreamWriter sw, string nodeName) public CT_PivotAreaReference() { - this.extLstField = new CT_ExtensionList(); - this.xField = new List(); + //this.extLstField = new CT_ExtensionList(); + //this.xField = new List(); this.selectedField = true; this.byPositionField = false; this.relativeField = false; @@ -2109,6 +2109,7 @@ public CT_PivotAreaReference() this.varPSubtotalField = false; } + [XmlAttribute] public List x { get @@ -2121,6 +2122,7 @@ public List x } } + [XmlAttribute] public CT_ExtensionList extLst { get @@ -2133,6 +2135,7 @@ public CT_ExtensionList extLst } } + [XmlAttribute] public uint field { get @@ -2158,6 +2161,7 @@ public bool fieldSpecified } } + [XmlAttribute] public uint count { get @@ -2183,6 +2187,7 @@ public bool countSpecified } } + [XmlAttribute] [DefaultValue(true)] public bool selected { @@ -2196,6 +2201,7 @@ public bool selected } } + [XmlAttribute] [DefaultValue(false)] public bool byPosition { @@ -2209,6 +2215,7 @@ public bool byPosition } } + [XmlAttribute] [DefaultValue(false)] public bool relative { @@ -2222,6 +2229,7 @@ public bool relative } } + [XmlAttribute] [DefaultValue(false)] public bool defaultSubtotal { @@ -2235,6 +2243,7 @@ public bool defaultSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool sumSubtotal { @@ -2248,6 +2257,7 @@ public bool sumSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool countASubtotal { @@ -2261,6 +2271,7 @@ public bool countASubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool avgSubtotal { @@ -2274,6 +2285,7 @@ public bool avgSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool maxSubtotal { @@ -2287,6 +2299,7 @@ public bool maxSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool minSubtotal { @@ -2300,6 +2313,7 @@ public bool minSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool productSubtotal { @@ -2313,6 +2327,7 @@ public bool productSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool countSubtotal { @@ -2326,6 +2341,7 @@ public bool countSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool stdDevSubtotal { @@ -2339,6 +2355,7 @@ public bool stdDevSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool stdDevPSubtotal { @@ -2352,6 +2369,7 @@ public bool stdDevPSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool varSubtotal { @@ -2365,6 +2383,7 @@ public bool varSubtotal } } + [XmlAttribute] [DefaultValue(false)] public bool varPSubtotal { @@ -2386,6 +2405,7 @@ public class CT_Index private uint vField; + [XmlAttribute()] public uint v { get @@ -2409,7 +2429,7 @@ public static CT_Index Parse(XmlNode node, XmlNamespaceManager namespaceManager) internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "v", this.v); + XmlHelper.WriteAttribute(sw, "v", this.v, true); sw.Write(">"); sw.Write(string.Format("", nodeName)); } diff --git a/OpenXmlFormats/Vml/SpreadsheetDrawing.cs b/OpenXmlFormats/Vml/SpreadsheetDrawing.cs index 2170c06e6..ae8da03ac 100644 --- a/OpenXmlFormats/Vml/SpreadsheetDrawing.cs +++ b/OpenXmlFormats/Vml/SpreadsheetDrawing.cs @@ -9,10 +9,11 @@ namespace NPOI.OpenXmlFormats.Vml.Spreadsheet { - public class CT_AlternateContent + public class CT_AlternateContent { - public string innerXml { get; set; } - public CT_AlternateContent() + public string outerXml { get; set; } + + public CT_AlternateContent() { } public static CT_AlternateContent Parse(XmlNode node, XmlNamespaceManager namespaceManager) @@ -25,23 +26,13 @@ public static CT_AlternateContent Parse(XmlNode node, XmlNamespaceManager namesp { return ac; } - ac.innerXml = node.InnerXml; + ac.outerXml = node.OuterXml; return ac; } internal void Write(StreamWriter sw, string nodeName) { - sw.Write(string.Format("", nodeName)); - } - else - { - sw.Write(">"); - sw.Write(this.innerXml); - sw.Write(string.Format("", nodeName)); - } - + if (this.outerXml != null) + sw.Write(this.outerXml); } } diff --git a/openxml4Net/Util/XmlHelper.cs b/openxml4Net/Util/XmlHelper.cs index d084f1b8d..69ec63337 100644 --- a/openxml4Net/Util/XmlHelper.cs +++ b/openxml4Net/Util/XmlHelper.cs @@ -312,9 +312,9 @@ public static void WriteAttribute(StreamWriter sw, string attributeName, bool va { WriteAttribute(sw, attributeName, value, true); } - public static void WriteAttribute(StreamWriter sw, string attributeName, bool value, bool writeIfBlank) + public static void WriteAttribute(StreamWriter sw, string attributeName, bool value, bool writeIfBlank, bool defaultValue = false) { - if (value == false && !writeIfBlank) + if (value == defaultValue && !writeIfBlank) return; WriteAttribute(sw, attributeName, value ? "1" : "0"); } @@ -339,6 +339,17 @@ public static void WriteAttribute(StreamWriter sw, string attributeName, int val { WriteAttribute(sw, attributeName, value, false); } + public static void WriteAttribute(StreamWriter sw, string attributeName, uint value, bool writeIfBlank) + { + if (value == 0 && !writeIfBlank) + return; + + WriteAttribute(sw, attributeName, value.ToString(CultureInfo.InvariantCulture)); + } + public static void WriteAttribute(StreamWriter sw, string attributeName, uint value) + { + WriteAttribute(sw, attributeName, value, false); + } public static void WriteAttribute(StreamWriter sw, string attributeName, string value) { WriteAttribute(sw, attributeName, value, false); @@ -356,26 +367,22 @@ public static void WriteAttribute(StreamWriter sw, string attributeName, byte[] WriteAttribute(sw, attributeName, BitConverter.ToString(value).Replace("-", ""), false); } + public static void WriteAttribute(StreamWriter sw, string attributeName, uint value, uint defaultValue, bool writeIfBlank = false) { if(value != defaultValue) - WriteAttribute(sw, attributeName, (int)value, true); + WriteAttribute(sw, attributeName, value, true); else if(writeIfBlank) - WriteAttribute(sw, attributeName, (int)value, writeIfBlank); - } - public static void WriteAttribute(StreamWriter sw, string attributeName, uint value, bool writeIfBlank = false) - { - WriteAttribute(sw, attributeName, (int)value, writeIfBlank); + WriteAttribute(sw, attributeName, value, writeIfBlank); } public static void WriteAttribute(StreamWriter sw, string attributeName, DateTime? value) { if (value == null) return; - WriteAttribute(sw, attributeName, value.ToString(), false); - //how to write xsd:datetime data - throw new NotImplementedException(); + WriteAttribute(sw, attributeName, value.Value.ToString("yyyy-MM-ddTHH:mm:ss"), false); } + public static void LoadXmlSafe(XmlDocument xmlDoc, Stream stream) { XmlReaderSettings settings = new XmlReaderSettings(); From f1dcab206ea7e8ef22b41ffd16d01f8a27c37ac5 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 1 Dec 2021 03:40:18 +0800 Subject: [PATCH 003/159] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2e8d1bcca..c671d48c1 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ NPOI
This project is the .NET version of POI Java project. With NPOI, you can read/write Office 2003/2007 files very easily.
+[Who is using NPOI?](https://github.com/nissl-lab/npoi/issues/705) + Get Started with NPOI ============ [Getting Started with NPOI](https://github.com/nissl-lab/npoi/wiki/Getting-Started-with-NPOI) @@ -17,16 +19,14 @@ Get Started with NPOI [ORM on NPOI](https://github.com/nissl-lab/npoi/wiki/ORM-on-NPOI) +[NPOI Changelog](https://github.com/nissl-lab/npoi/wiki/Changelog) + +Replace DotnetCore.NPOI with NPOI ASAP +=========== [Stop using Dotnetcore/NPOI nuget package. It’s obsolete!](https://tonyqus.medium.com/stop-using-dotnetcore-npoi-nuget-package-its-too-obsolete-6d0aeedb3319) [The real history of Dotnetcore.NPOI](https://tonyqus.medium.com/the-real-history-of-dotnetcore-npoi-999bb5e140c7) -[中国哪些公司在用.NET](https://github.com/dotnet-cn/jobs) - -[起底开源邪教NCC组织](https://github.com/nissl-lab/npoi/wiki/起底开源邪教NCC组织) - -[NPOI Changelog](https://github.com/nissl-lab/npoi/wiki/Changelog) - Telegram User Group ================ Join us on telegram: https://t.me/npoidevs From 9501f0b2ea1fe2d17fae8ec6c4aa2a729f388429 Mon Sep 17 00:00:00 2001 From: KnyazSh Date: Wed, 1 Dec 2021 00:54:24 +0300 Subject: [PATCH 004/159] Fix NullReferenceException --- .../Spreadsheet/PivotTable/CT_PivotCacheRecords.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs index 1efcd6f02..64c2628f9 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs @@ -44,9 +44,12 @@ internal void Write(StreamWriter sw) sw.Write(">"); if (this.extLst != null) this.extLst.Write(sw, "extLst"); - foreach (CT_PivotCacheRecord o in this.r) + if (this.r != null && this.r.Count > 0) { - o.Write(sw); + foreach (CT_PivotCacheRecord o in this.r) + { + o.Write(sw); + } } sw.Write(""); } From f24334869a6c4096df65ae1493984f9b7dbc9bce Mon Sep 17 00:00:00 2001 From: KnyazSh Date: Wed, 1 Dec 2021 00:59:07 +0300 Subject: [PATCH 005/159] Fix a chart based on a pivot table --- OpenXmlFormats/Drawing/Chart/Chart.cs | 231 ++++++++++++++------------ 1 file changed, 128 insertions(+), 103 deletions(-) diff --git a/OpenXmlFormats/Drawing/Chart/Chart.cs b/OpenXmlFormats/Drawing/Chart/Chart.cs index 2c6d3fc6d..0224fb62a 100644 --- a/OpenXmlFormats/Drawing/Chart/Chart.cs +++ b/OpenXmlFormats/Drawing/Chart/Chart.cs @@ -74,6 +74,8 @@ public static CT_ChartSpace Parse(XmlNode node, XmlNamespaceManager namespaceMan ctObj.lang = CT_TextLanguageID.Parse(childNode, namespaceManager); else if (childNode.LocalName == "roundedCorners") ctObj.roundedCorners = CT_Boolean.Parse(childNode, namespaceManager); + else if (childNode.LocalName == "AlternateContent") + ctObj.alternateContent = Vml.Spreadsheet.CT_AlternateContent.Parse(childNode, namespaceManager); else if (childNode.LocalName == "style") ctObj.style = CT_Style.Parse(childNode, namespaceManager); else if (childNode.LocalName == "clrMapOvr") @@ -117,6 +119,8 @@ internal void Write(Stream stream) this.lang.Write(sw, "lang"); if (this.roundedCorners != null) this.roundedCorners.Write(sw, "roundedCorners"); + if (this.alternateContent != null) + this.alternateContent.Write(sw, "AlternateContent"); if (this.style != null) this.style.Write(sw, "style"); if (this.clrMapOvr != null) @@ -197,6 +201,19 @@ public CT_Boolean roundedCorners } } + Vml.Spreadsheet.CT_AlternateContent alternateContentField = null; + public Vml.Spreadsheet.CT_AlternateContent alternateContent + { + get + { + return alternateContentField; + } + set + { + this.alternateContentField = value; + } + } + [XmlElement(Order = 3)] public CT_Style style { @@ -428,8 +445,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } } @@ -513,8 +529,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "horizontalDpi", this.horizontalDpi); XmlHelper.WriteAttribute(sw, "verticalDpi", this.verticalDpi); XmlHelper.WriteAttribute(sw, "copies", this.copies); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -711,8 +726,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "b", this.b); XmlHelper.WriteAttribute(sw, "header", this.header); XmlHelper.WriteAttribute(sw, "footer", this.footer); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -1520,8 +1534,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } } @@ -2219,8 +2232,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -2297,8 +2309,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } } @@ -3228,8 +3239,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -4061,8 +4071,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private ST_Crosses valField; @@ -4123,8 +4132,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -4165,8 +4173,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private ST_TimeUnit valField; @@ -4783,8 +4790,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -4828,8 +4834,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -4870,8 +4875,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private ST_LblAlgn valField; @@ -5716,8 +5720,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -6673,8 +6676,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } } @@ -6763,8 +6765,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } @@ -6923,8 +6924,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private ST_PictureFormat valField; @@ -6982,8 +6982,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private double valField; @@ -7656,8 +7655,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } } @@ -7997,8 +7995,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -8067,8 +8064,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } public CT_Order() @@ -8122,8 +8118,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -8533,8 +8528,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private ST_ErrDir valField; @@ -8590,8 +8584,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private ST_ErrBarType valField; @@ -8663,8 +8656,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -9356,7 +9348,7 @@ public class CT_MultiLvlStrData private CT_UnsignedInt ptCountField; - private List lvlField; + private List lvlField; private List extLstField; @@ -9365,14 +9357,14 @@ public static CT_MultiLvlStrData Parse(XmlNode node, XmlNamespaceManager namespa if (node == null) return null; CT_MultiLvlStrData ctObj = new CT_MultiLvlStrData(); - ctObj.lvl = new List(); + ctObj.lvl = new List(); ctObj.extLst = new List(); foreach (XmlNode childNode in node.ChildNodes) { if (childNode.LocalName == "ptCount") ctObj.ptCount = CT_UnsignedInt.Parse(childNode, namespaceManager); else if (childNode.LocalName == "lvl") - ctObj.lvl.Add(CT_StrVal.Parse(childNode, namespaceManager)); + ctObj.lvl.Add(CT_Lvl.Parse(childNode, namespaceManager)); else if (childNode.LocalName == "extLst") ctObj.extLst.Add(CT_Extension.Parse(childNode, namespaceManager)); } @@ -9387,9 +9379,9 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(">"); if (this.ptCount != null) this.ptCount.Write(sw, "ptCount"); - if (this.lvl != null) + if (this.lvl != null && this.lvl.Count > 0) { - foreach (CT_StrVal x in this.lvl) + foreach (CT_Lvl x in this.lvl) { x.Write(sw, "lvl"); } @@ -9422,7 +9414,7 @@ public CT_UnsignedInt ptCount } [XmlElement(Order = 1)] - public List lvl + public List lvl { get { @@ -9709,8 +9701,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -9759,8 +9750,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -9831,8 +9821,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -9882,8 +9871,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -9950,8 +9938,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private sbyte valField; @@ -10008,8 +9995,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -10281,18 +10267,18 @@ internal void Write(StreamWriter sw, string nodeName) x.Write(sw, "dateAx"); } } - if (this.catAx != null) + if (this.valAx != null) { - foreach (CT_CatAx x in this.catAx) + foreach (CT_ValAx x in this.valAx) { - x.Write(sw, "catAx"); + x.Write(sw, "valAx"); } } - if (this.valAx != null) + if (this.catAx != null) { - foreach (CT_ValAx x in this.valAx) + foreach (CT_CatAx x in this.catAx) { - x.Write(sw, "valAx"); + x.Write(sw, "catAx"); } } if (this.spPr != null) @@ -10689,8 +10675,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } [XmlAttribute] @@ -10754,8 +10739,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } } @@ -10790,8 +10774,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } @@ -10833,8 +10816,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } @@ -10890,8 +10872,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } @@ -11252,7 +11233,7 @@ public class CT_Chart private CT_Boolean autoTitleDeletedField; - private List pivotFmtsField; + private CT_PivotFmts pivotFmtsField; private CT_View3D view3DField; @@ -11282,7 +11263,6 @@ public static CT_Chart Parse(XmlNode node, XmlNamespaceManager namespaceManager) if (node == null) return null; CT_Chart ctObj = new CT_Chart(); - ctObj.pivotFmts = new List(); ctObj.extLst = new List(); foreach (XmlNode childNode in node.ChildNodes) { @@ -11290,6 +11270,8 @@ public static CT_Chart Parse(XmlNode node, XmlNamespaceManager namespaceManager) ctObj.title = CT_Title.Parse(childNode, namespaceManager); else if (childNode.LocalName == "autoTitleDeleted") ctObj.autoTitleDeleted = CT_Boolean.Parse(childNode, namespaceManager); + else if (childNode.LocalName == "pivotFmts") + ctObj.pivotFmts = CT_PivotFmts.Parse(childNode, namespaceManager); else if (childNode.LocalName == "view3D") ctObj.view3D = CT_View3D.Parse(childNode, namespaceManager); else if (childNode.LocalName == "floor") @@ -11308,8 +11290,6 @@ public static CT_Chart Parse(XmlNode node, XmlNamespaceManager namespaceManager) ctObj.dispBlanksAs = CT_DispBlanksAs.Parse(childNode, namespaceManager); else if (childNode.LocalName == "showDLblsOverMax") ctObj.showDLblsOverMax = CT_Boolean.Parse(childNode, namespaceManager); - else if (childNode.LocalName == "pivotFmts") - ctObj.pivotFmts.Add(CT_PivotFmt.Parse(childNode, namespaceManager)); else if (childNode.LocalName == "extLst") ctObj.extLst.Add(CT_Extension.Parse(childNode, namespaceManager)); } @@ -11326,6 +11306,8 @@ internal void Write(StreamWriter sw, string nodeName) this.title.Write(sw, "title"); if (this.autoTitleDeleted != null) this.autoTitleDeleted.Write(sw, "autoTitleDeleted"); + if (this.pivotFmts != null) + this.pivotFmts.Write(sw, "pivotFmts"); if (this.view3D != null) this.view3D.Write(sw, "view3D"); if (this.floor != null) @@ -11344,13 +11326,7 @@ internal void Write(StreamWriter sw, string nodeName) this.dispBlanksAs.Write(sw, "dispBlanksAs"); if (this.showDLblsOverMax != null) this.showDLblsOverMax.Write(sw, "showDLblsOverMax"); - if (this.pivotFmts != null) - { - foreach (CT_PivotFmt x in this.pivotFmts) - { - x.Write(sw, "pivotFmts"); - } - } + if (this.extLst != null) { foreach (CT_Extension x in this.extLst) @@ -11423,7 +11399,7 @@ public CT_Boolean autoTitleDeleted } [XmlElement(Order = 2)] - public List pivotFmts + public CT_PivotFmts pivotFmts { get { @@ -11742,7 +11718,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format(""); if (this.name != null) - sw.Write(string.Format("{0}", this.name)); + sw.Write(string.Format("{0}", this.name)); if (this.fmtId != null) this.fmtId.Write(sw, "fmtId"); if (this.extLst != null) @@ -11819,8 +11795,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } private byte valField; @@ -11877,8 +11852,7 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } } @@ -11921,9 +11895,32 @@ public List ext [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/chart", IsNullable = true)] public class CT_Lvl { - private List ptField; + public static CT_Lvl Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + if (node == null) + return null; + CT_Lvl ctObj = new CT_Lvl(); + ctObj.pt = new List(); + foreach (XmlNode childNode in node.ChildNodes) + { + if (childNode.LocalName == "pt") + ctObj.pt.Add(CT_StrVal.Parse(childNode, namespaceManager)); + } + return ctObj; + } + + internal void Write(StreamWriter sw, string nodeName) + { + sw.Write(string.Format("", nodeName)); + foreach (CT_StrVal x in this.pt) + { + x.Write(sw, "pt"); + } + sw.Write(string.Format("", nodeName)); + } + public CT_Lvl() { //this.ptField = new List(); @@ -12011,9 +12008,37 @@ public List bandFmt [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/chart", IsNullable = true)] public class CT_PivotFmts { - private List pivotFmtField; + public static CT_PivotFmts Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + if (node == null) + return null; + CT_PivotFmts ctObj = new CT_PivotFmts(); + ctObj.pivotFmt = new List(); + foreach (XmlNode childNode in node.ChildNodes) + { + if (childNode.LocalName == "pivotFmt") + ctObj.pivotFmt.Add(CT_PivotFmt.Parse(childNode, namespaceManager)); + } + return ctObj; + } + + internal void Write(StreamWriter sw, string nodeName) + { + if (this.pivotFmt.Count == 0) + { + return; + } + + sw.Write(string.Format("", nodeName)); + foreach (CT_PivotFmt x in this.pivotFmt) + { + x.Write(sw, "pivotFmt"); + } + sw.Write(string.Format("", nodeName)); + } + public CT_PivotFmts() { //this.pivotFmtField = new List(); From 4ee5c5eee41967b282d713be71353a86b81601d5 Mon Sep 17 00:00:00 2001 From: KnyazSh Date: Thu, 2 Dec 2021 01:51:23 +0300 Subject: [PATCH 006/159] Fix default values of CT_PivotArea properties --- OpenXmlFormats/Spreadsheet/Sheet.cs | 51 +++++++++++++++-------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/Sheet.cs b/OpenXmlFormats/Spreadsheet/Sheet.cs index c400e90ed..0463c3579 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet.cs @@ -1633,12 +1633,12 @@ public static CT_PivotArea Parse(XmlNode node, XmlNamespaceManager namespaceMana ctObj.field = XmlHelper.ReadInt(node.Attributes["field"]); if (node.Attributes["type"] != null) ctObj.type = (ST_PivotAreaType)Enum.Parse(typeof(ST_PivotAreaType), node.Attributes["type"].Value); - ctObj.dataOnly = XmlHelper.ReadBool(node.Attributes["dataOnly"]); + ctObj.dataOnly = XmlHelper.ReadBool(node.Attributes["dataOnly"], true); ctObj.labelOnly = XmlHelper.ReadBool(node.Attributes["labelOnly"]); ctObj.grandRow = XmlHelper.ReadBool(node.Attributes["grandRow"]); ctObj.grandCol = XmlHelper.ReadBool(node.Attributes["grandCol"]); ctObj.cacheIndex = XmlHelper.ReadBool(node.Attributes["cacheIndex"]); - ctObj.outline = XmlHelper.ReadBool(node.Attributes["outline"]); + ctObj.outline = XmlHelper.ReadBool(node.Attributes["outline"], true); ctObj.offset = XmlHelper.ReadString(node.Attributes["offset"]); ctObj.collapsedLevelsAreSubtotals = XmlHelper.ReadBool(node.Attributes["collapsedLevelsAreSubtotals"]); if (node.Attributes["axis"] != null) @@ -1658,15 +1658,16 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "field", this.field); - XmlHelper.WriteAttribute(sw, "type", this.type.ToString()); - XmlHelper.WriteAttribute(sw, "dataOnly", this.dataOnly); - XmlHelper.WriteAttribute(sw, "labelOnly", this.labelOnly); - XmlHelper.WriteAttribute(sw, "grandRow", this.grandRow); - XmlHelper.WriteAttribute(sw, "grandCol", this.grandCol); - XmlHelper.WriteAttribute(sw, "cacheIndex", this.cacheIndex); - XmlHelper.WriteAttribute(sw, "outline", this.outline); + if (this.type != ST_PivotAreaType.normal) + XmlHelper.WriteAttribute(sw, "type", this.type.ToString()); + XmlHelper.WriteAttribute(sw, "dataOnly", this.dataOnly, false, true); + XmlHelper.WriteAttribute(sw, "labelOnly", this.labelOnly, false); + XmlHelper.WriteAttribute(sw, "grandRow", this.grandRow, false); + XmlHelper.WriteAttribute(sw, "grandCol", this.grandCol, false); + XmlHelper.WriteAttribute(sw, "cacheIndex", this.cacheIndex, false); + XmlHelper.WriteAttribute(sw, "outline", this.outline, false, true); XmlHelper.WriteAttribute(sw, "offset", this.offset); - XmlHelper.WriteAttribute(sw, "collapsedLevelsAreSubtotals", this.collapsedLevelsAreSubtotals); + XmlHelper.WriteAttribute(sw, "collapsedLevelsAreSubtotals", this.collapsedLevelsAreSubtotals, false); XmlHelper.WriteAttribute(sw, "axis", this.axis.ToString()); XmlHelper.WriteAttribute(sw, "fieldPosition", this.fieldPosition); sw.Write(">"); @@ -2060,21 +2061,21 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "field", this.field); XmlHelper.WriteAttribute(sw, "count", this.count); - XmlHelper.WriteAttribute(sw, "selected", this.selected); - XmlHelper.WriteAttribute(sw, "byPosition", this.byPosition); - XmlHelper.WriteAttribute(sw, "relative", this.relative); - XmlHelper.WriteAttribute(sw, "defaultSubtotal", this.defaultSubtotal); - XmlHelper.WriteAttribute(sw, "sumSubtotal", this.sumSubtotal); - XmlHelper.WriteAttribute(sw, "countASubtotal", this.countASubtotal); - XmlHelper.WriteAttribute(sw, "avgSubtotal", this.avgSubtotal); - XmlHelper.WriteAttribute(sw, "maxSubtotal", this.maxSubtotal); - XmlHelper.WriteAttribute(sw, "minSubtotal", this.minSubtotal); - XmlHelper.WriteAttribute(sw, "productSubtotal", this.productSubtotal); - XmlHelper.WriteAttribute(sw, "countSubtotal", this.countSubtotal); - XmlHelper.WriteAttribute(sw, "stdDevSubtotal", this.stdDevSubtotal); - XmlHelper.WriteAttribute(sw, "stdDevPSubtotal", this.stdDevPSubtotal); - XmlHelper.WriteAttribute(sw, "varSubtotal", this.varSubtotal); - XmlHelper.WriteAttribute(sw, "varPSubtotal", this.varPSubtotal); + XmlHelper.WriteAttribute(sw, "selected", this.selected, false, true); + XmlHelper.WriteAttribute(sw, "byPosition", this.byPosition, false); + XmlHelper.WriteAttribute(sw, "relative", this.relative, false); + XmlHelper.WriteAttribute(sw, "defaultSubtotal", this.defaultSubtotal, false); + XmlHelper.WriteAttribute(sw, "sumSubtotal", this.sumSubtotal, false); + XmlHelper.WriteAttribute(sw, "countASubtotal", this.countASubtotal, false); + XmlHelper.WriteAttribute(sw, "avgSubtotal", this.avgSubtotal, false); + XmlHelper.WriteAttribute(sw, "maxSubtotal", this.maxSubtotal, false); + XmlHelper.WriteAttribute(sw, "minSubtotal", this.minSubtotal, false); + XmlHelper.WriteAttribute(sw, "productSubtotal", this.productSubtotal, false); + XmlHelper.WriteAttribute(sw, "countSubtotal", this.countSubtotal, false); + XmlHelper.WriteAttribute(sw, "stdDevSubtotal", this.stdDevSubtotal, false); + XmlHelper.WriteAttribute(sw, "stdDevPSubtotal", this.stdDevPSubtotal, false); + XmlHelper.WriteAttribute(sw, "varSubtotal", this.varSubtotal, false); + XmlHelper.WriteAttribute(sw, "varPSubtotal", this.varPSubtotal, false); sw.Write(">"); if (this.extLst != null) this.extLst.Write(sw, "extLst"); From 746ee0cfc098bd06e8224cabb3b04a0484584b9c Mon Sep 17 00:00:00 2001 From: Federico Colombo Date: Mon, 13 Dec 2021 23:25:10 -0600 Subject: [PATCH 007/159] Adding CloneStyleFrom to avoid losing custom style properties (i.e. FillForegroundXSSFColor) --- main/SS/Util/CellUtil.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/main/SS/Util/CellUtil.cs b/main/SS/Util/CellUtil.cs index e4918b699..86395f7a5 100644 --- a/main/SS/Util/CellUtil.cs +++ b/main/SS/Util/CellUtil.cs @@ -395,6 +395,7 @@ public static void SetCellStyleProperties(ICell cell, Dictionary if (newStyle == null) { newStyle = workbook.CreateCellStyle(); + newStyle.CloneStyleFrom(originalStyle); SetFormatProperties(newStyle, workbook, values); } From 7783066c66d74904c83ad8bca91eae624d631dbc Mon Sep 17 00:00:00 2001 From: Patrice DARGENTON Date: Sat, 1 Jan 2022 08:58:01 +0100 Subject: [PATCH 008/159] GetColumnWidth: no need to explore the whole sheet --- main/SS/Util/SheetUtil.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main/SS/Util/SheetUtil.cs b/main/SS/Util/SheetUtil.cs index dc65b2773..8952f8558 100644 --- a/main/SS/Util/SheetUtil.cs +++ b/main/SS/Util/SheetUtil.cs @@ -418,6 +418,10 @@ public static double GetColumnWidth(ISheet sheet, int column, bool useMergedCell DataFormatter formatter = new DataFormatter(); int defaultCharWidth = GetDefaultCharWidth(sheet.Workbook); + // No need to explore the whole sheet: explore only the first 100 lines + const int nbRowMax = 100; + if (lastRow - firstRow > nbRowMax) lastRow = firstRow + nbRowMax; + double width = -1; for (int rowIdx = firstRow; rowIdx <= lastRow; ++rowIdx) { From 3b93678e15fd4fc8da225e0b35b57d9af4f1e23b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=8B=87?= Date: Sun, 9 Jan 2022 13:05:35 +0800 Subject: [PATCH 009/159] fix Hyperlink without Remove --- .../Record/Aggregates/RowRecordsAggregate.cs | 11 ++++++++++- main/HSSF/UserModel/HSSFSheet.cs | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/main/HSSF/Record/Aggregates/RowRecordsAggregate.cs b/main/HSSF/Record/Aggregates/RowRecordsAggregate.cs index 2a2a8fb61..8c98f632d 100644 --- a/main/HSSF/Record/Aggregates/RowRecordsAggregate.cs +++ b/main/HSSF/Record/Aggregates/RowRecordsAggregate.cs @@ -41,13 +41,14 @@ public class RowRecordsAggregate : RecordAggregate private SortedList _rowRecords; //private int size = 0; private ValueRecordsAggregate _valuesAgg; + private List _hyperlinkRecordRecords; private List _unknownRecords; private SharedValueManager _sharedValueManager; // Cache values to speed up performance of // getStartRowNumberForBlock / getEndRowNumberForBlock, see Bugzilla 47405 private RowRecord[] _rowRecordValues = null; - + public IEnumerable HyperlinkRecordRecords { get => _hyperlinkRecordRecords; } /** Creates a new instance of ValueRecordsAggregate */ @@ -60,6 +61,7 @@ private RowRecordsAggregate(SharedValueManager svm) { _rowRecords = new SortedList(); _valuesAgg = new ValueRecordsAggregate(); + _hyperlinkRecordRecords = new List(); _unknownRecords = new List(); _sharedValueManager = svm; } @@ -124,6 +126,10 @@ public override void VisitContainedRecords(RecordVisitor rv) // Calculate Offset from the start of a DBCellRecord to the first Row cellRecord.RowOffset = (pos); rv.VisitRecord(cellRecord); + } + foreach (Record _hyperlinkRecord in _hyperlinkRecordRecords) + { + rv.VisitRecord(_hyperlinkRecord); } foreach (Record _unknownRecord in _unknownRecords) { @@ -149,6 +155,9 @@ public RowRecordsAggregate(RecordStream rs, SharedValueManager svm) case DConRefRecord.sid: AddUnknownRecord(rec); continue; + case HyperlinkRecord.sid: + _hyperlinkRecordRecords.Add((HyperlinkRecord)rec); + continue; case DBCellRecord.sid: // end of 'Row Block'. Should only occur after cell records // ignore DBCELL records because POI generates them upon re-serialization diff --git a/main/HSSF/UserModel/HSSFSheet.cs b/main/HSSF/UserModel/HSSFSheet.cs index 10e08457a..c6d572332 100644 --- a/main/HSSF/UserModel/HSSFSheet.cs +++ b/main/HSSF/UserModel/HSSFSheet.cs @@ -2419,6 +2419,15 @@ public IHyperlink GetHyperlink(int row, int column) { return new HSSFHyperlink(link); } + } + else if (rec is RowRecordsAggregate rra) { + foreach (var link in rra.HyperlinkRecordRecords) + { + if (link.FirstColumn == column && link.FirstRow == row) + { + return new HSSFHyperlink(link); + } + } } } return null; @@ -2448,6 +2457,14 @@ public List GetHyperlinkList() if (rec is HyperlinkRecord){ HyperlinkRecord link = (HyperlinkRecord)rec; hyperlinkList.Add(new HSSFHyperlink(link)); + } + else if (rec is RowRecordsAggregate rra) { + foreach (var link in rra.HyperlinkRecordRecords) + { + if (link is HyperlinkRecord hylink){ + hyperlinkList.Add(new HSSFHyperlink(link)); + } + } } } return hyperlinkList; From b4f81e92dce93c94bb569c1c8b5a3c2d8697d0ff Mon Sep 17 00:00:00 2001 From: "SEGECO\\pdar" Date: Wed, 19 Jan 2022 08:00:09 +0100 Subject: [PATCH 010/159] Optional param maxRows added --- main/SS/Util/SheetUtil.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/SS/Util/SheetUtil.cs b/main/SS/Util/SheetUtil.cs index 8952f8558..aa887831a 100644 --- a/main/SS/Util/SheetUtil.cs +++ b/main/SS/Util/SheetUtil.cs @@ -411,16 +411,16 @@ public static double GetColumnWidth(ISheet sheet, int column, bool useMergedCell * @param useMergedCells whether to use merged cells * @param firstRow 0-based index of the first row to consider (inclusive) * @param lastRow 0-based index of the last row to consider (inclusive) + * @param maxRows limit the scope to maxRows rows to speed up the function, or leave 0 (optional) * @return the width in pixels or -1 if cell is empty */ - public static double GetColumnWidth(ISheet sheet, int column, bool useMergedCells, int firstRow, int lastRow) + public static double GetColumnWidth(ISheet sheet, int column, bool useMergedCells, int firstRow, int lastRow, int maxRows=0) { DataFormatter formatter = new DataFormatter(); int defaultCharWidth = GetDefaultCharWidth(sheet.Workbook); - // No need to explore the whole sheet: explore only the first 100 lines - const int nbRowMax = 100; - if (lastRow - firstRow > nbRowMax) lastRow = firstRow + nbRowMax; + // No need to explore the whole sheet: explore only the first maxRows lines + if (maxRows > 0 && lastRow - firstRow > maxRows) lastRow = firstRow + maxRows; double width = -1; for (int rowIdx = firstRow; rowIdx <= lastRow; ++rowIdx) From e202417ae3a05bf492f84183b3432f49ed71cb5c Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 20 Jan 2022 05:55:03 +0800 Subject: [PATCH 011/159] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c671d48c1..d2f22d8d2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +想通过Github找.NET工作吗?欢迎watch[这个项目](https://github.com/dotnet-cn/jobs/) + NPOI =================== [![NuGet Badge](https://buildstats.info/nuget/NPOI)](https://www.nuget.org/packages/NPOI) From b02b5e73409f832fb293d889ee0ad0731f883ad3 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Fri, 4 Feb 2022 09:47:18 +0800 Subject: [PATCH 012/159] fix ST_TargetScreenSize enum value parsing issue use Enums.NET to fix #739 --- OpenXmlFormats/NPOI.OpenXmlFormats.csproj | 5 +++ .../Workbook/CT_WebPublishObjects.cs | 33 +++++++++---------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/OpenXmlFormats/NPOI.OpenXmlFormats.csproj b/OpenXmlFormats/NPOI.OpenXmlFormats.csproj index a8b9271bb..4cec91f2b 100644 --- a/OpenXmlFormats/NPOI.OpenXmlFormats.csproj +++ b/OpenXmlFormats/NPOI.OpenXmlFormats.csproj @@ -296,6 +296,11 @@ NPOI.OpenXml4Net + + + 4.0.0 + + diff --git a/OpenXmlFormats/Spreadsheet/Workbook/CT_WebPublishObjects.cs b/OpenXmlFormats/Spreadsheet/Workbook/CT_WebPublishObjects.cs index 89ed133f0..c3ce69654 100644 --- a/OpenXmlFormats/Spreadsheet/Workbook/CT_WebPublishObjects.cs +++ b/OpenXmlFormats/Spreadsheet/Workbook/CT_WebPublishObjects.cs @@ -6,55 +6,52 @@ using System.Text; using System.Xml; using System.Xml.Serialization; +using EnumsNET; namespace NPOI.OpenXmlFormats.Spreadsheet -{ - +{ public enum ST_TargetScreenSize { - - - [XmlEnum("544x376")] + [Description("544x376")] Item544x376, - - [XmlEnum("640x480")] + [Description("640x480")] Item640x480, - [XmlEnum("720x512")] + [Description("720x512")] Item720x512, - [XmlEnum("800x600")] + [Description("800x600")] Item800x600, - [XmlEnum("1024x768")] + [Description("1024x768")] Item1024x768, - [XmlEnum("1152x882")] + [Description("1152x882")] Item1152x882, - [XmlEnum("1152x900")] + [Description("1152x900")] Item1152x900, - [XmlEnum("1280x1024")] + [Description("1280x1024")] Item1280x1024, - [XmlEnum("1600x1200")] + [Description("1600x1200")] Item1600x1200, - [XmlEnum("1800x1440")] + [Description("1800x1440")] Item1800x1440, - [XmlEnum("1920x1200")] + [Description("1920x1200")] Item1920x1200, } [Serializable] @@ -90,7 +87,9 @@ public static CT_WebPublishing Parse(XmlNode node, XmlNamespaceManager namespace ctObj.vml = XmlHelper.ReadBool(node.Attributes["vml"]); ctObj.allowPng = XmlHelper.ReadBool(node.Attributes["allowPng"]); if (node.Attributes["targetScreenSize"] != null) - ctObj.targetScreenSize = (ST_TargetScreenSize)Enum.Parse(typeof(ST_TargetScreenSize), node.Attributes["targetScreenSize"].Value); + { + ctObj.targetScreenSize = Enums.Parse(node.Attributes["targetScreenSize"].Value,false, EnumFormat.Description); + } ctObj.dpi = XmlHelper.ReadUInt(node.Attributes["dpi"]); ctObj.codePage = XmlHelper.ReadUInt(node.Attributes["codePage"]); return ctObj; From e8c7b805046128e5accbf64fa437edc9b1d41889 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Fri, 4 Feb 2022 10:02:37 +0800 Subject: [PATCH 013/159] retire XmlEnumParser use Enums.NET instead --- OpenXmlFormats/Spreadsheet/AutoFilter.cs | 36 +++++++++---------- ooxml/NPOI.OOXML.csproj | 6 +++- .../XSSFConditionalFormattingRule.cs | 3 +- .../UserModel/XSSFIconMultiStateFormatting.cs | 3 +- ooxml/XSSF/Util/XmlEnumParser.cs | 1 + 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/AutoFilter.cs b/OpenXmlFormats/Spreadsheet/AutoFilter.cs index 5b61099e7..648d2fe82 100644 --- a/OpenXmlFormats/Spreadsheet/AutoFilter.cs +++ b/OpenXmlFormats/Spreadsheet/AutoFilter.cs @@ -900,73 +900,71 @@ public bool iconIdSpecified public enum ST_IconSetType { - - - [XmlEnum("3Arrows")] + [Description("3Arrows")] Item3Arrows, - [XmlEnum("3ArrowsGray")] + [Description("3ArrowsGray")] Item3ArrowsGray, - [XmlEnum("3Flags")] + [Description("3Flags")] Item3Flags, - [XmlEnum("3TrafficLights1")] + [Description("3TrafficLights1")] Item3TrafficLights1, - [XmlEnum("3TrafficLights2")] + [Description("3TrafficLights2")] Item3TrafficLights2, - [XmlEnum("3Signs")] + [Description("3Signs")] Item3Signs, - [XmlEnum("3Symbols")] + [Description("3Symbols")] Item3Symbols, - [XmlEnum("3Symbols2")] + [Description("3Symbols2")] Item3Symbols2, - [XmlEnum("4Arrows")] + [Description("4Arrows")] Item4Arrows, - [XmlEnum("4ArrowsGray")] + [Description("4ArrowsGray")] Item4ArrowsGray, - [XmlEnum("4RedToBlack")] + [Description("4RedToBlack")] Item4RedToBlack, - [XmlEnum("4Rating")] + [Description("4Rating")] Item4Rating, - [XmlEnum("4TrafficLights")] + [Description("4TrafficLights")] Item4TrafficLights, - [XmlEnum("5Arrows")] + [Description("5Arrows")] Item5Arrows, - [XmlEnum("5ArrowsGray")] + [Description("5ArrowsGray")] Item5ArrowsGray, - [XmlEnum("5Rating")] + [Description("5Rating")] Item5Rating, - [XmlEnum("5Quarters")] + [Description("5Quarters")] Item5Quarters, } diff --git a/ooxml/NPOI.OOXML.csproj b/ooxml/NPOI.OOXML.csproj index b78cb0ab8..20eb93579 100644 --- a/ooxml/NPOI.OOXML.csproj +++ b/ooxml/NPOI.OOXML.csproj @@ -386,7 +386,11 @@ - + + + 4.0.0 + + {10fa8538-157a-4380-a4f6-8e2c3ee92cae} diff --git a/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs b/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs index 97c88ec44..19bc9fad5 100644 --- a/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs +++ b/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs @@ -17,6 +17,7 @@ * ==================================================================== */ +using EnumsNET; using NPOI.OpenXmlFormats.Spreadsheet; using NPOI.SS.UserModel; using NPOI.XSSF.Model; @@ -278,7 +279,7 @@ public XSSFIconMultiStateFormatting CreateMultiStateFormatting(IconSet iconSet) // Set the type of the icon set if (iconSet.name != null) { - ST_IconSetType xIconSet = XmlEnumParser.ForName(iconSet.name, ST_IconSetType.Item3TrafficLights1); + ST_IconSetType xIconSet =Enums.Parse(iconSet.name, false, EnumFormat.Description); icons.iconSet = xIconSet; } diff --git a/ooxml/XSSF/UserModel/XSSFIconMultiStateFormatting.cs b/ooxml/XSSF/UserModel/XSSFIconMultiStateFormatting.cs index ec6ee0f78..e4e8f0f5c 100644 --- a/ooxml/XSSF/UserModel/XSSFIconMultiStateFormatting.cs +++ b/ooxml/XSSF/UserModel/XSSFIconMultiStateFormatting.cs @@ -19,6 +19,7 @@ namespace NPOI.XSSF.UserModel { using System; + using EnumsNET; using NPOI.OpenXmlFormats.Spreadsheet; using NPOI.SS.UserModel; using NPOI.XSSF.Util; @@ -46,7 +47,7 @@ public IconSet IconSet } set { - ST_IconSetType xIconSet = XmlEnumParser.ForName(value.name, ST_IconSetType.Item3TrafficLights1); + ST_IconSetType xIconSet = Enums.Parse(value.name, false, EnumFormat.Description); _iconset.iconSet = (xIconSet); } } diff --git a/ooxml/XSSF/Util/XmlEnumParser.cs b/ooxml/XSSF/Util/XmlEnumParser.cs index 75b74e9ea..2e575ad34 100644 --- a/ooxml/XSSF/Util/XmlEnumParser.cs +++ b/ooxml/XSSF/Util/XmlEnumParser.cs @@ -8,6 +8,7 @@ namespace NPOI.XSSF.Util { + [Obsolete] public class XmlEnumParser { private static Dictionary values; From 6b9ff4bf827e6ece61fb50e45c97f5e511fb7752 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Fri, 4 Feb 2022 15:00:36 +0800 Subject: [PATCH 014/159] handling printerSettingsX.bin correctly while calling CopySheet fix #715 --- ooxml/XSSF/UserModel/XSSFSheet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ooxml/XSSF/UserModel/XSSFSheet.cs b/ooxml/XSSF/UserModel/XSSFSheet.cs index a5d45ce97..13a5641b3 100644 --- a/ooxml/XSSF/UserModel/XSSFSheet.cs +++ b/ooxml/XSSF/UserModel/XSSFSheet.cs @@ -4805,7 +4805,7 @@ public ISheet CopySheet(String name, Boolean copyStyle) continue; } //skip printerSettings.bin part - if (r.GetPackagePart().PartName.Name == "/xl/printerSettings/printerSettings1.bin") + if (r.GetPackagePart().PartName.Name.StartsWith("/xl/printerSettings/printerSettings")) continue; PackageRelationship rel = r.GetPackageRelationship(); clonedSheet.GetPackagePart().AddRelationship( From 348b5b399c59c7deedd366294383448aa89d418b Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 5 Feb 2022 01:15:30 +0800 Subject: [PATCH 015/159] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d2f22d8d2..783608f46 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ NPOI [![NuGet Badge](https://buildstats.info/nuget/NPOI)](https://www.nuget.org/packages/NPOI) [![Ko-Fi](https://img.shields.io/static/v1?style=flat-square&message=Support%20the%20Project&color=success&style=plastic&logo=ko-fi&label=$$)](https://ko-fi.com/tonyqus) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square&logo=Apache)](License.md) -[![Badge](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu/#/en_US) [![traffic](https://api.segment.io/v1/pixel/track?data=ewogICJ3cml0ZUtleSI6ICJBV2NjaWd1UkhKODBuNkJ4WlI4cHRaRzBINzY0RmJObCIsCiAgInVzZXJJZCI6ICJ0b255cXVzIiwKICAiZXZlbnQiOiAiTlBPSSBIb21lcGFnZSIKfQ== )](#)
From 9dc96481878fe6e83b7e658ec52bbef053cd2065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=8B=87?= Date: Sat, 5 Feb 2022 13:30:22 +0800 Subject: [PATCH 016/159] write a unit test that named test52447 --- testcases/main/HSSF/UserModel/TestBugs.cs | 11 +++++++++++ testcases/test-data/spreadsheet/52447.xls | Bin 0 -> 54784 bytes 2 files changed, 11 insertions(+) create mode 100644 testcases/test-data/spreadsheet/52447.xls diff --git a/testcases/main/HSSF/UserModel/TestBugs.cs b/testcases/main/HSSF/UserModel/TestBugs.cs index be176cd74..8bb6fa2b6 100644 --- a/testcases/main/HSSF/UserModel/TestBugs.cs +++ b/testcases/main/HSSF/UserModel/TestBugs.cs @@ -3485,5 +3485,16 @@ public void Test53564() //fos.Close(); wb.Close(); } + + + // follow https://svn.apache.org/viewvc?view=revision&revision=1896552 to write a unit test for this fix. + [Test] + public void test52447() + { + using (IWorkbook wb = HSSFTestDataSamples.OpenSampleWorkbook("52447.xls")) + { + Assert.IsNotNull(cell.CellFormula); + } + } } } \ No newline at end of file diff --git a/testcases/test-data/spreadsheet/52447.xls b/testcases/test-data/spreadsheet/52447.xls new file mode 100644 index 0000000000000000000000000000000000000000..7feb7c1b141aa1d8fb467e31aedff86ebb60e7b6 GIT binary patch literal 54784 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

w8r$FEOVp z(H)WZn=rZ(B8f`vCByj=bAG^BQT-fJWG^wNzUp|1Id$Lv5_5jg#PePky~M1ad0B5W>xWDj^{0B&zs-Kt zJD+cJ9e5p<#CqG;uP+PMmj&y~%=)7$rX`kDD-J)pwd+ zX4Zu!jQpxuU-o5vMX zoOhU0*V8-9xsL1UsbO7D?+VVlg7YqOeoPVEWloYPm%E1ZE_42*vGQ`>WzJtwoOhY? z<0b{oc~{iYyT1OsCphm3&U?)HQ;OgobCN`y_YCJf=KO@Q@^apD%H4!)W7B+BJg!}%(6e#%%8r@p#>l{tUjgpoh>H(p-lHvfhRqc;Bwkf?up zm03T*{r+#7zVv}$eIQsLFzcHtwFk^f60trotPhy=En`KjdT#PSus&d)604Jf_UQxm zspfp(%lR5}{-Vm|HRk*!Bhgs;%SNKH)RpKPTEAk#=o?yY7_sW{^fhMvRbxqh)z_o1 zv0wGw<=2>3Uyr`Vykq!2;|#vfxGsN+^@%Y!P4RjG&uf0h(I5|gJ&X5o1(Pgm%p~iZ z^FBTHtqu5y!0|Y}JS3ZshsaIj&y(?P;UVA!);B)%)z39{G_R-4RC#z<5b5awKo%au zL>juR)~}`cqww*OMVG^Gjhcqf({a#+orTL7dZ)){&6!QFzgkZZHP@f%5m1K@(d+Nm z(<7Gj=MP%+C^*`RLVx+z?)=^PJDTbB&U*bn4-a|FM;y9EL$6196Zt>M6l>;uSI&p! znNnAN1}+-;^Y&9XHo4CId3#!bwJiP9%Z@+)PUz_DEbB#~bKEcEl7}qoc@EO)jb`09 zI0>zD%2V2nWxFp5JIDLO9HirpW4kwn9hJRh4a}r>ZTB1r))yE_>l`1>VkE5_&vvsI zN$V!C?vAi?{CZEE8)_R~bmQ2~cQOgqJi+I9eWlUS;7p}wTfc_5Bs~LZx%KNtqQ~O@ zhLT1htO(O;$8r=(d4=_xDr_`|k)J4)(H!<$Dr^jgk-sQxj0hXcVdOUo8_QvDt9S(* zM*gF)0)!P?pH^YxIE?&AVdG3Z>V)W=inu#^jz5uwqazNSN?jW4o(Tai$UF19|ar12c~J4T|g&namFhyAXRDD3x?G?Bxo%qR!y zA68huufisA7?mA`P2#XWP+^lfY^n;Q9%Y5~hX~{D%4r4&)35(;yQ|OVyWJ5L``4$s zG8OBbnXXLTRcw+gkHQ!^viouxEBW_ZcVDtea=Wuuqj=nA>U3Wp5#57RZnU=R+Bw~q z><*potH?BGVsW~!oJh~@b-I>6 zf{rBWT1u=xhD3LfpMlp)@TY$vg}k1JgDk0|S#EtEmeeSzqgk$+A(aIUQDjA3&T{Kd zRO-~_EVsU(;!u|p=GfH1gxTKF{V6P*COrneB0@@wI;B$Uiz+SZhDxnJQ)y8* Date: Sun, 6 Feb 2022 08:14:32 +0800 Subject: [PATCH 017/159] fix ST_DataConsolidateFunction maps to DataConsolidateFunction #697 --- ooxml/XSSF/UserModel/XSSFPivotTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ooxml/XSSF/UserModel/XSSFPivotTable.cs b/ooxml/XSSF/UserModel/XSSFPivotTable.cs index 84f3818ac..1563f8ccf 100644 --- a/ooxml/XSSF/UserModel/XSSFPivotTable.cs +++ b/ooxml/XSSF/UserModel/XSSFPivotTable.cs @@ -364,7 +364,7 @@ private void AddDataField(DataConsolidateFunction function, int columnIndex, Str dataFields = pivotTableDefinition.AddNewDataFields(); } CT_DataField dataField = dataFields.AddNewDataField(); - dataField.subtotal = (ST_DataConsolidateFunction)(function.Value); + dataField.subtotal = (ST_DataConsolidateFunction)(function.Value-1); ICell cell = GetDataSheet().GetRow(pivotArea.FirstCell.Row) .GetCell(pivotArea.FirstCell.Col + columnIndex); cell.SetCellType(CellType.String); From 6b1d17fa20097f49bbd4632fd1b9e700201c6b88 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 6 Feb 2022 08:23:51 +0800 Subject: [PATCH 018/159] change values of DataConsolidateFunction with -1 Fix #697 --- .../NPOI.OpenXmlFormats.Core.csproj | 1 + main/SS/UserModel/DataConsolidateFunction.cs | 22 +++++++-------- ooxml/XSSF/UserModel/XSSFPivotTable.cs | 2 +- .../XSSF/UserModel/BaseTestXSSFPivotTable.cs | 28 ------------------- 4 files changed, 13 insertions(+), 40 deletions(-) diff --git a/OpenXmlFormats/NPOI.OpenXmlFormats.Core.csproj b/OpenXmlFormats/NPOI.OpenXmlFormats.Core.csproj index d42848f4d..5b33678cb 100644 --- a/OpenXmlFormats/NPOI.OpenXmlFormats.Core.csproj +++ b/OpenXmlFormats/NPOI.OpenXmlFormats.Core.csproj @@ -22,6 +22,7 @@ + diff --git a/main/SS/UserModel/DataConsolidateFunction.cs b/main/SS/UserModel/DataConsolidateFunction.cs index 2a0f9c8e5..7a1d02dc3 100644 --- a/main/SS/UserModel/DataConsolidateFunction.cs +++ b/main/SS/UserModel/DataConsolidateFunction.cs @@ -26,17 +26,17 @@ namespace NPOI.SS.UserModel */ public class DataConsolidateFunction { - public static DataConsolidateFunction AVERAGE = new DataConsolidateFunction(1, "Average"), - COUNT = new DataConsolidateFunction(2, "Count"), - COUNT_NUMS = new DataConsolidateFunction(3, "Count"), - MAX = new DataConsolidateFunction(4, "Max"), - MIN = new DataConsolidateFunction(5, "Min"), - PRODUCT = new DataConsolidateFunction(6, "Product"), - STD_DEV = new DataConsolidateFunction(7, "StdDev"), - STD_DEVP = new DataConsolidateFunction(8, "StdDevp"), - SUM = new DataConsolidateFunction(9, "Sum"), - VAR = new DataConsolidateFunction(10, "Var"), - VARP = new DataConsolidateFunction(11, "Varp"); + public static DataConsolidateFunction AVERAGE = new DataConsolidateFunction(0, "Average"), + COUNT = new DataConsolidateFunction(1, "Count"), + COUNT_NUMS = new DataConsolidateFunction(2, "Count"), + MAX = new DataConsolidateFunction(3, "Max"), + MIN = new DataConsolidateFunction(4, "Min"), + PRODUCT = new DataConsolidateFunction(5, "Product"), + STD_DEV = new DataConsolidateFunction(6, "StdDev"), + STD_DEVP = new DataConsolidateFunction(7, "StdDevp"), + SUM = new DataConsolidateFunction(8, "Sum"), + VAR = new DataConsolidateFunction(9, "Var"), + VARP = new DataConsolidateFunction(10, "Varp"); private int value; private String name; diff --git a/ooxml/XSSF/UserModel/XSSFPivotTable.cs b/ooxml/XSSF/UserModel/XSSFPivotTable.cs index 1563f8ccf..84f3818ac 100644 --- a/ooxml/XSSF/UserModel/XSSFPivotTable.cs +++ b/ooxml/XSSF/UserModel/XSSFPivotTable.cs @@ -364,7 +364,7 @@ private void AddDataField(DataConsolidateFunction function, int columnIndex, Str dataFields = pivotTableDefinition.AddNewDataFields(); } CT_DataField dataField = dataFields.AddNewDataField(); - dataField.subtotal = (ST_DataConsolidateFunction)(function.Value-1); + dataField.subtotal = (ST_DataConsolidateFunction)(function.Value); ICell cell = GetDataSheet().GetRow(pivotArea.FirstCell.Row) .GetCell(pivotArea.FirstCell.Col + columnIndex); cell.SetCellType(CellType.String); diff --git a/testcases/ooxml/XSSF/UserModel/BaseTestXSSFPivotTable.cs b/testcases/ooxml/XSSF/UserModel/BaseTestXSSFPivotTable.cs index 5ccd19245..9e0aec053 100644 --- a/testcases/ooxml/XSSF/UserModel/BaseTestXSSFPivotTable.cs +++ b/testcases/ooxml/XSSF/UserModel/BaseTestXSSFPivotTable.cs @@ -293,34 +293,6 @@ public void TestAddDataColumnWithOffsetData() offsetPivotTable.AddColumnLabel(DataConsolidateFunction.SUM, 0); } - [Ignore("not found in poi")] - [Test] - public void Test58294() - { - //XSSFWorkbook wb = new XSSFWorkbook("C:\\temp\\test1.xlsx"); - //XSSFSheet sheet = wb.GetSheetAt(1) as XSSFSheet; - //XSSFSheet sheet0 = wb.GetSheetAt(0) as XSSFSheet; - //sheet0.SetActiveCell("A4"); - //XSSFPivotTable pivotTable = sheet0.CreatePivotTable(new AreaReference("A3:H6"), new CellReference("A4"), sheet); - //pivotTable.AddRowLabel(1); - //pivotTable.AddRowLabel(3); - //pivotTable.AddRowLabel(5); - //pivotTable.AddColumnLabel(DataConsolidateFunction.SUM, 6, "Sum of days with hauls"); - //pivotTable.AddColumnLabel(DataConsolidateFunction.SUM, 7, "Sum of days site cutoff"); - ////checkPivotTables(wb); - //FileStream fileOut = new FileStream("c:\\temp\\test2new.xlsx", FileMode.Create, FileAccess.ReadWrite); - //try - //{ - // wb.Write(fileOut); - //} - //finally - //{ - // fileOut.Close(); - //} - //XSSFWorkbook wbBack = XSSFTestDataSamples.WriteOutAndReadBack(wb); - ////checkPivotTables(wbBack); - //wb.Close(); - } private void checkPivotTables(XSSFWorkbook wb) { IList pivotTables = (wb.GetSheetAt(0) as XSSFSheet).GetPivotTables(); From 0b2c236ed23e49ebb8a6d716d1e8dad75ab2d3c2 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 6 Feb 2022 08:26:52 +0800 Subject: [PATCH 019/159] change properties of IColor --- main/SS/UserModel/Color.cs | 4 ++-- main/SS/UserModel/ExtendedColor.cs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/main/SS/UserModel/Color.cs b/main/SS/UserModel/Color.cs index 9240b51a8..17b3dbe1d 100644 --- a/main/SS/UserModel/Color.cs +++ b/main/SS/UserModel/Color.cs @@ -2,7 +2,7 @@ { public interface IColor { - //short Indexed { get; } - //byte[] RGB { get; } + short Indexed { get; } + byte[] RGB { get; } } } diff --git a/main/SS/UserModel/ExtendedColor.cs b/main/SS/UserModel/ExtendedColor.cs index 43c03786a..16c0f9d47 100644 --- a/main/SS/UserModel/ExtendedColor.cs +++ b/main/SS/UserModel/ExtendedColor.cs @@ -60,6 +60,11 @@ protected void SetColor(Color clr) */ public abstract short Index { get; } + public virtual short Indexed + { + get { return Index; } + } + /** * Index of Theme color, if {@link #isThemed()} is true */ From 9651a3b5ffd664e075e8f0e0b8638b55ac574404 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 6 Feb 2022 08:30:01 +0800 Subject: [PATCH 020/159] remove two tools --- tools/JavaToCSharp/App.config | 106 ----- .../JavaToCSharp/Configuration/J2CSSection.cs | 17 - .../JavaToCSharp/Configuration/RuleSection.cs | 42 -- .../Configuration/RuleSectionCollection.cs | 40 -- tools/JavaToCSharp/IRule.cs | 23 - tools/JavaToCSharp/JavaToCSharp.csproj | 78 ---- tools/JavaToCSharp/JavaToCSharp.csproj.user | 9 - tools/JavaToCSharp/JavaToCSharp.gpState | 4 - tools/JavaToCSharp/JavaToCSharp.sln | 22 - tools/JavaToCSharp/Program.cs | 25 -- tools/JavaToCSharp/Properties/AssemblyInfo.cs | 36 -- tools/JavaToCSharp/RuleEngine.cs | 110 ----- tools/JavaToCSharp/Rules/CapitalizeRule.cs | 40 -- tools/JavaToCSharp/Rules/DeleteRule.cs | 31 -- .../Rules/EquivalentExceptionRule.cs | 112 ----- tools/JavaToCSharp/Rules/EquivalentRule.cs | 61 --- tools/JavaToCSharp/Rules/OtherRule.cs | 97 ----- tools/JavaToCSharp/Rules/RemoveGetRule.cs | 25 -- tools/JavaToCSharp/Rules/foreachRule.cs | 29 -- tools/JavaToCSharp/Rules/getIndexerRule.cs | 34 -- tools/JavaToCSharp/Rules/skiplineRule.cs | 27 -- tools/JavaToCSharp/batchrename.ps1 | 1 - tools/JavaToCSharp/run.ps1 | 5 - tools/POIFS Browser/AboutDialog.Designer.cs | 126 ------ tools/POIFS Browser/AboutDialog.cs | 41 -- tools/POIFS Browser/AboutDialog.resx | 120 ------ tools/POIFS Browser/AbstractTreeNode.cs | 73 ---- tools/POIFS Browser/DirectoryTreeNode.cs | 278 ------------ tools/POIFS Browser/DocumentTreeNode.cs | 61 --- tools/POIFS Browser/EscherPropertyTreeNode.cs | 45 -- tools/POIFS Browser/EscherRecordTreeNode.cs | 59 --- .../HSSF/AbstractRecordTreeNode.cs | 65 --- .../HSSF/CellRangeAddressTreeNode.cs | 56 --- .../HSSF/CellValueRecordTreeNode.cs | 31 -- .../HSSF/RecordAggregateTreeNode.cs | 118 ----- tools/POIFS Browser/HSSF/RecordTreeNode.cs | 107 ----- tools/POIFS Browser/HSSF/SubRecordTreeNode.cs | 58 --- .../HSSF/UnicodeStringTreeNode.cs | 28 -- .../HWPF/AbstractHWPFTreeNode.cs | 45 -- tools/POIFS Browser/HWPF/PAPXTreeNode.cs | 20 - tools/POIFS Browser/Images/Binary.png | Bin 340 -> 0 bytes tools/POIFS Browser/Images/File.png | Bin 319 -> 0 bytes tools/POIFS Browser/Images/Folder.png | Bin 439 -> 0 bytes tools/POIFS Browser/Images/FolderOpen.png | Bin 501 -> 0 bytes tools/POIFS Browser/Images/Open.png | Bin 523 -> 0 bytes tools/POIFS Browser/Images/Property.png | Bin 482 -> 0 bytes tools/POIFS Browser/Images/Save.png | Bin 498 -> 0 bytes tools/POIFS Browser/Images/SaveAs.png | Bin 539 -> 0 bytes tools/POIFS Browser/Images/SummaryStream.png | Bin 332 -> 0 bytes tools/POIFS Browser/MainForm.Designer.cs | 406 ------------------ tools/POIFS Browser/MainForm.cs | 278 ------------ tools/POIFS Browser/MainForm.resx | 256 ----------- tools/POIFS Browser/MockRecordVisitor.cs | 26 -- tools/POIFS Browser/POIFSBrowser.csproj | 141 ------ .../POIFS Browser/POIFSBrowser.csproj.vspscc | 10 - tools/POIFS Browser/POIFSBrowser.sln | 22 - tools/POIFS Browser/Program.cs | 43 -- .../POIFS Browser/Properties/AssemblyInfo.cs | 36 -- .../Properties/Resources.Designer.cs | 103 ----- tools/POIFS Browser/Properties/Resources.resx | 133 ------ .../Properties/Settings.Designer.cs | 26 -- .../Properties/Settings.settings | 7 - tools/POIFS Browser/PropertyListViewItem.cs | 98 ----- tools/POIFS Browser/Resources/NET_h_rgb_2.png | Bin 14573 -> 0 bytes tools/POIFS Browser/Resources/Open.png | Bin 523 -> 0 bytes tools/POIFS Browser/Resources/SaveAs.png | Bin 539 -> 0 bytes .../POIFS Browser/Resources/arrow_refresh.png | Bin 685 -> 0 bytes tools/POIFS Browser/app.config | 3 - 68 files changed, 3893 deletions(-) delete mode 100644 tools/JavaToCSharp/App.config delete mode 100644 tools/JavaToCSharp/Configuration/J2CSSection.cs delete mode 100644 tools/JavaToCSharp/Configuration/RuleSection.cs delete mode 100644 tools/JavaToCSharp/Configuration/RuleSectionCollection.cs delete mode 100644 tools/JavaToCSharp/IRule.cs delete mode 100644 tools/JavaToCSharp/JavaToCSharp.csproj delete mode 100644 tools/JavaToCSharp/JavaToCSharp.csproj.user delete mode 100644 tools/JavaToCSharp/JavaToCSharp.gpState delete mode 100644 tools/JavaToCSharp/JavaToCSharp.sln delete mode 100644 tools/JavaToCSharp/Program.cs delete mode 100644 tools/JavaToCSharp/Properties/AssemblyInfo.cs delete mode 100644 tools/JavaToCSharp/RuleEngine.cs delete mode 100644 tools/JavaToCSharp/Rules/CapitalizeRule.cs delete mode 100644 tools/JavaToCSharp/Rules/DeleteRule.cs delete mode 100644 tools/JavaToCSharp/Rules/EquivalentExceptionRule.cs delete mode 100644 tools/JavaToCSharp/Rules/EquivalentRule.cs delete mode 100644 tools/JavaToCSharp/Rules/OtherRule.cs delete mode 100644 tools/JavaToCSharp/Rules/RemoveGetRule.cs delete mode 100644 tools/JavaToCSharp/Rules/foreachRule.cs delete mode 100644 tools/JavaToCSharp/Rules/getIndexerRule.cs delete mode 100644 tools/JavaToCSharp/Rules/skiplineRule.cs delete mode 100644 tools/JavaToCSharp/batchrename.ps1 delete mode 100644 tools/JavaToCSharp/run.ps1 delete mode 100644 tools/POIFS Browser/AboutDialog.Designer.cs delete mode 100644 tools/POIFS Browser/AboutDialog.cs delete mode 100644 tools/POIFS Browser/AboutDialog.resx delete mode 100644 tools/POIFS Browser/AbstractTreeNode.cs delete mode 100644 tools/POIFS Browser/DirectoryTreeNode.cs delete mode 100644 tools/POIFS Browser/DocumentTreeNode.cs delete mode 100644 tools/POIFS Browser/EscherPropertyTreeNode.cs delete mode 100644 tools/POIFS Browser/EscherRecordTreeNode.cs delete mode 100644 tools/POIFS Browser/HSSF/AbstractRecordTreeNode.cs delete mode 100644 tools/POIFS Browser/HSSF/CellRangeAddressTreeNode.cs delete mode 100644 tools/POIFS Browser/HSSF/CellValueRecordTreeNode.cs delete mode 100644 tools/POIFS Browser/HSSF/RecordAggregateTreeNode.cs delete mode 100644 tools/POIFS Browser/HSSF/RecordTreeNode.cs delete mode 100644 tools/POIFS Browser/HSSF/SubRecordTreeNode.cs delete mode 100644 tools/POIFS Browser/HSSF/UnicodeStringTreeNode.cs delete mode 100644 tools/POIFS Browser/HWPF/AbstractHWPFTreeNode.cs delete mode 100644 tools/POIFS Browser/HWPF/PAPXTreeNode.cs delete mode 100644 tools/POIFS Browser/Images/Binary.png delete mode 100644 tools/POIFS Browser/Images/File.png delete mode 100644 tools/POIFS Browser/Images/Folder.png delete mode 100644 tools/POIFS Browser/Images/FolderOpen.png delete mode 100644 tools/POIFS Browser/Images/Open.png delete mode 100644 tools/POIFS Browser/Images/Property.png delete mode 100644 tools/POIFS Browser/Images/Save.png delete mode 100644 tools/POIFS Browser/Images/SaveAs.png delete mode 100644 tools/POIFS Browser/Images/SummaryStream.png delete mode 100644 tools/POIFS Browser/MainForm.Designer.cs delete mode 100644 tools/POIFS Browser/MainForm.cs delete mode 100644 tools/POIFS Browser/MainForm.resx delete mode 100644 tools/POIFS Browser/MockRecordVisitor.cs delete mode 100644 tools/POIFS Browser/POIFSBrowser.csproj delete mode 100644 tools/POIFS Browser/POIFSBrowser.csproj.vspscc delete mode 100644 tools/POIFS Browser/POIFSBrowser.sln delete mode 100644 tools/POIFS Browser/Program.cs delete mode 100644 tools/POIFS Browser/Properties/AssemblyInfo.cs delete mode 100644 tools/POIFS Browser/Properties/Resources.Designer.cs delete mode 100644 tools/POIFS Browser/Properties/Resources.resx delete mode 100644 tools/POIFS Browser/Properties/Settings.Designer.cs delete mode 100644 tools/POIFS Browser/Properties/Settings.settings delete mode 100644 tools/POIFS Browser/PropertyListViewItem.cs delete mode 100644 tools/POIFS Browser/Resources/NET_h_rgb_2.png delete mode 100644 tools/POIFS Browser/Resources/Open.png delete mode 100644 tools/POIFS Browser/Resources/SaveAs.png delete mode 100644 tools/POIFS Browser/Resources/arrow_refresh.png delete mode 100644 tools/POIFS Browser/app.config diff --git a/tools/JavaToCSharp/App.config b/tools/JavaToCSharp/App.config deleted file mode 100644 index 20af00af1..000000000 --- a/tools/JavaToCSharp/App.config +++ /dev/null @@ -1,106 +0,0 @@ - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/JavaToCSharp/Configuration/J2CSSection.cs b/tools/JavaToCSharp/Configuration/J2CSSection.cs deleted file mode 100644 index 323cdfaec..000000000 --- a/tools/JavaToCSharp/Configuration/J2CSSection.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Configuration; - -namespace JavaToCSharp.Configuration -{ - public class J2CSSection : ConfigurationSection - { - [ConfigurationProperty("Rules", IsDefaultCollection = true)] - [ConfigurationCollection(typeof(RuleSection))] - public RuleSectionCollection Rules - { - get { return this["Rules"] as RuleSectionCollection; } - } - } -} diff --git a/tools/JavaToCSharp/Configuration/RuleSection.cs b/tools/JavaToCSharp/Configuration/RuleSection.cs deleted file mode 100644 index 4e2d7968c..000000000 --- a/tools/JavaToCSharp/Configuration/RuleSection.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Configuration; - -namespace JavaToCSharp.Configuration -{ - public class RuleSection : ConfigurationElement - { - public RuleSection() { } - public RuleSection(string name, string pattern, string replacement) - { - this["name"] = name; - this["pattern"] = name; - this["replacement"] = name; - } - - [ConfigurationProperty("name", IsRequired = true)] - public string Name { - get { return (string)this["name"]; } - set { this["name"] = value; } - } - [ConfigurationProperty("pattern", IsRequired = true)] - public string Pattern - { - get { return (string)this["pattern"]; } - set { this["pattern"] = value; } - } - [ConfigurationProperty("replacement", IsRequired = true)] - public string Replacement - { - get { return (string)this["replacement"]; } - set { this["replacement"] = value; } - } - [ConfigurationProperty("type", IsRequired = false)] - public string Type - { - get { return (string)this["type"]; } - set { this["type"] = value; } - } - } -} diff --git a/tools/JavaToCSharp/Configuration/RuleSectionCollection.cs b/tools/JavaToCSharp/Configuration/RuleSectionCollection.cs deleted file mode 100644 index 0a484dbc7..000000000 --- a/tools/JavaToCSharp/Configuration/RuleSectionCollection.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Configuration; - -namespace JavaToCSharp.Configuration -{ - public class RuleSectionCollection : ConfigurationElementCollection - { - public void Add(RuleSection element) - { - BaseAdd(element); - } - - public void Remove(string name) - { - BaseRemove(name); - } - - public override ConfigurationElementCollectionType CollectionType - { - get - { - return ConfigurationElementCollectionType.AddRemoveClearMap; - } - } - protected override ConfigurationElement CreateNewElement() - { - return new RuleSection(); - } - protected override object GetElementKey(ConfigurationElement element) - { - return ((RuleSection)element).Name; - } - public new RuleSection this[string name] - { - get { return (RuleSection)BaseGet(name); } - } - } -} diff --git a/tools/JavaToCSharp/IRule.cs b/tools/JavaToCSharp/IRule.cs deleted file mode 100644 index 504132004..000000000 --- a/tools/JavaToCSharp/IRule.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Text; -using System.Text.RegularExpressions; - - -namespace JavaToCSharp -{ - public interface IRule - { - bool Execute(string strOrigin, out string strOutput, int iRowNumber); - } - - public abstract class Rule: IRule - { - #region IRuleExecutor Members - - public abstract bool Execute(string strOrigin, out string strOutput,int iRowNumber); - - public abstract string RuleName { get; } - - #endregion - } -} diff --git a/tools/JavaToCSharp/JavaToCSharp.csproj b/tools/JavaToCSharp/JavaToCSharp.csproj deleted file mode 100644 index 078416599..000000000 --- a/tools/JavaToCSharp/JavaToCSharp.csproj +++ /dev/null @@ -1,78 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {766E09AF-B872-444D-BD76-C8BB17E38DAC} - Exe - Properties - JavaToCSharp - JavaToCSharp - v3.0 - 512 - - - - - 3.5 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - - - \ No newline at end of file diff --git a/tools/JavaToCSharp/JavaToCSharp.csproj.user b/tools/JavaToCSharp/JavaToCSharp.csproj.user deleted file mode 100644 index c829caefd..000000000 --- a/tools/JavaToCSharp/JavaToCSharp.csproj.user +++ /dev/null @@ -1,9 +0,0 @@ - - - - ShowAllFiles - - - PackageHelper.java - - \ No newline at end of file diff --git a/tools/JavaToCSharp/JavaToCSharp.gpState b/tools/JavaToCSharp/JavaToCSharp.gpState deleted file mode 100644 index 159ffa107..000000000 --- a/tools/JavaToCSharp/JavaToCSharp.gpState +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/tools/JavaToCSharp/JavaToCSharp.sln b/tools/JavaToCSharp/JavaToCSharp.sln deleted file mode 100644 index 7e89443a4..000000000 --- a/tools/JavaToCSharp/JavaToCSharp.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaToCSharp", "JavaToCSharp.csproj", "{766E09AF-B872-444D-BD76-C8BB17E38DAC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {766E09AF-B872-444D-BD76-C8BB17E38DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {766E09AF-B872-444D-BD76-C8BB17E38DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {766E09AF-B872-444D-BD76-C8BB17E38DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {766E09AF-B872-444D-BD76-C8BB17E38DAC}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/tools/JavaToCSharp/Program.cs b/tools/JavaToCSharp/Program.cs deleted file mode 100644 index 519803b67..000000000 --- a/tools/JavaToCSharp/Program.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using JavaToCSharp.Configuration; -using System.Configuration; -using JavaToCSharp.Rules; - -namespace JavaToCSharp -{ - class Program - { - static void Main(string[] args) - { - if (args.Length == 0) - { - Console.WriteLine("Missing Argument - file name"); - return; - } - - RuleEngine re = new RuleEngine(); - re.LoadRules(); - re.Run(args[0]); - } - } -} diff --git a/tools/JavaToCSharp/Properties/AssemblyInfo.cs b/tools/JavaToCSharp/Properties/AssemblyInfo.cs deleted file mode 100644 index ef773070d..000000000 --- a/tools/JavaToCSharp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JavaToCSharp")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JavaToCSharp")] -[assembly: AssemblyCopyright("Copyright © 2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("73559d31-ce9e-4298-9c0a-93cc2dc4e7a3")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tools/JavaToCSharp/RuleEngine.cs b/tools/JavaToCSharp/RuleEngine.cs deleted file mode 100644 index 0b109db24..000000000 --- a/tools/JavaToCSharp/RuleEngine.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; -using System.Reflection; -using JavaToCSharp.Configuration; -using System.Configuration; -using JavaToCSharp.Rules; - -namespace JavaToCSharp -{ - public class RuleEngine - { - List rules = new List(); - - public void AddRule(Rule rule) - { - rules.Add(rule); - } - - /// - /// Loads the rules. - /// - public void LoadRules() - { - //load from app.config - J2CSSection config = - ConfigurationManager.GetSection( - "J2CS") as J2CSSection; - foreach (RuleSection rs in config.Rules) - { - EquivalentRule r = new EquivalentRule(rs.Name); - r.Pattern = rs.Pattern; - r.Replacement = rs.Replacement; - //Console.WriteLine(r.ToString()); - AddRule(r); - } - //load from assembly - Assembly asm = Assembly.GetExecutingAssembly(); - string nameSpace = "JavaToCSharp.Rules"; - foreach (Type type in asm.GetTypes()) - { - if (type.Namespace == nameSpace && !type.IsAbstract && type.Name != "EquivalentRule") - AddRule(Activator.CreateInstance(type) as Rule); - } - } - - protected static void Log(string message) - { - Console.WriteLine(message); - } - protected static void LogTip(string message) - { - Console.WriteLine(message); - } - - protected void LogExecute(string ruleName,int iRowNumber) - { - Log(string.Format("[Line {0}] {1}", iRowNumber, ruleName)); - } - - //protected void LogFind(string strOrigin, int iRowNumber) - //{ - // Log(string.Format("[{0}]", this.RuleName)); - // Log(string.Format("(Line: {0}): {1}", iRowNumber, strOrigin.Trim().Replace("\r", ""))); - //} - - //protected void LogAttention(string strOrigin, int iRowNumber) - //{ - // LogTip(string.Format("[{0}]", this.RuleName)); - // LogTip(string.Format("(Line: {0}): {1}", iRowNumber, strOrigin.Trim().Replace("\r", ""))); - //} - public void Run(string path) - { - //read from file - if (!File.Exists(path)) - { - Console.WriteLine("File not found"); - return; - } - StreamReader sr = new StreamReader(path, true); - string strOrigin = sr.ReadToEnd(); - sr.Close(); - - //run rules - StringBuilder sb = new StringBuilder(); - string[] arrInput = strOrigin.Split(new char[] { '\n' }); - for (int i = 0; i < arrInput.Length; i++) - { - string tmp = arrInput[i].Replace("\r", ""); - - int ruleNum = i + 1; - foreach (Rule rule in rules) - { - if (rule.Execute(tmp, out tmp, ruleNum)) - { - LogExecute(rule.RuleName, ruleNum); - } - } - sb.AppendLine(tmp); - } - - //save result to file - StreamWriter sw = new StreamWriter(path, false); - sw.Write(sb.ToString()); - sw.Close(); - } - - } -} diff --git a/tools/JavaToCSharp/Rules/CapitalizeRule.cs b/tools/JavaToCSharp/Rules/CapitalizeRule.cs deleted file mode 100644 index 5700fab29..000000000 --- a/tools/JavaToCSharp/Rules/CapitalizeRule.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - - -namespace JavaToCSharp.Rules -{ - public class CapitalizeRule : Rule - { - public override string RuleName - { - get { return "Capitalize Rule"; } - } - - const string pattern = @"(remove|add|apply|process|make|rename|change|run|equal|confirm|write|compare|separate|notify|contain|clone|ungroup|trim|isNaN|substring|classify|init|show|shift|replace|check|single|create|eval|append|convert|clear|fill|serialize|find|indexOf|lastIndexOf|resolve|parse|toString|coerce|lookup|valueOf|hook|unhook)\S+"; - - string CapitalizeString(Match matchString) - { - string strTemp = matchString.ToString(); - strTemp = char.ToUpper(strTemp[0]) + strTemp.Substring(1, strTemp.Length - 1); - return strTemp; - } - - - public override bool Execute(string strOrigin, out string strOutput, int iRowNumber) - { - Regex regex = new Regex(pattern); - string result = strOrigin; - bool changedFlag = false; - if (regex.IsMatch(strOrigin)) - { - result = regex.Replace(strOrigin, new MatchEvaluator(CapitalizeString)); - changedFlag = true; - } - strOutput = result; - return changedFlag; - } - } -} diff --git a/tools/JavaToCSharp/Rules/DeleteRule.cs b/tools/JavaToCSharp/Rules/DeleteRule.cs deleted file mode 100644 index fca26c788..000000000 --- a/tools/JavaToCSharp/Rules/DeleteRule.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace JavaToCSharp.Rules -{ - public class DeleteRule : Rule - { - public override string RuleName - { - get { return "Delete Keyword"; } - } - - const string pattern = @"\sfinal\s|\sthrows\s[a-zA-Z]+Exception|\.doubleValue\(\)"; - - public override bool Execute(string strOrigin, out string strOutput, int iRowNumber) - { - Regex regex = new Regex(pattern); - string result = strOrigin; - bool changedFlag = false; - if (regex.IsMatch(strOrigin)) - { - result = regex.Replace(strOrigin," "); - changedFlag = true; - } - strOutput = result; - return changedFlag; - } - } -} diff --git a/tools/JavaToCSharp/Rules/EquivalentExceptionRule.cs b/tools/JavaToCSharp/Rules/EquivalentExceptionRule.cs deleted file mode 100644 index c8c6293bc..000000000 --- a/tools/JavaToCSharp/Rules/EquivalentExceptionRule.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace JavaToCSharp.Rules -{ - public abstract class EquivalentExceptionRule : EquivalentRule - { - protected override sealed string Replacement - { - get { return this.Pattern; } - } - } - - - public class EquivalentRuleException1 : EquivalentExceptionRule - { - - protected override string Pattern - { - get { return @"RuntimeException"; } - } - - public override string ReplaceString(Match match) - { - return "Exception"; - } - } - - public class EquivalentRuleException2 : EquivalentExceptionRule - { - - protected override string Pattern - { - get { return @"ClassCastException"; } - } - - public override string ReplaceString(Match match) - { - return "InvalidCastException"; - } - } - - public class EquivalentRuleException3 : EquivalentExceptionRule - { - - protected override string Pattern - { - get { return @"UnsupportedEncodingException"; } - } - - public override string ReplaceString(Match match) - { - return "EncoderFallbackException"; - } - } - - public class EquivalentRuleException4 : EquivalentExceptionRule - { - - protected override string Pattern - { - get { return @"AssertionFailedError"; } - } - - public override string ReplaceString(Match match) - { - return "AssertFailedException"; - } - } - - public class EquivalentRuleException5 : EquivalentExceptionRule - { - - protected override string Pattern - { - get { return @"IllegalArgumentException"; } - } - - public override string ReplaceString(Match match) - { - return "ArgumentException"; - } - } - public class EquivalentRuleException6 : EquivalentExceptionRule - { - - protected override string Pattern - { - get { return @"NumberFormatException"; } - } - - public override string ReplaceString(Match match) - { - return "FormatException"; - } - } - public class EquivalentRuleException7 : EquivalentExceptionRule - { - - protected override string Pattern - { - get { return @"IllegalAccessError|IllegalStateException"; } - } - - public override string ReplaceString(Match match) - { - return "InvalidOperationException"; - } - } - -} diff --git a/tools/JavaToCSharp/Rules/EquivalentRule.cs b/tools/JavaToCSharp/Rules/EquivalentRule.cs deleted file mode 100644 index c77ed943e..000000000 --- a/tools/JavaToCSharp/Rules/EquivalentRule.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace JavaToCSharp.Rules -{ - public class EquivalentRule : Rule - { - public EquivalentRule() - { - - } - public EquivalentRule(string name) - { - this._name = name; - } - - string _name; - public override string RuleName - { - get { return _name; } - } - - public virtual string Replacement - { - get; - set; - } - - public virtual string Pattern - { - get; - set; - } - - protected virtual string ReplaceString(Match match) - { - return this.Replacement; - } - - public override sealed bool Execute(string strOrigin, out string strOutput, int iRowNumber) - { - Regex regex = new Regex(this.Pattern); - string result=strOrigin; - bool changedFlag = false; - - if (regex.IsMatch(strOrigin)) - { - result= regex.Replace(strOrigin, new MatchEvaluator(ReplaceString)); - changedFlag = true; - } - strOutput = result; - return changedFlag; - } - public override string ToString() - { - return string.Format("Equivalent Rule '{0}'", this.RuleName); - } - } -} diff --git a/tools/JavaToCSharp/Rules/OtherRule.cs b/tools/JavaToCSharp/Rules/OtherRule.cs deleted file mode 100644 index 78c7ab208..000000000 --- a/tools/JavaToCSharp/Rules/OtherRule.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace JavaToCSharp.Rules -{ - class NPOINamespaceRule:EquivalentRule - { - public override string RuleName - { - get - { - return "namespace"; - } - } - - public override string Pattern - { - get { return @"org\.apache\.poi\.([a-zA-Z]+)\."; } - } - - public override string Replacement - { - get { return "namespace"; } - } - protected override string ReplaceString(System.Text.RegularExpressions.Match match) - { - return "NPOI."+match.Groups[1].Value.ToUpper()+"."; - } - } - - public class EquivalentRule1 : EquivalentRule - { - public override string RuleName - { - get - { - return "new Double"; - } - } - - public override string Replacement - { - get { return "new Double"; } - } - - public override string Pattern - { - get { return @"new Double\((.+)\)"; } - } - - protected override string ReplaceString(Match match) - { - return match.Groups[1].Value; - } - } - public class EquivalentRule2 : EquivalentRule - { - public override string Replacement - { - get { return ".charAt(i)"; } - } - - public override string Pattern - { - get { return @"\.charAt\((\w+)\)"; } - } - - protected override string ReplaceString(Match match) - { - return string.Format("[{0}]", match.Groups[1].Value); - } - } - - - public class EquivalentRule3 : EquivalentRule - { - public override string Replacement - { - get { return "read"; } - } - - public override string Pattern - { - get { return @"(\s|\.)(read\S)"; } - } - - protected override string ReplaceString(Match match) - { - string strTemp = match.Groups[2].Value; - return match.Groups[1].Value + char.ToUpper(strTemp[0]) + strTemp.Substring(1, strTemp.Length - 1); - } - - - } -} diff --git a/tools/JavaToCSharp/Rules/RemoveGetRule.cs b/tools/JavaToCSharp/Rules/RemoveGetRule.cs deleted file mode 100644 index 30a92bd20..000000000 --- a/tools/JavaToCSharp/Rules/RemoveGetRule.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace JavaToCSharp.Rules -{ - public class RemoveGetRule : EquivalentRule - { - public override string Replacement - { - get { return "GetXXX()"; } - } - - public override string Pattern - { - get { return @"Get(FirstRow|Col|XFIndex|Data|LastRow|Column|Sid|Author|TotalSize|Row|FirstColumn|LastColumn|InnerValueEval|Height|Width|StringValue|ExternSheetIndex|Sheet|ColumnIndex|RowIndex)\(\)"; } - } - - protected override string ReplaceString(Match match) - { - return match.Groups[1].Value; - } - } -} diff --git a/tools/JavaToCSharp/Rules/foreachRule.cs b/tools/JavaToCSharp/Rules/foreachRule.cs deleted file mode 100644 index 527607350..000000000 --- a/tools/JavaToCSharp/Rules/foreachRule.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace JavaToCSharp.Rules -{ - public class foreachRule : Rule - { - public override bool Execute(string strOrigin, out string strOutput, int iRowNumber) - { - string trimmedStr= strOrigin.Trim(); - strOutput = strOrigin; - if (!trimmedStr.StartsWith("for(") && !trimmedStr.StartsWith("for (")) - { - return false; - } - if (strOutput.IndexOf(":", 4) < 0) - return false; - strOutput = strOutput.Replace("for(", "foreach("); - strOutput = strOutput.Replace("for (", "foreach ("); - strOutput = strOutput.Replace(" : ", " in "); - return true; - } - public override string RuleName - { - get { return "for(Type x : y)"; } - } - } -} diff --git a/tools/JavaToCSharp/Rules/getIndexerRule.cs b/tools/JavaToCSharp/Rules/getIndexerRule.cs deleted file mode 100644 index 4f61fcb3f..000000000 --- a/tools/JavaToCSharp/Rules/getIndexerRule.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace JavaToCSharp.Rules -{ - public class getIndexerRule : Rule - { - string ReplaceString(Match matchString) - { - string strTemp = matchString.Groups[1].Value; - return string.Format("[{0}]",strTemp); - } - - public override bool Execute(string strOrigin, out string strOutput, int iRowNumber) - { - strOutput = strOrigin; - Regex regex=new Regex(@"\.get\((\s+\w+\s+)\)",RegexOptions.IgnoreCase); - - bool changedFlag = false; - if (regex.IsMatch(strOrigin)) - { - strOutput = regex.Replace(strOrigin, new MatchEvaluator(ReplaceString)); - changedFlag = true; - } - return changedFlag; - } - public override string RuleName - { - get { return ".get(i)"; } - } - } -} diff --git a/tools/JavaToCSharp/Rules/skiplineRule.cs b/tools/JavaToCSharp/Rules/skiplineRule.cs deleted file mode 100644 index 8b9e9e37a..000000000 --- a/tools/JavaToCSharp/Rules/skiplineRule.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace JavaToCSharp.Rules -{ - public class skiplineRule:Rule - { - public override bool Execute(string strOrigin, out string strOutput, int iRowNumber) - { - Regex regex = new Regex(@"(import|using)\s(java\.)", RegexOptions.IgnoreCase); - - if (regex.IsMatch(strOrigin)) - { - strOutput = string.Empty; - return true; - } - strOutput = strOrigin; - return false; - } - public override string RuleName - { - get { return "Skip this line"; } - } - } -} diff --git a/tools/JavaToCSharp/batchrename.ps1 b/tools/JavaToCSharp/batchrename.ps1 deleted file mode 100644 index 21bae074f..000000000 --- a/tools/JavaToCSharp/batchrename.ps1 +++ /dev/null @@ -1 +0,0 @@ -dir a\*.java | foreach { move-item -literal $_ $_.Name.Replace(".java", ".cs")} \ No newline at end of file diff --git a/tools/JavaToCSharp/run.ps1 b/tools/JavaToCSharp/run.ps1 deleted file mode 100644 index 148279b47..000000000 --- a/tools/JavaToCSharp/run.ps1 +++ /dev/null @@ -1,5 +0,0 @@ -$folder="a"; -foreach ($file in Get-ChildItem $folder) -{ -.\JavaToCSharp "$folder\$file"; -} \ No newline at end of file diff --git a/tools/POIFS Browser/AboutDialog.Designer.cs b/tools/POIFS Browser/AboutDialog.Designer.cs deleted file mode 100644 index 70a7d083c..000000000 --- a/tools/POIFS Browser/AboutDialog.Designer.cs +++ /dev/null @@ -1,126 +0,0 @@ -namespace NPOI.Tools.POIFSBrowser -{ - partial class AboutDialog - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.flowLayoutPanel1 = new NPOI.Tools.POIFSBrowser.AboutDialog.EtchedBorderedPanel(); - this.okButton = new System.Windows.Forms.Button(); - this.pictureBox1 = new System.Windows.Forms.PictureBox(); - this.label1 = new System.Windows.Forms.Label(); - this.webSiteLink = new System.Windows.Forms.LinkLabel(); - this.flowLayoutPanel1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); - this.SuspendLayout(); - // - // flowLayoutPanel1 - // - this.flowLayoutPanel1.BackColor = System.Drawing.SystemColors.Control; - this.flowLayoutPanel1.Controls.Add(this.okButton); - this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; - this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 110); - this.flowLayoutPanel1.Name = "flowLayoutPanel1"; - this.flowLayoutPanel1.Size = new System.Drawing.Size(524, 30); - this.flowLayoutPanel1.TabIndex = 1; - // - // okButton - // - this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; - this.okButton.Location = new System.Drawing.Point(438, 3); - this.okButton.Name = "okButton"; - this.okButton.Size = new System.Drawing.Size(83, 25); - this.okButton.TabIndex = 0; - this.okButton.Text = "OK"; - this.okButton.UseVisualStyleBackColor = true; - // - // pictureBox1 - // - this.pictureBox1.Image = global::NPOI.Tools.POIFSBrowser.Properties.Resources.NET_h_rgb_2; - this.pictureBox1.Location = new System.Drawing.Point(184, 12); - this.pictureBox1.Name = "pictureBox1"; - this.pictureBox1.Size = new System.Drawing.Size(328, 81); - this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; - this.pictureBox1.TabIndex = 0; - this.pictureBox1.TabStop = false; - // - // label1 - // - this.label1.Location = new System.Drawing.Point(12, 9); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(166, 46); - this.label1.TabIndex = 2; - this.label1.Text = "Developed by using open source NPOI library which is available at:"; - // - // webSiteLink - // - this.webSiteLink.AutoSize = true; - this.webSiteLink.Location = new System.Drawing.Point(12, 55); - this.webSiteLink.Name = "webSiteLink"; - this.webSiteLink.Size = new System.Drawing.Size(170, 13); - this.webSiteLink.TabIndex = 3; - this.webSiteLink.TabStop = true; - this.webSiteLink.Text = "http://www.codeplex.com/NPOI"; - this.webSiteLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.webSiteLink_LinkClicked); - // - // AboutDialog - // - this.AcceptButton = this.okButton; - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.White; - this.ClientSize = new System.Drawing.Size(524, 140); - this.Controls.Add(this.webSiteLink); - this.Controls.Add(this.label1); - this.Controls.Add(this.flowLayoutPanel1); - this.Controls.Add(this.pictureBox1); - this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "AboutDialog"; - this.ShowIcon = false; - this.ShowInTaskbar = false; - this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "About POIFS Browser"; - this.flowLayoutPanel1.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.PictureBox pictureBox1; - private EtchedBorderedPanel flowLayoutPanel1; - private System.Windows.Forms.Button okButton; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.LinkLabel webSiteLink; - } -} \ No newline at end of file diff --git a/tools/POIFS Browser/AboutDialog.cs b/tools/POIFS Browser/AboutDialog.cs deleted file mode 100644 index 2267a81d7..000000000 --- a/tools/POIFS Browser/AboutDialog.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Windows.Forms; - -namespace NPOI.Tools.POIFSBrowser -{ - public partial class AboutDialog : Form - { - private static AboutDialog _instance; - public static AboutDialog Instance - { - get - { - if (_instance == null) - { - _instance = new AboutDialog(); - } - - return _instance; - } - } - - private AboutDialog() - { - InitializeComponent(); - } - - private void webSiteLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - System.Diagnostics.Process.Start(webSiteLink.Text); - } - - private class EtchedBorderedPanel : Panel - { - protected override void OnPaint(PaintEventArgs e) - { - base.OnPaint(e); - - ControlPaint.DrawBorder3D(e.Graphics, this.ClientRectangle, Border3DStyle.Etched, Border3DSide.Top); - } - } - } -} \ No newline at end of file diff --git a/tools/POIFS Browser/AboutDialog.resx b/tools/POIFS Browser/AboutDialog.resx deleted file mode 100644 index 19dc0dd8b..000000000 --- a/tools/POIFS Browser/AboutDialog.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/tools/POIFS Browser/AbstractTreeNode.cs b/tools/POIFS Browser/AbstractTreeNode.cs deleted file mode 100644 index cb2cb2147..000000000 --- a/tools/POIFS Browser/AbstractTreeNode.cs +++ /dev/null @@ -1,73 +0,0 @@ - -/* ==================================================================== - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==================================================================== */ - -/* ================================================================ - * POIFS Browser - * Author: NPOI Team - * HomePage: http://www.codeplex.com/npoi - * Contributors: - * Huseyin Tufekcilerli 2008.11 1.0 - * Tony Qu 2009.2.18 1.2 alpha - * - * ==============================================================*/ - -using System; -using System.Windows.Forms; -using NPOI.POIFS.FileSystem; - -namespace NPOI.Tools.POIFSBrowser -{ - internal abstract class AbstractTreeNode : TreeNode, IComparable - { - public EntryNode EntryNode { get; private set; } - - public AbstractTreeNode(EntryNode entryNode) - : base(entryNode.Name) - { - this.EntryNode = entryNode; - } - - protected void ChangeImage(string imageKey) - { - this.ImageKey = this.SelectedImageKey = imageKey; - } - - #region IComparable Members - - public int CompareTo(AbstractTreeNode other) - { - bool b1 = this is DirectoryTreeNode; - bool b2 = other is DirectoryTreeNode; - - if (b1 && !b2) - { - return -1; - } - else if (!b1 && b2) - { - return 1; - } - else - { - return this.Text.CompareTo(other.Text); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/tools/POIFS Browser/DirectoryTreeNode.cs b/tools/POIFS Browser/DirectoryTreeNode.cs deleted file mode 100644 index f2a517ccd..000000000 --- a/tools/POIFS Browser/DirectoryTreeNode.cs +++ /dev/null @@ -1,278 +0,0 @@ -/* ==================================================================== - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==================================================================== */ - -/* ================================================================ - * POIFS Browser - * Author: NPOI Team - * HomePage: http://www.codeplex.com/npoi - * Contributors: - * Huseyin Tufekcilerli 2008.11 1.0 - * Tony Qu 2009.2.18 1.2 alpha - * Tony Qu 2009.3.4 1.2 beta 1 - * - * ==============================================================*/ - -using System; -using System.Collections.Generic; -using System.Windows.Forms; -using System.Collections; - -using NPOI.POIFS.FileSystem; -using NPOI.HSSF.Record; -using NPOI.HSSF.Util; -using NPOI.HSSF.UserModel; -using NPOI.HSSF.Record.Aggregates; -using NPOI.SS.Util; -using NPOI.DDF; -using NPOI.HSSF.Record.Chart; - -namespace NPOI.Tools.POIFSBrowser -{ - internal class DirectoryTreeNode : AbstractTreeNode - { - public DirectoryNode DirectoryNode { get; private set; } - private bool _isExpanded; - - public DirectoryTreeNode(DirectoryNode dn) - : base(dn) - { - - this.DirectoryNode = dn; - - ChangeImage("Folder"); - - this.Nodes.Add(string.Empty); // Dummy node - } - - - internal void OnExpanded() - { - if (!_isExpanded) - { - this.TreeView.BeginUpdate(); - this.Nodes.Clear(); - this.Nodes.AddRange(GetChildren(this.DirectoryNode,null)); - - ChangeImage("FolderOpen"); - - this.TreeView.EndUpdate(); - - _isExpanded = true; - } - } - - internal void OnCollapsed() - { - ChangeImage("Folder"); - } - - internal static TreeNode[] GetChildren(DirectoryNode node,object innerDoc) - { - var children = new List(); - - var entries = node.Entries; - - while (entries.MoveNext()) - { - EntryNode entry = entries.Current as EntryNode; - AbstractTreeNode treeNode; - if (entry is DirectoryNode) - { - treeNode = new DirectoryTreeNode(entry as DirectoryNode); - - var o = entry as DirectoryNode; - } - else - { - var o = entry as DocumentNode; - - treeNode = new DocumentTreeNode(entry as DocumentNode); - - #region handle Excel BIFF records - - if (treeNode.Text.ToLower() == "workbook") - { - HandleWorkbook(treeNode, (HSSFWorkbook)innerDoc); - } - //else if(treeNode.Text.ToLower() == "worddocument") - //{ - // HandleWord(treeNode, (HWPFDocument)innerDoc); - //} - - #endregion - } - - children.Add(treeNode); - } - - children.Sort(); - - return children.ToArray(); - } - - //static void HandleWord(TreeNode treeNode, HWPFDocument hwpf) - //{ - // TreeNode paragraphsNode = CreateFolderNode(treeNode, "Paragraphs"); - // List papxs = hwpf.ParagraphTable.GetParagraphs(); - // foreach (PAPX papx in papxs) - // { - // TreeNode tn = new TreeNode("PAPX"); - // paragraphsNode.Nodes.Add(tn); - // } - // foreach(FieldsDocumentPart part in Enum.GetValues(typeof(FieldsDocumentPart))) - // { - // LoadDocumentPart(hwpf, treeNode, part.ToString(), part); - // } - // TreeNode docTextNode = new TreeNode("DocumentText"); - // docTextNode.Tag = hwpf.GetDocumentText(); - // treeNode.Nodes.Add(docTextNode); - //} - static TreeNode CreateFolderNode(TreeNode parent, string name) - { - TreeNode newnode = new TreeNode(name); - newnode.ImageKey = "Folder"; - newnode.SelectedImageKey = "Folder"; - parent.Nodes.Add(newnode); - return newnode; - } - //static void LoadDocumentPart(HWPFDocument hwpf,TreeNode parent,string partName,FieldsDocumentPart part) - //{ - - // TreeNode mainNode = new TreeNode(partName); - // mainNode.ImageKey = "Folder"; - // mainNode.SelectedImageKey = "Folder"; - // parent.Nodes.Add(mainNode); - // Fields fields = hwpf.GetFields(); - // foreach (Field field in fields.GetFields(part)) - // { - // TreeNode tn = new TreeNode("Field"); - // mainNode.Nodes.Add(tn); - // } - //} - - - static void HandleWorkbook(TreeNode treeNode,HSSFWorkbook hssfworkbook) - { - - if (hssfworkbook.NumberOfSheets > 0) - { - treeNode.ImageKey = "Folder"; - treeNode.SelectedImageKey = "Folder"; - for (int i = 0; i < hssfworkbook.NumberOfSheets; i++) - { - string sheettext = string.Format("Sheet {0}", i + 1); - TreeNode sheetnode = - treeNode.Nodes.Add(sheettext, sheettext, "Folder", "Folder"); - - HSSFSheet hssfsheet=((HSSFSheet)hssfworkbook.GetSheetAt(i)); - EscherAggregate ea = hssfsheet.DrawingEscherAggregate; - IEnumerator iterator1 = hssfsheet.Sheet.Records.GetEnumerator(); - int chartIndex = 1; - while (iterator1.MoveNext()) - { - if (iterator1.Current is BOFRecord) - { - BOFRecord bof = (BOFRecord)iterator1.Current; - if (bof.Type == BOFRecordType.Chart) - { - string chartTitle = string.Format("Chart {0}" , chartIndex); - TreeNode chartnode = sheetnode.Nodes.Add(chartTitle, chartTitle, "Folder", "Folder"); - chartnode.Nodes.Add(new RecordTreeNode(bof)); - while (iterator1.MoveNext()) - { - if (iterator1.Current is RecordAggregate) - { - RecordAggregate record = (RecordAggregate)iterator1.Current; - chartnode.Nodes.Add(new RecordAggregateTreeNode(record)); - } - else /*if(iterator1.Current is Record)*/ - { - chartnode.Nodes.Add(new RecordTreeNode((Record)iterator1.Current)); - } - if (iterator1.Current is EOFRecord) - break; - } - chartIndex++; - } - } - else if (iterator1.Current is Record) - { - Record record = (Record)iterator1.Current; - sheetnode.Nodes.Add(new RecordTreeNode(record)); - } - else if (iterator1.Current is RecordAggregate) - { - RecordAggregate record = (RecordAggregate)iterator1.Current; - sheetnode.Nodes.Add(new RecordAggregateTreeNode(record)); - } - } - //RecordTreeNode rtn = new DirectoryTreeNode(); - if (ea != null) - { - foreach (EscherRecord er in ea.EscherRecords) - { - sheetnode.Nodes.Add(new EscherRecordTreeNode(er)); - } - } - } - - } - else - { - treeNode.ImageKey = "Binary"; - } - IEnumerator iterator2 = hssfworkbook.Workbook.Records.GetEnumerator(); - while (iterator2.MoveNext()) - { - if (iterator2.Current is Record) //&& !(iterator2.Current is UnknownRecord)) - { - Record record = (Record)iterator2.Current; - if (record is DrawingGroupRecord) - { - hssfworkbook.GetAllPictures(); - } - treeNode.Nodes.Add(new RecordTreeNode(record)); - } - else if (iterator2.Current is RecordBase) - { - RecordBase record = (RecordBase)iterator2.Current; - treeNode.Nodes.Add(record.GetType().Name); - } - } - - - } - - private class RecordVisitor1 : RecordVisitor - { - private TreeNode node; - public RecordVisitor1(TreeNode psNode) - { - node = psNode; - } - #region RecordVisitor 成员 - - public void VisitRecord(Record r) - { - node.Nodes.Add(new RecordTreeNode(r)); - } - - #endregion - } - - } -} \ No newline at end of file diff --git a/tools/POIFS Browser/DocumentTreeNode.cs b/tools/POIFS Browser/DocumentTreeNode.cs deleted file mode 100644 index b11088d84..000000000 --- a/tools/POIFS Browser/DocumentTreeNode.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* ==================================================================== - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==================================================================== */ - -/* ================================================================ - * POIFS Browser - * Author: NPOI Team - * HomePage: http://www.codeplex.com/npoi - * Contributors: - * Huseyin Tufekcilerli 2008.11 1.0 - * Tony Qu 2009.2.18 1.2 alpha - * - * ==============================================================*/ - -using NPOI.POIFS.FileSystem; -using System.IO; -using NPOI.Util; - -namespace NPOI.Tools.POIFSBrowser -{ - internal class DocumentTreeNode : AbstractTreeNode - { - public DocumentNode DocumentNode { get; private set; } - - public DocumentTreeNode(DocumentNode documentNode) - : base(documentNode) - { - this.DocumentNode = documentNode; - - ChangeImage(documentNode.Name.EndsWith("SummaryInformation") ? "SummaryStream" - : "File"); - } - - public Stream GetDocumentStream() - { - var document = this.DocumentNode.Document; - - var dst = new byte[document.Size]; - - if (document.Size > 0) - { - document.Read(dst, 0); - } - - return new ByteArrayInputStream(dst); - } - } -} \ No newline at end of file diff --git a/tools/POIFS Browser/EscherPropertyTreeNode.cs b/tools/POIFS Browser/EscherPropertyTreeNode.cs deleted file mode 100644 index c1cc321ee..000000000 --- a/tools/POIFS Browser/EscherPropertyTreeNode.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using NPOI.DDF; - -namespace NPOI.Tools.POIFSBrowser -{ - public class EscherPropertyTreeNode:AbstractRecordTreeNode - { - - - public EscherPropertyTreeNode(EscherProperty ep) - { - this.Record = ep; - this.Text = ep.Name; - this.ImageKey = "Binary"; - } - - public override bool HasBinary - { - get { return true; } - } - - public override byte[] GetBytes() - { - - if (this.Record is EscherSimpleProperty) - { - EscherSimpleProperty esp = (EscherSimpleProperty)this.Record; - byte[] data = new byte[esp.PropertySize]; - esp.SerializeSimplePart(data, 0); - return data; - } - else if (this.Record is EscherComplexProperty) - { - EscherComplexProperty esp = (EscherComplexProperty)this.Record; - byte[] data = new byte[esp.PropertySize]; - esp.SerializeComplexPart(data, 0); - return data; - } - return null; - - } - } -} diff --git a/tools/POIFS Browser/EscherRecordTreeNode.cs b/tools/POIFS Browser/EscherRecordTreeNode.cs deleted file mode 100644 index c76dc11c0..000000000 --- a/tools/POIFS Browser/EscherRecordTreeNode.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Windows.Forms; - -using NPOI.DDF; - -namespace NPOI.Tools.POIFSBrowser -{ - public class EscherRecordTreeNode : AbstractRecordTreeNode - { - public EscherRecordTreeNode(EscherRecord record) - { - this.Record = record; - this.Text = record.GetType().Name; - if(record.ChildRecords.Count>0) - this.ImageKey = "Folder"; - else - this.ImageKey = "Binary"; - - if (record is AbstractEscherOptRecord) - { - AbstractEscherOptRecord rec = ((AbstractEscherOptRecord)record); - foreach (EscherProperty ep in rec.EscherProperties ) - { - EscherPropertyTreeNode cnode = new EscherPropertyTreeNode(ep); - this.Nodes.Add(cnode); - } - } - else - { - GetChildren(this); - } - } - - private void GetChildren(EscherRecordTreeNode node) - { - EscherRecord record=((EscherRecord)node.Record); - for (int i = 0; i < record.ChildRecords.Count; i++) - { - EscherRecordTreeNode cnode=new EscherRecordTreeNode((EscherRecord)record.ChildRecords[i]); - this.Nodes.Add(cnode); - } - } - - public override byte[] GetBytes() - { - EscherRecord record=(EscherRecord)this.Record; - byte[] bytes=new byte[record.RecordSize]; - record.Serialize(0, bytes); - return bytes; - } - - public override bool HasBinary - { - get { return true; } - } - } -} diff --git a/tools/POIFS Browser/HSSF/AbstractRecordTreeNode.cs b/tools/POIFS Browser/HSSF/AbstractRecordTreeNode.cs deleted file mode 100644 index 2667b5a98..000000000 --- a/tools/POIFS Browser/HSSF/AbstractRecordTreeNode.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections; -using System.Text; -using System.Windows.Forms; -using System.Reflection; - -using NPOI.HSSF.Record; -using NPOI.SS.Formula.PTG; - -namespace NPOI.Tools.POIFSBrowser -{ - public abstract class AbstractRecordTreeNode:TreeNode - { - public abstract bool HasBinary { get; } - - public object Record { get; protected set; } - - public virtual ListViewItem[] GetPropertyList() - { - ArrayList tmplist = new ArrayList(); - - PropertyInfo[] properties = Record.GetType().GetProperties(); - int n = 1; - foreach (PropertyInfo property in properties) - { - string propertyValueText = "(null)"; - string propertyValueType = ""; - try - { - object propertyValue = property.GetValue(this.Record, null); - - - if (propertyValue != null) - { - //parse Ptg array - if (propertyValue is Ptg[]) - { - propertyValueText = ""; - Ptg[] ptgs=(Ptg[])propertyValue; - for(int i=0;iEe1{EqTC11HCLezx$i>dh&J2dU+`QaC)^Qtq|8-Hho}OFPBl#-l*S{*e z8|XFVsddDqW>qz@M>=Pt46cj3S6N^OyL6{pGsh?+-kWj*od; edxbw|j=9{8yE8vW@-YE@$KdJe=d#Wzp$PyTdwXO6 diff --git a/tools/POIFS Browser/Images/File.png b/tools/POIFS Browser/Images/File.png deleted file mode 100644 index 345b657205541adcc126a40ce2fcc9edd579861e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP-3sA zi(`m||Juohe1{BpT&q?34jNhnD6I z0|m(x<}E9xT?$rNYQi+(ZT;-W$t)KZ=_txRlVE2siPi4sW6)P_n6`y+Nxqf-#`}y? zdS5-SU!3Uohkt=aA7g;=S$n2W-}7uY`aF}nayu+9?(e?q{CkSx*6jYY)*|3hH_#&t Mp00i_>zopr0I#EQ8~^|S diff --git a/tools/POIFS Browser/Images/Folder.png b/tools/POIFS Browser/Images/Folder.png deleted file mode 100644 index 53ae37acd94a6cc4a17ae513f96ebfa5f1d8060a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 439 zcmV;o0Z9IdP)Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUzHAzH4R5(w?k|A=#Fcd^{1Rp`L(6G=)&=GWl2!_?HVG#@zOIQpF z0u94z7$|6{8*KJla-(M4Nm1*WCC-2EujH&&|NeY_tn2RR@R-wK*ZiuSfN|`Am=Yy{ zLho=oytPjyGPo4a_kJ&F7qfd`n@KJ(V*ilbX!O9y-`NP)78oqzh3O4iYcL69Qxe>d zfLMSbgM;CiMm%c_dsPTnu@s=(D)5yX1->wZ!?8y1Dp;d)gDjBCOPd*~ya30pNrG(! zqi2DW2F01b!E)wYop2-^yh7Jj&`pyYP%wSQuj+H{0zS_$1w6k9n%6oB+;$d}wHIL7 zPI%z?{YIgFQL})VfkGRZ&vGOswW#{ hNL4@Q>A&<(25us<*5FG&f%pIb002ovPDHLkV1l>SvsC~9 diff --git a/tools/POIFS Browser/Images/FolderOpen.png b/tools/POIFS Browser/Images/FolderOpen.png deleted file mode 100644 index 96e930649a6c0d7aae7a412d6ab7ecd33b75f733..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 501 zcmVTlc91HQ4mGXz*I4ATOvos5xFj(kVJyv7vKxB z`GbkWW!F$~OcWG`!mVRgqSC9tamTgdA?D5OhEOp}tM1o*Z+G9WSEQ6U#GV|4{vQNo z`@T6Vo)#bePNd)I>ipH)H~#|duU)h^O*%d@tTq5<2m)?DiS+E(<@w9Y#bX2|0akMl z3~)MkR_pp=`EGFmK!5;36h%?&2y${(?ZC(Z@p|4p82_rZ`xHA^LIB5X-gDkGtjSbKgxOc5c+q_qyj|f(#yfcgNl6wlsr?0iLIQ zQ46GL&pyT&6hacM8{h8I?dE&$03`_bn%bOA+e~W@H@8WBg`qIqfr{mHMgaGYE`T^g r8oPlc6oV!Tp#TG1YDa?N(C_>QCF0%iZ|dzm00000NkvXXu0mjf*nHc= diff --git a/tools/POIFS Browser/Images/Open.png b/tools/POIFS Browser/Images/Open.png deleted file mode 100644 index 6cec6869a67199844a8ee2707df6cbf1f2e7fe9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 523 zcmV+m0`&cfP)Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUziAh93R5(w)ldW#UFcgLJ1`QmmSz!am7BrRBaI9e~OUR_MnoKII z*~((Ft*nNHfrW;HfrEa7vwIycYPVKX$s&D{mH7NzTS=1tc$P19uC#$#5gUu;x>i@^ z77LB(0_sQ;cE$vq0C%d<`zLZiv)l$win1qF`Gw|L9;0^F;V2#G^ zeR;O(x41K)jh;luVm81>1w-G=Ms;Wf&8#Y%Gz$hSh{>H;5ye@48E{H4h%{D%>CeO{ z8Yi}hO+uShY{T_@tAjy%pv`n9XsN*^Xfz(rZ6(Ut)FizDnXtIolR?;YG=ihGlfXg3 z?{l;!Bk`RlgDl}Xo@PEtchMjS2^Kba5PO?J%>`!ngYKGxf$OTfCZ-<=(R8US^xF(> zm7{Fw#H3CkjA$>d=+Wso}TKFoRrGwE)Pebd5(yZz*3jtk; zE`2Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUzU`a$lR5(wylc8?IFc60O2J0IvaBP8NF&qpW^c&1*Dx2BLY8ZHA zQ&w0yu$^?W66O2uKUta0{-f6Kt9Jdd^1gTQF2OD|QcB4D95B$_Oz}1mbC)oB4SG!^|M<@)7o%Q$Yzs{g{Ay zzE_T!8FqZSZrT*mNrH)>tZ-qnz%*PDND@gPvCk!N{(`{C3Rflz?D|Ur3T3z7_MiW} YUs9+NXJ@7rhX4Qo07*qoM6N<$g3_MDPx#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUza7jc#R5(wqQ=xJKF%;wz`U!l(Nn|845*gLOH4+(#Jc*1%Mj|6| zJy{u98Hsz{eFt2)(wg?o>@ai5?%RDYEEfM!=~P9Lt~geS5jYg-`z!WELI8Gyc)|~I z?@eOPg3I!2)r*MTWO19M_%5)oKMIgsYZYU&be)4xBM@Pk4ruXN-N@y$N@o*d6tOl= z4w#n$9tZPk$-@T()=Bd}3Sd|&fP=Q?-oe#21xWV0 zUJA(EzYjgWkr>C{RqFuxG&eFYIk3)PpjnagAoPx#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUznMp)JR5(wilc93MFc3ujDftA$Lcu^mvj~<|pdc)Qfr4Qb469&S z1PcWV1q%hsuDjf0L3Aw9ob!pCL zk$CL#*Xds(b^>6^s$YN*n)>rwQI)b1e)rzR*dpWT09peOnF;)U2K+jx46n3L0ALKb zXKh*~SOC7NH-sSRoKeL}N=emPQ-F2l4gihP1XsY4fJUD379}Su?W2lkvr*7as|4&c z0q${5pob0Hj*=sc)k|+PguO|phP!J3#za6mb%juzOgfhta0%6` zx31kUD@c3yfMWFC$>s252Zzz56UGNKmx+MU80tQ4WcOTGL80+5Y`>@+xEdK`AlN)x^Hi}tff;bmZBx@2Hsmt?j d{O`v+d;_QNOmAmJ^y2^k002ovPDHLkV1h{R;eG%B diff --git a/tools/POIFS Browser/Images/SummaryStream.png b/tools/POIFS Browser/Images/SummaryStream.png deleted file mode 100644 index 1f12b96675c4bbf28f41dc5234f238973153e8ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 332 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP~w!Q zi(`m||JuolLQIA{uHQXmnv)Ae`T{zYh1^v)70FIL;gzDQ_e9)kv-B*njWd5vE>o}c zw7Qvj>(U?HO=3S!JYl}9`r&6)`Q0jyZD%Gvzx(e(`vq|Uq0Q5*EBT7wIA72@!|lM$ zP!e!ehP7Diz~QjE!p0j6oD2-2(fhayE-^BBFbF89ypOWkcXB8Hgz0(>v#xxwOs{4M zVLG6A@q&bc=$${AzuhGgZ#c3tEGn5&{foW9!TY6U1#?J*r_h8gQ+Bdf-JUjk`T5y@ bkL1fPzj;|^?*0!zUom*P`njxgN@xNARPcDH diff --git a/tools/POIFS Browser/MainForm.Designer.cs b/tools/POIFS Browser/MainForm.Designer.cs deleted file mode 100644 index 72c5e8853..000000000 --- a/tools/POIFS Browser/MainForm.Designer.cs +++ /dev/null @@ -1,406 +0,0 @@ -namespace NPOI.Tools.POIFSBrowser -{ - partial class MainForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); - this.menuStrip = new System.Windows.Forms.MenuStrip(); - this.fileMenu = new System.Windows.Forms.ToolStripMenuItem(); - this.fileOpenMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.fileMenSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - this.fileExitMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.aboutMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.statusStrip = new System.Windows.Forms.StatusStrip(); - this.toolStrip = new System.Windows.Forms.ToolStrip(); - this.openToolStripButton = new System.Windows.Forms.ToolStripButton(); - this.refreshToolScripButton = new System.Windows.Forms.ToolStripButton(); - this.mainSplitContainer = new System.Windows.Forms.SplitContainer(); - this.documentsGroupBox = new System.Windows.Forms.GroupBox(); - this.documentTreeView = new System.Windows.Forms.TreeView(); - this.documentTreeContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); - this.saveAsMenu = new System.Windows.Forms.ToolStripMenuItem(); - this.iconsImageList = new System.Windows.Forms.ImageList(this.components); - this.streamGroupBox = new System.Windows.Forms.GroupBox(); - this.streamTabControl = new System.Windows.Forms.TabControl(); - this.tabPageBinary = new System.Windows.Forms.TabPage(); - this.viewableTextBox = new System.Windows.Forms.TextBox(); - this.tabPageProperties = new System.Windows.Forms.TabPage(); - this.propertiesListView = new System.Windows.Forms.ListView(); - this.columnId = new System.Windows.Forms.ColumnHeader(); - this.columnName = new System.Windows.Forms.ColumnHeader(); - this.columnType = new System.Windows.Forms.ColumnHeader(); - this.columnValue = new System.Windows.Forms.ColumnHeader(); - this.menuStrip.SuspendLayout(); - this.toolStrip.SuspendLayout(); - this.mainSplitContainer.Panel1.SuspendLayout(); - this.mainSplitContainer.Panel2.SuspendLayout(); - this.mainSplitContainer.SuspendLayout(); - this.documentsGroupBox.SuspendLayout(); - this.documentTreeContextMenu.SuspendLayout(); - this.streamGroupBox.SuspendLayout(); - this.streamTabControl.SuspendLayout(); - this.tabPageBinary.SuspendLayout(); - this.tabPageProperties.SuspendLayout(); - this.SuspendLayout(); - // - // menuStrip - // - this.menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.fileMenu, - this.helpToolStripMenuItem}); - this.menuStrip.Location = new System.Drawing.Point(0, 0); - this.menuStrip.Name = "menuStrip"; - this.menuStrip.Size = new System.Drawing.Size(784, 25); - this.menuStrip.TabIndex = 0; - this.menuStrip.Text = "menuStrip1"; - // - // fileMenu - // - this.fileMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.fileOpenMenuItem, - this.fileMenSeparator1, - this.fileExitMenuItem}); - this.fileMenu.Name = "fileMenu"; - this.fileMenu.Size = new System.Drawing.Size(39, 21); - this.fileMenu.Text = "&File"; - // - // fileOpenMenuItem - // - this.fileOpenMenuItem.Image = global::NPOI.Tools.POIFSBrowser.Properties.Resources.Open; - this.fileOpenMenuItem.Name = "fileOpenMenuItem"; - this.fileOpenMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); - this.fileOpenMenuItem.Size = new System.Drawing.Size(164, 22); - this.fileOpenMenuItem.Text = "&Open..."; - this.fileOpenMenuItem.Click += new System.EventHandler(this.Open_Click); - // - // fileMenSeparator1 - // - this.fileMenSeparator1.Name = "fileMenSeparator1"; - this.fileMenSeparator1.Size = new System.Drawing.Size(161, 6); - // - // fileExitMenuItem - // - this.fileExitMenuItem.Name = "fileExitMenuItem"; - this.fileExitMenuItem.Size = new System.Drawing.Size(164, 22); - this.fileExitMenuItem.Text = "E&xit"; - this.fileExitMenuItem.Click += new System.EventHandler(this.fileExitMenuItem_Click); - // - // helpToolStripMenuItem - // - this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.aboutMenuItem}); - this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; - this.helpToolStripMenuItem.Size = new System.Drawing.Size(47, 21); - this.helpToolStripMenuItem.Text = "&Help"; - // - // aboutMenuItem - // - this.aboutMenuItem.Name = "aboutMenuItem"; - this.aboutMenuItem.Size = new System.Drawing.Size(120, 22); - this.aboutMenuItem.Text = "&About..."; - this.aboutMenuItem.Click += new System.EventHandler(this.aboutMenuItem_Click); - // - // statusStrip - // - this.statusStrip.Location = new System.Drawing.Point(0, 542); - this.statusStrip.Name = "statusStrip"; - this.statusStrip.Size = new System.Drawing.Size(784, 22); - this.statusStrip.TabIndex = 1; - this.statusStrip.Text = "statusStrip1"; - // - // toolStrip - // - this.toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.openToolStripButton, - this.refreshToolScripButton}); - this.toolStrip.Location = new System.Drawing.Point(0, 25); - this.toolStrip.Name = "toolStrip"; - this.toolStrip.Size = new System.Drawing.Size(784, 25); - this.toolStrip.TabIndex = 2; - this.toolStrip.Text = "toolStrip1"; - // - // openToolStripButton - // - this.openToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this.openToolStripButton.Image = global::NPOI.Tools.POIFSBrowser.Properties.Resources.Open; - this.openToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; - this.openToolStripButton.Name = "openToolStripButton"; - this.openToolStripButton.Size = new System.Drawing.Size(23, 22); - this.openToolStripButton.Text = "Open File"; - this.openToolStripButton.ToolTipText = "Open Document"; - this.openToolStripButton.Click += new System.EventHandler(this.Open_Click); - // - // refreshToolScripButton - // - this.refreshToolScripButton.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; - this.refreshToolScripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this.refreshToolScripButton.Image = global::NPOI.Tools.POIFSBrowser.Properties.Resources.arrow_refresh; - this.refreshToolScripButton.ImageTransparentColor = System.Drawing.Color.Magenta; - this.refreshToolScripButton.Name = "refreshToolScripButton"; - this.refreshToolScripButton.Size = new System.Drawing.Size(23, 22); - this.refreshToolScripButton.Text = "Refresh File"; - this.refreshToolScripButton.Click += new System.EventHandler(this.refreshToolScripButton_Click); - // - // mainSplitContainer - // - this.mainSplitContainer.Dock = System.Windows.Forms.DockStyle.Fill; - this.mainSplitContainer.Location = new System.Drawing.Point(0, 50); - this.mainSplitContainer.Name = "mainSplitContainer"; - // - // mainSplitContainer.Panel1 - // - this.mainSplitContainer.Panel1.Controls.Add(this.documentsGroupBox); - this.mainSplitContainer.Panel1.Padding = new System.Windows.Forms.Padding(3, 0, 0, 0); - // - // mainSplitContainer.Panel2 - // - this.mainSplitContainer.Panel2.Controls.Add(this.streamGroupBox); - this.mainSplitContainer.Panel2.Padding = new System.Windows.Forms.Padding(0, 0, 3, 0); - this.mainSplitContainer.Size = new System.Drawing.Size(784, 492); - this.mainSplitContainer.SplitterDistance = 261; - this.mainSplitContainer.TabIndex = 3; - // - // documentsGroupBox - // - this.documentsGroupBox.Controls.Add(this.documentTreeView); - this.documentsGroupBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.documentsGroupBox.Location = new System.Drawing.Point(3, 0); - this.documentsGroupBox.Name = "documentsGroupBox"; - this.documentsGroupBox.Padding = new System.Windows.Forms.Padding(3, 3, 3, 6); - this.documentsGroupBox.Size = new System.Drawing.Size(258, 492); - this.documentsGroupBox.TabIndex = 1; - this.documentsGroupBox.TabStop = false; - this.documentsGroupBox.Text = "Documents"; - // - // documentTreeView - // - this.documentTreeView.ContextMenuStrip = this.documentTreeContextMenu; - this.documentTreeView.Dock = System.Windows.Forms.DockStyle.Fill; - this.documentTreeView.ImageIndex = 0; - this.documentTreeView.ImageList = this.iconsImageList; - this.documentTreeView.Location = new System.Drawing.Point(3, 17); - this.documentTreeView.Name = "documentTreeView"; - this.documentTreeView.SelectedImageIndex = 0; - this.documentTreeView.Size = new System.Drawing.Size(252, 469); - this.documentTreeView.TabIndex = 0; - this.documentTreeView.AfterCollapse += new System.Windows.Forms.TreeViewEventHandler(this.documentTreeView_AfterCollapse); - this.documentTreeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.documentTreeView_AfterSelect); - this.documentTreeView.AfterExpand += new System.Windows.Forms.TreeViewEventHandler(this.documentTreeView_AfterExpand); - // - // documentTreeContextMenu - // - this.documentTreeContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.saveAsMenu}); - this.documentTreeContextMenu.Name = "documentTreeContextMenu"; - this.documentTreeContextMenu.Size = new System.Drawing.Size(130, 26); - // - // saveAsMenu - // - this.saveAsMenu.Image = global::NPOI.Tools.POIFSBrowser.Properties.Resources.SaveAs; - this.saveAsMenu.Name = "saveAsMenu"; - this.saveAsMenu.Size = new System.Drawing.Size(129, 22); - this.saveAsMenu.Text = "Save as..."; - this.saveAsMenu.Click += new System.EventHandler(this.saveAsMenu_Click); - // - // iconsImageList - // - this.iconsImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("iconsImageList.ImageStream"))); - this.iconsImageList.TransparentColor = System.Drawing.Color.Transparent; - this.iconsImageList.Images.SetKeyName(0, "File"); - this.iconsImageList.Images.SetKeyName(1, "Folder"); - this.iconsImageList.Images.SetKeyName(2, "FolderOpen"); - this.iconsImageList.Images.SetKeyName(3, "Property"); - this.iconsImageList.Images.SetKeyName(4, "Binary"); - this.iconsImageList.Images.SetKeyName(5, "SummaryStream"); - // - // streamGroupBox - // - this.streamGroupBox.Controls.Add(this.streamTabControl); - this.streamGroupBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.streamGroupBox.Location = new System.Drawing.Point(0, 0); - this.streamGroupBox.Name = "streamGroupBox"; - this.streamGroupBox.Padding = new System.Windows.Forms.Padding(3, 3, 3, 6); - this.streamGroupBox.Size = new System.Drawing.Size(516, 492); - this.streamGroupBox.TabIndex = 1; - this.streamGroupBox.TabStop = false; - this.streamGroupBox.Text = "Stream"; - // - // streamTabControl - // - this.streamTabControl.Controls.Add(this.tabPageBinary); - this.streamTabControl.Controls.Add(this.tabPageProperties); - this.streamTabControl.Dock = System.Windows.Forms.DockStyle.Fill; - this.streamTabControl.ImageList = this.iconsImageList; - this.streamTabControl.Location = new System.Drawing.Point(3, 17); - this.streamTabControl.Name = "streamTabControl"; - this.streamTabControl.SelectedIndex = 0; - this.streamTabControl.Size = new System.Drawing.Size(510, 469); - this.streamTabControl.TabIndex = 2; - // - // tabPageBinary - // - this.tabPageBinary.Controls.Add(this.viewableTextBox); - this.tabPageBinary.ImageKey = "Binary"; - this.tabPageBinary.Location = new System.Drawing.Point(4, 23); - this.tabPageBinary.Name = "tabPageBinary"; - this.tabPageBinary.Padding = new System.Windows.Forms.Padding(3); - this.tabPageBinary.Size = new System.Drawing.Size(502, 442); - this.tabPageBinary.TabIndex = 0; - this.tabPageBinary.Text = "Binary"; - this.tabPageBinary.UseVisualStyleBackColor = true; - // - // viewableTextBox - // - this.viewableTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.viewableTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); - this.viewableTextBox.Location = new System.Drawing.Point(3, 3); - this.viewableTextBox.Multiline = true; - this.viewableTextBox.Name = "viewableTextBox"; - this.viewableTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.viewableTextBox.Size = new System.Drawing.Size(496, 436); - this.viewableTextBox.TabIndex = 1; - this.viewableTextBox.WordWrap = false; - // - // tabPageProperties - // - this.tabPageProperties.Controls.Add(this.propertiesListView); - this.tabPageProperties.ImageKey = "Property"; - this.tabPageProperties.Location = new System.Drawing.Point(4, 23); - this.tabPageProperties.Name = "tabPageProperties"; - this.tabPageProperties.Padding = new System.Windows.Forms.Padding(3); - this.tabPageProperties.Size = new System.Drawing.Size(502, 442); - this.tabPageProperties.TabIndex = 1; - this.tabPageProperties.Text = "Properties"; - this.tabPageProperties.UseVisualStyleBackColor = true; - // - // propertiesListView - // - this.propertiesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.columnId, - this.columnName, - this.columnType, - this.columnValue}); - this.propertiesListView.Dock = System.Windows.Forms.DockStyle.Fill; - this.propertiesListView.FullRowSelect = true; - this.propertiesListView.GridLines = true; - this.propertiesListView.Location = new System.Drawing.Point(3, 3); - this.propertiesListView.Name = "propertiesListView"; - this.propertiesListView.ShowItemToolTips = true; - this.propertiesListView.Size = new System.Drawing.Size(496, 436); - this.propertiesListView.SmallImageList = this.iconsImageList; - this.propertiesListView.TabIndex = 0; - this.propertiesListView.UseCompatibleStateImageBehavior = false; - this.propertiesListView.View = System.Windows.Forms.View.Details; - // - // columnId - // - this.columnId.Text = "ID"; - this.columnId.Width = 49; - // - // columnName - // - this.columnName.Text = "Name"; - this.columnName.Width = 150; - // - // columnType - // - this.columnType.Text = "Type"; - this.columnType.Width = 59; - // - // columnValue - // - this.columnValue.Text = "Value"; - this.columnValue.Width = 225; - // - // MainForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(784, 564); - this.Controls.Add(this.mainSplitContainer); - this.Controls.Add(this.toolStrip); - this.Controls.Add(this.statusStrip); - this.Controls.Add(this.menuStrip); - this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); - this.MainMenuStrip = this.menuStrip; - this.Name = "MainForm"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "POIFS Browser"; - this.menuStrip.ResumeLayout(false); - this.menuStrip.PerformLayout(); - this.toolStrip.ResumeLayout(false); - this.toolStrip.PerformLayout(); - this.mainSplitContainer.Panel1.ResumeLayout(false); - this.mainSplitContainer.Panel2.ResumeLayout(false); - this.mainSplitContainer.ResumeLayout(false); - this.documentsGroupBox.ResumeLayout(false); - this.documentTreeContextMenu.ResumeLayout(false); - this.streamGroupBox.ResumeLayout(false); - this.streamTabControl.ResumeLayout(false); - this.tabPageBinary.ResumeLayout(false); - this.tabPageBinary.PerformLayout(); - this.tabPageProperties.ResumeLayout(false); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.MenuStrip menuStrip; - private System.Windows.Forms.ToolStripMenuItem fileMenu; - private System.Windows.Forms.StatusStrip statusStrip; - private System.Windows.Forms.ToolStrip toolStrip; - private System.Windows.Forms.SplitContainer mainSplitContainer; - private System.Windows.Forms.TreeView documentTreeView; - private System.Windows.Forms.ImageList iconsImageList; - private System.Windows.Forms.ToolStripMenuItem fileOpenMenuItem; - private System.Windows.Forms.ToolStripButton openToolStripButton; - private System.Windows.Forms.ListView propertiesListView; - private System.Windows.Forms.ColumnHeader columnId; - private System.Windows.Forms.ColumnHeader columnName; - private System.Windows.Forms.ColumnHeader columnType; - private System.Windows.Forms.ColumnHeader columnValue; - private System.Windows.Forms.GroupBox streamGroupBox; - private System.Windows.Forms.GroupBox documentsGroupBox; - private System.Windows.Forms.TextBox viewableTextBox; - private System.Windows.Forms.ContextMenuStrip documentTreeContextMenu; - private System.Windows.Forms.ToolStripMenuItem saveAsMenu; - private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem aboutMenuItem; - private System.Windows.Forms.TabControl streamTabControl; - private System.Windows.Forms.TabPage tabPageBinary; - private System.Windows.Forms.TabPage tabPageProperties; - private System.Windows.Forms.ToolStripSeparator fileMenSeparator1; - private System.Windows.Forms.ToolStripMenuItem fileExitMenuItem; - private System.Windows.Forms.ToolStripButton refreshToolScripButton; - } -} - diff --git a/tools/POIFS Browser/MainForm.cs b/tools/POIFS Browser/MainForm.cs deleted file mode 100644 index f4cf2665c..000000000 --- a/tools/POIFS Browser/MainForm.cs +++ /dev/null @@ -1,278 +0,0 @@ -/* ==================================================================== - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==================================================================== */ - -/* ================================================================ - * POIFS Browser - * Author: NPOI Team - * HomePage: http://www.codeplex.com/npoi - * Contributors: - * Huseyin Tufekcilerli 2008.11 1.0 - * Tony Qu 2009.2.18 1.2 alpha - * - * ==============================================================*/ - -using System; -using System.IO; -using System.Collections; -using System.Windows.Forms; - -using NPOI.HPSF; -using NPOI.POIFS.FileSystem; -using NPOI.HSSF.UserModel; -using NPOI.HSSF.Record; - -namespace NPOI.Tools.POIFSBrowser -{ - public partial class MainForm : Form - { - private POIFSFileSystem _currentFileSystem; - - - public MainForm() - { - InitializeComponent(); - } - - private void OpenDocument(string path) - { - HSSFWorkbook hssfworkbook = null; - //HWPFDocument hwpf = null; - - using (var stream = File.OpenRead(path)) - { - try - { - _currentFileSystem = new POIFSFileSystem(stream); - //supposing every Excel file has .xls as the extension - if (path.ToLower().IndexOf(".xls")>0) - { - hssfworkbook = new HSSFWorkbook(_currentFileSystem); - } - //else if (path.ToLower().IndexOf(".doc") > 0) - //{ - // hwpf =new HWPFDocument(_currentFileSystem); - //} - } - catch (Exception) - { - MessageBox.Show("Error opening file. Possibly the file is not an OLE2 Compund file.", - "Open File Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - - if (_currentFileSystem != null) - { - this.Text = string.Format("POIFS Browser - {0}", path); - documentTreeView.BeginUpdate(); - documentTreeView.Nodes.Clear(); - - TreeNode[] children=null; - if (hssfworkbook != null) - { - children = DirectoryTreeNode.GetChildren(_currentFileSystem.Root, hssfworkbook); - } - else - { - children = DirectoryTreeNode.GetChildren(_currentFileSystem.Root, null); - } - documentTreeView.Nodes.AddRange(children); - documentTreeView.EndUpdate(); - - } - } - - private void documentTreeView_AfterExpand(object sender, TreeViewEventArgs e) - { - var directoryTreeNode = e.Node as DirectoryTreeNode; - - if (directoryTreeNode != null) - { - directoryTreeNode.OnExpanded(); - } - } - - private void documentTreeView_AfterCollapse(object sender, TreeViewEventArgs e) - { - var directoryTreeNode = e.Node as DirectoryTreeNode; - - if (directoryTreeNode != null) - { - directoryTreeNode.OnCollapsed(); - } - } - - private void documentTreeView_AfterSelect(object sender, TreeViewEventArgs e) - { - ClearViewControls(); - - if (e.Node is DocumentTreeNode) - { - var documentTreeNode = e.Node as DocumentTreeNode; - var stream = documentTreeNode.GetDocumentStream(); - - var viewableArray = documentTreeNode.DocumentNode.Document.ViewableArray; - if (viewableArray.Length > 0) - { - viewableTextBox.Text = viewableArray.GetValue(0).ToString(); - } - - if (PropertySet.IsPropertySetStream(stream)) - { - var ps = PropertySetFactory.Create(stream); - - propertiesListView.Items.AddRange(PropertyListViewItem.Create(ps)); - - if (!streamTabControl.TabPages.ContainsKey("tabPageProperties")) - { - streamTabControl.TabPages.Add(tabPageProperties); - } - } - else - { - if (streamTabControl.TabPages.ContainsKey("tabPageProperties")) - { - streamTabControl.TabPages.Remove(tabPageProperties); - } - } - - if (!streamTabControl.TabPages.ContainsKey("tabPageBinary")) - { - streamTabControl.TabPages.Add(tabPageBinary); - } - - } - else if (e.Node is AbstractRecordTreeNode) - { - AbstractRecordTreeNode node = (AbstractRecordTreeNode)e.Node; - - if (node.HasBinary) - { - byte[] buffer = node.GetBytes(); - viewableTextBox.Text = NPOI.Util.HexDump.Dump(buffer, buffer.Length, 0); - if (!streamTabControl.TabPages.ContainsKey("tabPageBinary")) - { - streamTabControl.TabPages.Add(tabPageBinary); - } - } - else - { - if (streamTabControl.TabPages.ContainsKey("tabPageBinary")) - { - streamTabControl.TabPages.Remove(tabPageBinary); - } - } - if (!streamTabControl.TabPages.ContainsKey("tabPageProperties")) - { - streamTabControl.TabPages.Add(tabPageProperties); - } - propertiesListView.Items.AddRange(node.GetPropertyList()); - - if (!streamTabControl.TabPages.ContainsKey("tabPageBinary")) - { - streamTabControl.TabPages.Add(tabPageBinary); - } - } - else - { - if (!streamTabControl.TabPages.ContainsKey("tabPageProperties")) - { - streamTabControl.TabPages.Add(tabPageProperties); - } - } - - } - - private void ClearViewControls() - { - viewableTextBox.Clear(); - propertiesListView.Items.Clear(); - } - - private string filename; - private void Open_Click(object sender, EventArgs e) - { - var dialog = new OpenFileDialog() - { - Filter = "Microsoft Office 97-2003 Documents|*.xls;*.doc;*.ppt|POIFS File|*.poifs|All files (*.*)|*.*", - Multiselect = false, - Title = "Open OLE2 Compund Document", - }; - - var result = dialog.ShowDialog(this); - - if (result == DialogResult.OK) - { - filename = dialog.FileName; - OpenDocument(filename); - - ClearViewControls(); - } - } - - private void saveAsMenu_Click(object sender, EventArgs e) - { - - var documentTreeNode = documentTreeView.SelectedNode as DocumentTreeNode; - var directoryTreeNode = documentTreeView.SelectedNode as DirectoryTreeNode; - if (documentTreeNode != null) - { - var dialog = new SaveFileDialog() - { - Filter = "All files|*.*", - Title = "Save Document Stream As" - }; - - var result = dialog.ShowDialog(this); - - if (result == DialogResult.OK) - { - using (var stream = documentTreeNode.GetDocumentStream()) - using (var fileStream = File.OpenWrite(dialog.FileName)) - { - var bufferLength = 4096; - var buffer = new byte[bufferLength]; - int bytesRead; - - while ((bytesRead = stream.Read(buffer, 0, bufferLength)) > 0) - { - fileStream.Write(buffer, 0, bytesRead); - } - } - } - } - } - - private void aboutMenuItem_Click(object sender, EventArgs e) - { - AboutDialog.Instance.ShowDialog(this); - } - - private void fileExitMenuItem_Click(object sender, EventArgs e) - { - Application.Exit(); - } - - private void refreshToolScripButton_Click(object sender, EventArgs e) - { - if (string.IsNullOrEmpty(filename)) - return; - - OpenDocument(filename); - ClearViewControls(); - } - } -} diff --git a/tools/POIFS Browser/MainForm.resx b/tools/POIFS Browser/MainForm.resx deleted file mode 100644 index 2904e19a3..000000000 --- a/tools/POIFS Browser/MainForm.resx +++ /dev/null @@ -1,256 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - - 132, 17 - - - 248, 17 - - - 485, 17 - - - 353, 17 - - - - AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w - LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 - ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACk - GgAAAk1TRnQBSQFMAgEBBgEAAQwBAAEEAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA - AwABIAMAAQEBAAEgBgABIP8AGwABsAGgAZAB/wFeAUYBLgH/AV4BRgEuAf8BXgFGAS4B/wFeAUYBLgH/ - AV4BRgEuAf8BXgFGAS4B/wFeAUYBLgH/AV4BRgEuAf8BXgFGAS4B/wFeAUYBLgH/GAABsAGYAZAB/wFe - AUYBLgH/AV4BRgEuAf8BXgFGAS4B/wFeAUYBLgH/AV4BRgEuAf8BXgFGAS4B/wFeAUYBLgH/AV4BRgEu - Af8BXgFGAS4B/wFeAUYBLgH/kAABsAGgAZAF/wGwAaABkAH/AbABoAGQAf8BsAGgAZAB/wGwAaABkAH/ - AbABoAGQAf8BsAGgAZAB/wGwAaABkAH/AbABoAGQAf8BXgFGAS4B/xgAAbABmAGQAf8C8AHgAf8C8AHg - Af8C8AHgAf8C8AHgAf8B8AHoAeAB/wHwAegB4AH/AfAB6AHgAf8B8AHoAeAB/wHwAegB4AH/AV4BRgEu - Af+QAAGwAaABkAr/AfgC/wPwAf8B8AHoAeAB/wHwAeAB0AH/AeAC0AH/AeAByAHAAf8BsAGgAZAB/wFe - AUYBLgH/GAABsAGYAZAB/wPwAv8ByAGBAv8ByAGBAv8ByAGBAv8ByAGBAv8ByAGBAv8ByAGBAv8ByAGB - Af8B8AHoAeAB/wFeAUYBLgH/kAABsAGgAZAK/wH4Av8D8AH/AfAB6AHgAf8B8AHgAdAB/wHgAtAB/wHg - AcgBwAH/AbABoAGQAf8BXgFGAS4B/xgAAbABmAGQAf8D8AL/AcgBkAL/AcgBkAL/AcgBkAL/AcgBkAL/ - AcgBkAL/AcgBkAL/AcgBgQH/AvAB4AH/AV4BRgEuAf+QAAGwAaABkA7/AfgB8AH/A/AB/wHwAuAB/wHw - AdgB0AH/AeAB0AHAAf8BsAGgAZAB/wFeAUYBLgH/GAABsAGYAZAB/wPwAv8ByAGQAf8BsAGYAZAB/wGw - AZgBkAH/AbABmAGQAf8BsAGYAZAB/wGwAZgBkAL/AdABkAH/A/AB/wFeAUYBLgH/kAABsAGgAZAJ/wGw - AXYBXgX/AbABdgFeAf8B8AHoAeAB/wHwAuAB/wHgAdgB0AH/AbABoAGQAf8BXgFGAS4B/xgAAbABmAGQ - Av8B+AHwAv8B0AGQAv8B0AGQAv8B0AGQAv8B0AGQAv8B0AGQAv8B0AGQAv8B0AGQAf8D8AH/AV4BRgEu - Af+QAAHAAagBkAX/AbABgQFuAf8BsAGBAV4B/wGwAXYBXgH/AbABgQFuAf8BwAGYAYEB/wHwAegB4AH/ - AfAB2AHQAf8BsAGgAZAB/wFeAUYBLgH/GAABsAGgAZAC/wH4AfAC/wHQAaAB/wGwAZgBkAL/AdABoAH/ - AbABmAGQAv8B0AGgAf8BsAGYAZAC/wHQAaAB/wPwAf8BXgFGAS4B/5AAAcABqAGgCf8BsAGBAW4F/wGw - AYgBbgH/A/AB/wHwAegB4AH/AfAC4AH/AbABoAGQAf8BXgFGAS4B/xgAAcABqAGQAv8B+AHwAv8B2AGg - Av8B2AGgAf8BsAGYAZAC/wHQAaAB/wGwAZgBkAL/AdgBoAL/AdABoAL/AfgB8AH/AV4BRgEuAf+QAAHA - AbABoAX/AbABiAFuAf8BsAGIAW4B/wGwAYEBbgH/AbABiAFuAf8BwAGQAYEB/wPwAf8B8AHoAeAB/wGw - AaABkAH/AV4BRgEuAf8YAAHAAagBoAL/AfgD/wHYAbAB/wGwAZgBkAL/AdgBsAH/AbABmAGQAv8B2AGw - Af8BsAGYAZAC/wHYAbAC/wH4AfAB/wFeAUYBLgH/kAAB0AGwAaAJ/wGwAYgBbgX/AbABiAFuBv8B+AHw - Af8BsAGgAZAB/wGwAaABkAH/AV4BRgEuAf8YAAHAAagBoAb/AdgBsAL/AdgBsAL/AdgBsAL/AdgBsAL/ - AdgBsAL/AdgBsAL/AdgBsAL/AfgB8AH/AV4BRgEuAf+QAAHQAbgBoBn/AbABoAGQAf8BXgFGAS4B/wFe - AUYBLgH/AV4BRgEuAf8YAAHAAbABoAb/AeABsAL/AeABsAL/AeABsAL/AeABsAL/AeABsAH/AbABmAGQ - Af8BXgFGAS4B/wFeAUYBLgH/AV4BRgEuAf+QAAHQAbgBsBn/AcABqAGQAf8B0AHIAcAB/wFeAUYBLgH/ - AeAB0AHAAf8YAAHQAbgBoAb/AeABwAL/AeABwAL/AeABwAL/AeABwAL/AeABwAH/AbABmAGQAf8B0AHI - AcAB/wFeAUYBLgH/AUwCSQGQkAAB4AHAAbAZ/wHAAagBoAH/AV4BRgEuAf8B4AHQAcAB/xwAAdABuAGw - Gf8BsAGgAZAB/wFeAUYBLgH/AUwCSQGQlAAB4AHAAbAB/wHgAcABsAH/AeABwAGwAf8B4AHAAbAB/wHg - AcABsAH/AdABwAGwAf8B0AG4AbAB/wHQAbABoAH/AeAB0AHAAf8gAAHQAbgBsAH/AdABuAGwAf8B0AG4 - AbAB/wHQAbgBsAH/AdABuAGwAf8B0AGwAaAB/wHAAbABoAH/AcABqAGgAf8BTAJJAZD/AP8AmgABsAGg - AZAB/wFYAUABKAH/AVgBQAEoAf8BWAFAASgB/wFYAUABKAH/AVgBQAEoAf8BWAFAASgB/wFYAUABKAH/ - AVgBQAEoAf8BWAFAASgB/wFYAUABKAH/TAABagGIAZAB/wFaAYEBkAH/AVoBcgGBAf8BSgFqAYEB/wFK - AVoBagH/AToBUgFaAf8BOgFCAUoB/wEqATIBOgH/ARoCKgH/AhoBKgH/AQoBEgEaAf8DCgH/AgoBGgH/ - DAABwAGgAZAB/wFbAUMBKwH/AVsBQwErAf8BWwFDASsB/wFbAUMBKwH/AVsBQwErAf8BWwFDASsB/wFb - AUMBKwH/AVsBQwErAf8BWwFDASsB/wFbAUMBKwH/AVsBQwErAf8YAAGwAaABkAX/AbABoAGQAf8BsAGg - AZAB/wGwAaABkAH/AbABoAGQAf8BsAGgAZAB/wGwAaABkAH/AbABoAGQAf8BsAGgAZAB/wFYAUABKAH/ - DAABaAGIAZAB/wFoAXABgQH/AVgCaAH/AUgCWAH/ATgCSAH/ASgBMAE4Af8BGAEgASgB/wEIAhgB/wII - ARgB/wIIARgB/wIIARgB/wIIARgB/wIIARgB/wIIARgB/wIIARgB/wQAAWoBiAGQAf8BkAGgAbAB/wFq - AbAB0AH/AQABkAHQAf8BAAGQAdAB/wEAAZAB0AH/AQABkAHAAf8BCgGIAcAB/wEKAYEBsAH/AQoBgQGw - Af8BGgFyAaAB/wEaAWoBkAH/ARoBQgFaAf8DMgFQCAABwAGoAZAC/wH4AfAC/wHwAeAB/wHwAegB4AH/ - AfAB4AHQAf8B8AHQAcAB/wHwAcgBsAH/AeABwAGgAf8B4AG4AaAB/wHgAbABkAH/AeABqAGQAf8BWwFD - ASsB/xgAAbABoAGQCv8B+AL/A/AB/wHwAegB4AH/AfAB4AHQAf8B4ALQAf8B4AHIAcAB/wGwAaABkAH/ - AVgBQAEoAf8MAAFoAYgBkAH/AaAB4AHwAf8BaAHQAfAB/wFIAbgB4AH/ASgBsAHgAf8BKAGoAeAB/wEY - AaAB0AH/ARgBmAHAAf8BGAGQAcAB/wEYAYEBsAH/ARgBgQGwAf8BCAGBAbAB/wEYAXABoAH/ARgBaAGQ - Af8BCAIYAf8EAAGBAYgBkAH/AYEBwAHQAf8BkAGoAbAB/wGBAeAC/wFaAdAC/wFKAcgC/wFKAcgC/wE6 - AcAB8AH/ASoBsAHwAf8BKgGoAfAB/wEaAaAB4AH/AQoBkAHQAf8BGgFiAYEB/wFVAk0BsAgAAcABqAGg - A/8B8AH/AcABqAGgAf8BsAKgAf8B8AHoAeAB/wGwAaABkAH/AbABmAGQAf8BsAGYAZAB/wGwAZgBkAH/ - AbABmAGQAf8B4AGwAZAB/wFbAUMBKwH/GAABsAGgAZAO/wH4AfAB/wPwAf8B8ALgAf8B8AHYAdAB/wHg - AdABwAH/AbABoAGQAf8BWAFAASgB/wwAAYEBiAGQAf8BsAHoAfAB/wGQAegC/wGBAeAC/wFoAdgC/wFo - AdAB8AH/AVgByAHwAf8BSAHAAfAB/wE4AbgB8AH/ASgBqAHwAf8BKAGoAeAB/wEYAZgB4AH/AQgBkAHQ - Af8BGAFwAaAB/wEYASABKAH/BAABgQGQAaAB/wGBAdAB8AH/AZABqAGwAf8BkAHAAdAB/wFqAdgC/wFa - AdAC/wFaAdAC/wFKAcgC/wFKAcAC/wE6AbgB8AH/ASoBsAHwAf8BKgGoAfAB/wEKAYgB0AH/ARoBQgFa - Af8DFwEgBAABwAGoAaAD/wHwA/8B8AL/AfgB8AL/AfAB4AH/AfAB6AHgAf8B8AHgAdAB/wHwAdABwAH/ - AfAByAGwAf8B4AHAAaAB/wHgAbgBoAH/AVsBQwErAf8YAAGwAaABkBL/AvAB/wHwAegB4AH/AfAC4AH/ - AeAB2AHQAf8BsAGgAZAB/wFYAUABKAH/DAABgQGQAaAB/wGwAegB8AH/AaAB6AL/AZAB6AL/AYEB4AL/ - AWgB2AL/AWgB0AHwAf8BWAHIAfAB/wFIAcAB8AH/ATgBuAHwAf8BKAGoAfAB/wEoAaAB4AH/ARgBmAHg - Af8BCAGBAbAB/wEoATABOAH/BAABgQGQAaAB/wGBAdgB8AH/AYEByAHgAf8BkAGoAbAB/wGBAeAC/wFq - AdAC/wFaAdgC/wFaAdAC/wFaAdAC/wFKAcgC/wE6AcAB8AH/AToBuAHwAf8BKgGwAfAB/wEaAWIBgQH/ - AUwCSQGQBAABwAGoAaAD/wHwAf8BsAGoAaAB/wGwAqAC/wH4AfAB/wGwAaABkAH/AbABmAGQAf8BsAGY - AZAB/wGwAZgBkAH/AbABmAGQAf8B4AHAAaAB/wFbAUMBKwH/GAABwAGoAZAS/wH4AfAB/wPwAf8B8AHo - AeAB/wHwAdgB0AH/AbABoAGQAf8BWAFAASgB/wwAAYEBkAGgAf8BsAHwAv8BsAHwAv8BoAHoAv8BkAHg - Av8BgQHgAv8BaAHYAv8BaAHQAfAB/wFYAcgB8AH/AUgBwAHwAf8BOAGwAfAB/wEoAagB8AH/ARgBoAHg - Af8BCAGBAbAB/wI4AUgB/wQAAYEBmAGgAf8BkAHgAfAB/wGQAeAC/wGQAagBsAH/AZABuAHAAf8BagHY - Av8BWgHYAv8BWgHYAv8BWgHYAv8BWgHQAv8BSgHQAv8BSgHIAv8BOgG4AfAB/wEqAaAB4AH/AVQBSwFO - AfADIQEwAcABsAGgA/8B8AP/AfAD/wHwA/8B8AL/AfgB8AL/AfAB4AH/AfAB6AHgAf8B8AHgAdAB/wHw - AdABwAH/AfAByAGwAf8BWwFDASsB/xgAAcABqAGgFv8B+AHwAf8B8AHoAeAB/wHwAuAB/wGwAaABkAH/ - AVgBQAEoAf8MAAGBAZgBoAH/AcAB8AL/AbAC8AH/AaAB8AL/AaAB6AL/AZAB4AL/AYEB4AL/AWgB2AL/ - AVgB0AHwAf8BWAHIAfAB/wFIAbgB8AH/ATgBsAHwAf8BKAGoAeAB/wEIAYgBwAH/AkgBWAH/BAABgQGY - AaAB/wGQAeAB8AH/AaAB6AL/AYEByAHgAf8BkAGoAbAB/wGBAeAC/wGBAeAC/wGBAeAC/wGBAeAC/wGB - AeAC/wGBAeAC/wGBAeAC/wFqAdgC/wFqAdgC/wFKAagB0AH/AVECTAGgAcABsAGgA/8B8AP/AfAD/wHw - A/8B8AP/AfAC/wH4AfAB/wHAAcgBwAH/ATsBWwFrAf8B8AHgAdAB/wHwAdABwAH/AVsBQwErAf8MAAMM - ARAIAAHAAbABoBb/AfgC/wPwAf8B8AHoAeAB/wGwAaABkAH/AVgBQAEoAf8MAAGBAZgBoAH/AcAB8AL/ - AbAB8AL/AbAB8AL/AaAB6AL/AZAB6AL/AZAB4AL/AYEB4AL/AWgB2AL/AVgB0AHwAf8BWAHIAfAB/wFI - AbgB8AH/ASgBqAHgAf8BCAGQAcAB/wFIAVgBaAH/BAABkAKgAf8BoAHoAfAB/wGgAegC/wGgAegC/wGQ - AbABwAH/AZABsAHAAf8BkAGoAbAB/wGQAagBsAH/AYEBoAGwAf8BgQGgAbAB/wGBAZgBoAH/AYEBmAGg - Af8BgQGQAaAB/wGBAZABoAH/AYEBiAGQAf8BagGIAZAB/wHAAbABoAX/AeAB6AHwAf8BWwFzAYEB/wPg - A/8B8AH/AcAByAHQAf8BSwFbAWsB/wErAagB0AH/ARsBMwE7Af8BsAKgAf8BWwFDASsB/wgAAUACPwFw - ATsBgQFLAf8IAAHQAbABoBr/AfgB8AH/A/AB/wGwAaABkAH/AVgBQAEoAf8MAAGQAqAB/wHAAfAC/wGw - AfAC/wGwAfAC/wGwAvAB/wGgAfAC/wGQAegC/wGQAeAC/wGBAeAC/wFoAdAC/wFYAdAB8AH/AUgBwAHw - Af8BSAG4AfAB/wEYAZgB0AH/AVgBaAGBAf8EAAGQAaABsAH/AaAB6AHwAf8BoAHwAv8BoAHoAv8BoAHo - Av8BgQHYAv8BWgHYAv8BWgHYAv8BWgHYAv8BWgHYAv8BWgHYAv8BWgHYAv8BagGIAZAB/wwAAdABsAGg - Bf8BkAGoAbAB/wGBAdAB4AH/AUsBWwFrAf8BsAG4AcAB/wFLAVsBawH/AVsBwAHgAf8BSwFbAWsB/wEr - AbgB8AH/AQsBIwErAf8BCwErATsB/wELASMBKwH/AUwCSQGQATsBcwE7Af8BSwGoAVsB/wgAAdABuAGg - Hf8BsAGgAZAB/wGwAaABkAH/AVgBQAEoAf8MAAGQAaABsAH/AcAB8AL/AcAB8AL/AcAB8AL/AcAB8AL/ - AbAB8AL/AbAB8AL/AaAB6AL/AZAB6AL/AZAB4AL/AYEB2AL/AWgB0AL/AWgByAHwAf8BWAHAAfAB/wFY - AWgBgQH/BAABkAGgAbAB/wGgAvAB/wGwAvAB/wGgAfAC/wGgAegC/wGgAegC/wFqAdgC/wGQAqAB/wGB - AZgBoAH/AYEBmAGgAf8BgQGQAaAB/wGBApAB/wFqAYgBkAH/DAAB0AG4AaAF/wHQAdgB4AH/AZABqAGw - Af8BgQHgAfAB/wFLAVsBawH/AYEB0AHgAf8BSwFbAWsB/wFbAdAB8AH/ATsBWwFrAf8BKwGwAeAB/wEb - AZgB0AH/ARsBiAGwAf8BGwFLAWsB/wFLAYgBWwH/AUsBsAFbAf8IAAHQAbgBsBn/AbABoAGQAf8BWAFA - ASgB/wFYAUABKAH/AVgBQAEoAf8MAAGQAaABsAH/AZABoAGwAf8BkAGgAbAB/wGQAaABsAH/AZABoAGw - Af8BkAGgAbAB/wGQAqAB/wGQAZgBoAH/AYEBmAGgAf8BgQGYAaAB/wGBAZgBoAH/AYEBmAGgAf8BgQGY - AaAB/wGBAZgBoAH/AToCOQFgBAABkAGoAbAB/wGgAdAB4AH/AbAC8AH/AbAC8AH/AaAB8AL/AaAB6AL/ - AZABoAGwAf8BRwJFAYAgAAHQAbgBoAH/AdABuAGgAf8B0AG4AaAB/wKwAaAB/wGQAagBsAH/AYEB4AHw - Af8BSwFbAWsB/wGBAeAB8AH/ATsBUwFrAf8BWwHQAfAB/wFLAcgB8AH/ATsBwAHwAf8BGwGwAeAB/wEb - AaAB0AH/AVsBmAFrAf8BSwG4AWsB/wgAAdABwAGwGf8BwAGoAZAB/wHQAcgBwAH/AVgBQAEoAf8BTAJJ - AZAMAAGQAagBsAH/AbAB6AHwAf8BsAHwAv8BsAHwAv8BsALwAf8BkAHgAfAB/wGQAaABsAH/AUcCRQGA - IAADMgFQAZABqAGwAf8BkAGoAbAB/wGQAagBsAH/AZABqAGwAf8BkAGoAbAB/wFMAkkBkDAAAZABqAGw - Af8BGwGBAaAB/wGQAagBsAH/AYEB4AHwAf8BOwFTAWsB/wGBAeAB8AH/AWsB2AHwAf8BWwHQAfAB/wFL - AcgB8AH/ATsBwAHwAf8BKwG4AfAB/wFrAagBgQH/AVsBwAGBAf8IAAHgAcABsBn/AcABqAGgAf8BWAFA - ASgB/wFMAkkBkBAAAyEBMAGQAagBsAH/AZABqAGwAf8BkAGoAbAB/wGQAagBsAH/AZABqAGwAf8DKgFA - cAABTAJJApABqAGwAf8BGwGBAaAB/wGQAagBsAH/AYEB4AHwAf8BgQHgAfAB/wGBAeAB8AH/AWsB2AHw - Af8BWwHQAfAB/wFbAbAB0AH/AYEBiAGQAf8BsAHAAaAB/wGQAdABoAH/CAAB4AHAAbAB/wHgAcABsAH/ - AeABwAGwAf8B4AHAAbAB/wHgAcABsAH/AdABwAGwAf8B0AG4AbAB/wHQAbABoAH/AUwCSQGQqAADMgFQ - AVUCTQGwAZABqAGwAf8BkAGgAbAB/wGQAqAB/wGBAZgBoAH/AYEBkAGgAf8BgQGIAZAB/wFHAkUBgAGg - AbgBoAH/AbABwAGgAf//AAEAAUIBTQE+BwABPgMAASgDAAFAAwABIAMAAQEBAAEBBgABARYAA/8BAAT/ - BAABwAEHAeABAwQAAcABBwHgAQMEAAHAAQcB4AEDBAABwAEHAeABAwQAAcABBwHgAQMEAAHAAQcB4AED - BAABwAEHAeABAwQAAcABBwHgAQMEAAHAAQcB4AEDBAABwAEHAeABAwQAAcABBwHgAQMEAAHAAQcB4AED - BAABwAEPAeABBwQAAcABHwHgAQ8EAAT/BAAI/wHAAQcC/wEAAQcBAAEPAcABBwEAAQEBAAEDAQABDwHA - AQcBAAEBAQABAwEAAQ8BwAEHAQABAQEAAQEBAAEPAcABBwEAAQEBAAEBAQABDwHAAQcBAAEBAwABDwHA - AQcBAAEBAwABDgHAAQcBAAEBAwABDAHAAQcBAAEBAQABBwIAAcABBwEAAQEBAAEHAgABwAEHAQABAQEA - Af8CAAHAAQcBAAH/AQEB/wHgAQABwAEPAQED/wHgAQABwAEfBP8B+AEACP8L - - - \ No newline at end of file diff --git a/tools/POIFS Browser/MockRecordVisitor.cs b/tools/POIFS Browser/MockRecordVisitor.cs deleted file mode 100644 index f873c1869..000000000 --- a/tools/POIFS Browser/MockRecordVisitor.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using NPOI.HSSF.Record.Aggregates; -using NPOI.HSSF.Record; - -namespace NPOI.Tools.POIFSBrowser -{ - public class MockRecordVisitor : RecordVisitor - { - - private List _list; - public MockRecordVisitor() - { - _list = new List(); - } - public void VisitRecord(Record r) - { - _list.Add(r); - } - public List Records - { - get { return _list; } - } - } -} diff --git a/tools/POIFS Browser/POIFSBrowser.csproj b/tools/POIFS Browser/POIFSBrowser.csproj deleted file mode 100644 index e7ea08622..000000000 --- a/tools/POIFS Browser/POIFSBrowser.csproj +++ /dev/null @@ -1,141 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {1D3BE35D-A885-4A96-BE95-94F81307F4DE} - WinExe - Properties - NPOI.Tools.POIFSBrowser - POIFSBrowser - v4.0 - 512 - - - - - - - - - - - 3.5 - - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - - ..\..\solution\Lib\ICSharpCode.SharpZipLib.dll - - - ..\..\solution\Lib\NPOI.dll - - - - - - - - - - Form - - - AboutDialog.cs - - - - - - - - - - - Code - - - Form - - - MainForm.cs - - - - - - AboutDialog.cs - - - MainForm.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tools/POIFS Browser/POIFSBrowser.csproj.vspscc b/tools/POIFS Browser/POIFSBrowser.csproj.vspscc deleted file mode 100644 index b6d32892f..000000000 --- a/tools/POIFS Browser/POIFSBrowser.csproj.vspscc +++ /dev/null @@ -1,10 +0,0 @@ -"" -{ -"FILE_VERSION" = "9237" -"ENLISTMENT_CHOICE" = "NEVER" -"PROJECT_FILE_RELATIVE_PATH" = "" -"NUMBER_OF_EXCLUDED_FILES" = "0" -"ORIGINAL_PROJECT_FILE_PATH" = "" -"NUMBER_OF_NESTED_PROJECTS" = "0" -"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" -} diff --git a/tools/POIFS Browser/POIFSBrowser.sln b/tools/POIFS Browser/POIFSBrowser.sln deleted file mode 100644 index e5d30d06c..000000000 --- a/tools/POIFS Browser/POIFSBrowser.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "POIFSBrowser", "POIFSBrowser.csproj", "{1D3BE35D-A885-4A96-BE95-94F81307F4DE}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1D3BE35D-A885-4A96-BE95-94F81307F4DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1D3BE35D-A885-4A96-BE95-94F81307F4DE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1D3BE35D-A885-4A96-BE95-94F81307F4DE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1D3BE35D-A885-4A96-BE95-94F81307F4DE}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/tools/POIFS Browser/Program.cs b/tools/POIFS Browser/Program.cs deleted file mode 100644 index 7dcb3c433..000000000 --- a/tools/POIFS Browser/Program.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* ==================================================================== - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==================================================================== */ - -/* ================================================================ - * POIFS Browser - * Author: NPOI Team - * HomePage: http://www.codeplex.com/npoi - * Contributors: - * Huseyin Tufekcilerli 2008.11 1.0 - * Tony Qu 2009.2.18 1.2 alpha - * - * ==============================================================*/ - -using System; -using System.Windows.Forms; - -namespace NPOI.Tools.POIFSBrowser -{ - static class Program - { - [STAThread] - static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm()); - } - } -} \ No newline at end of file diff --git a/tools/POIFS Browser/Properties/AssemblyInfo.cs b/tools/POIFS Browser/Properties/AssemblyInfo.cs deleted file mode 100644 index c608066ec..000000000 --- a/tools/POIFS Browser/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("POIFS Browser")] -[assembly: AssemblyDescription("NPOI.Tools.POIFSBrowser")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("NPOI Team")] -[assembly: AssemblyProduct("POIFS Browser")] -[assembly: AssemblyCopyright("Apache License 2.0")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("169c3ca6-4b0e-4d7e-8a5a-305805cfb201")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.4.0")] -[assembly: AssemblyFileVersion("1.2.4.0")] diff --git a/tools/POIFS Browser/Properties/Resources.Designer.cs b/tools/POIFS Browser/Properties/Resources.Designer.cs deleted file mode 100644 index d43c5a35c..000000000 --- a/tools/POIFS Browser/Properties/Resources.Designer.cs +++ /dev/null @@ -1,103 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.18444 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace NPOI.Tools.POIFSBrowser.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NPOI.Tools.POIFSBrowser.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap arrow_refresh { - get { - object obj = ResourceManager.GetObject("arrow_refresh", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap NET_h_rgb_2 { - get { - object obj = ResourceManager.GetObject("NET_h_rgb_2", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap Open { - get { - object obj = ResourceManager.GetObject("Open", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap SaveAs { - get { - object obj = ResourceManager.GetObject("SaveAs", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - } -} diff --git a/tools/POIFS Browser/Properties/Resources.resx b/tools/POIFS Browser/Properties/Resources.resx deleted file mode 100644 index fd731f5cc..000000000 --- a/tools/POIFS Browser/Properties/Resources.resx +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - ..\Resources\Open.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\SaveAs.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\NET_h_rgb_2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\resources\arrow_refresh.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - \ No newline at end of file diff --git a/tools/POIFS Browser/Properties/Settings.Designer.cs b/tools/POIFS Browser/Properties/Settings.Designer.cs deleted file mode 100644 index 66866106d..000000000 --- a/tools/POIFS Browser/Properties/Settings.Designer.cs +++ /dev/null @@ -1,26 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.18444 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace NPOI.Tools.POIFSBrowser.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - } -} diff --git a/tools/POIFS Browser/Properties/Settings.settings b/tools/POIFS Browser/Properties/Settings.settings deleted file mode 100644 index 39645652a..000000000 --- a/tools/POIFS Browser/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/tools/POIFS Browser/PropertyListViewItem.cs b/tools/POIFS Browser/PropertyListViewItem.cs deleted file mode 100644 index bab522dc4..000000000 --- a/tools/POIFS Browser/PropertyListViewItem.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* ==================================================================== - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==================================================================== */ - -/* ================================================================ - * POIFS Browser - * Author: NPOI Team - * HomePage: http://www.codeplex.com/npoi - * Contributors: - * Huseyin Tufekcilerli 2008.11 1.0 - * Tony Qu 2009.2.18 1.2 alpha - * - * ==============================================================*/ - -using System; -using System.Collections; -using System.Windows.Forms; -using NPOI.HPSF; -using NPOI.HPSF.Wellknown; - -namespace NPOI.Tools.POIFSBrowser -{ - internal class PropertyListViewItem : ListViewItem, IComparable - { - public Property Property { get; private set; } - public string TypeName { get; set; } - - private PropertyListViewItem(Property property, PropertyIDMap propertyIDMap) - : base(new string[] { - property.ID.ToString(), - propertyIDMap.ContainsKey(property.ID) ? - propertyIDMap[property.ID].ToString() : string.Empty, - property.Type.ToString(), - property.Value.ToString() - }) - { - this.Property = property; - - this.ImageKey = "Property"; - } - - public static PropertyListViewItem[] Create(PropertySet propertySet) - { - PropertyIDMap propertyIDMap; - - if (propertySet.IsDocumentSummaryInformation) - { - propertyIDMap = PropertyIDMap.DocumentSummaryInformationProperties; - } - else if (propertySet.IsSummaryInformation) - { - propertyIDMap = PropertyIDMap.SummaryInformationProperties; - } - else - { - propertyIDMap = new PropertyIDMap(new Hashtable()); - } - - var length = propertySet.Properties.Length; - var propertyListViewItems = new PropertyListViewItem[length]; - for (int i = 0; i < length; i++) - { - var property = propertySet.Properties[i]; - - //TODO:: filter empty name items - PropertyListViewItem tmp= new PropertyListViewItem(property, propertyIDMap); - - propertyListViewItems[i] =tmp ; - } - - System.Array.Sort(propertyListViewItems); - - return propertyListViewItems; - } - - #region IComparable Members - - public int CompareTo(PropertyListViewItem other) - { - return this.Property.ID.CompareTo(other.Property.ID); - } - - #endregion - } -} \ No newline at end of file diff --git a/tools/POIFS Browser/Resources/NET_h_rgb_2.png b/tools/POIFS Browser/Resources/NET_h_rgb_2.png deleted file mode 100644 index 6d4f42ab1077a2ea34d40e98e26d09b2ed6cd3fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14573 zcmVPx#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBVVYe_^wRCr$Podn;s(>u6O-Hi*?V0uWd@PF^kUGBZR9CyhjIWD=(|NC3-*jlYdyX%z; z@2#Ha^X#rx8jVJ)&;CX;8ol;kw#ilkRk#Fvwb#B22&nU= zgJaGA^7VKB$;<@*bv&p!GjmznVzv^nm4K}Tv@Zb?AqW8pAR91_&$bL!XK}LCG+2Fk zZP&PhZv9W)>oVcmmjPzb5X-!#{oY!fVldVS0d5wr`Jb@@l}iG60qD<+2>j33bqn^g zT4eSc;3F`<`kIR|y?19{eg9DYd17Vu<0I?xKabsX^NYw$x4wv6cjH;)>g%6JF3bNs za#8Nn$a&YEM9$8B9QjGsqsWgk9!3VIJ&2r`dOvdPm7>VeVEonlksqW!!2E}i6R$pq z9CzhG=@PHv*Ugp_Gi0yIGF23bUd2x+U3FnM?LmD6}eA}R^~q~T$=Z^a6#^q!jPQDg|o6B6;985SU5G~LE+@I2ZbM|KPa4uc?)&R zJT07)^Q3S_=HtRCX^#pgUVT_N=JJEWVdw8Yc_T9QtLF2L{@r`bPUaM}mO1oTT}sb2 z%<-))%z5Z8=JH-Va~-o^hugS>M|Gwh3xN#l3-HE#7f=-<7|niYke?d}1wtv-kIiMZ zcV@5}n^TzciX^M$NYQ8Gjj8h?$hY3uf8$+4gb zdi#J@|V z_$dTg!ZEy;@Cn|7HXASg{Bd&OpH}8QeYQ0B$+NjRkDq;-_2}8h84sT!f_yd~^OhBS zUc%!1r_bhId-7~%*5hZ>(jI;}>`>kx+k_l`={|6ip%A$oyDw#qT|Z;aLCcui`*F-| ze1anG6Au2)d-my%A;JIw4Kcs=D{xp;vuLguT_e(1wN1(FwT(%v`g)S1_0(OK{19?{ zn+Q5`hXfgD2IiKzT#ytAS~5}e$WL&3l?#&6A_>s^|BMsdlF}=Vys6wMiWE&$#xDeA zYuVPS`qGf%XP~HNWcG8%l_}ra&px4-p|S``hNT?EdoZII=h??uzu2so%dS27BSiVt zf}F=+&B}iK6~t)7()`cAnwRtB55r>9zH2-=Mp=p^$2TCxZgZGZ*H21$eHL?lcN?oc z@sN-@Q;w#$ip%W+8MY8W0=28^$44oDkYf<|E%-%*S9G>6Vk9wgD0=ie zM-YS0-5&xub~ZtC3(8q8NIF?5f-z^JXh$kXks!@U>pyiFF31R#0+2?SN{i7S4@Kvc z7H9Y?RiKb!H;@c|<-Rrj&rWks|6CFo8c2|YXedH1#FE$PM_4ThEjL{J_x$t+-+h|( z_?s!2kG@%u_w*Y`vIJDaUyR?M`D5epyHv!uj5&4>VNS2l6yiE~J@c4+SO_Hfd7FLt zJs`q%0@`4{{i}4~AjBQuCl!8n4p05%HHsP~RNs)Si5%-KzmNkljv>13xju2^mB;-d z$L=O@v7n5lf}|*gPDYAY%$fMQd_9{W%_%j*Ww;=fB5%H=njq;<>Nz06aybU$Jw>EJ zmS?9D)_@defoBdovw!2g{_^*th|oxcGYmy2m13=r@tuytx@gj8VImuQspy}RGah{( z4CdxO`5uz|{?p{!FI!KG`^Dh8qVyVLk5$aE#{%ZmW42ki4qnSVryLdH_3`m%Z4<8d zfe0y@RH7p_=2XIAuxOKhxYh_xfgeTSTI0WJg2#FS`VNzGyzBL;2>*Fok9K zK2W}B|9L)Xo7wvH>9y)kkYass4m_!|E9ZCKE0TXUmY}}MEQ<<_We5?H45fU8&wA`X zJjuN0CbDst?*9x){xm85;m^I+oc_hF-+FP>*kc)U>N%G=^+M!m0d7OqGp~=13Gx2q z#24)j-53ZFQZ%W=>zMCURgaurjs}Dm-h7`Mi0`MpxD)%qmOOuMP9a<^EVj&(XWg6b25y$SM6O1J3wy%#a(H|LcZkI@mV?u-*c>diS_1PS(4;SGFOqY@91aMO_D z1@J-y#M9s>r*vJl?}BBrD3YA$lm{*Xo*owwZI^#9Pu~sSQ967+Eyv{{oloD> z_N9}*EM2}H-`^nF^7L7n4_c0|Bb`t0DPJ_vIy`Smue83YWq7$zIZ&QM0llaF8?N6D zrBn4LNby~89}O{&nn&mU&3kF0p%|srW0k1TNQ5Lq0}evEsAkWihMrb|$V zSdN_Dp^|PpE76q)a_qCfYP^E?GM`UR32C@2`3NL9NQI77?dUa(&))$4EW$4??}h!2 zH1=8;qDKX)ZMn=m7AHcG*3mHZ@>PhDs@FPt?7z7JVx$Hd5hTvC{Nd1v#ZOZC$t#x! zKU{PI@^!c{_-9H_K8e1QN`M3+mk$X=o*td_JU#h3Bnp}$B+HOAC>`lU+o5%NI($9( zx`7JoNtdN`c{=pU#Ux#gw$Haid6!Rof0W9NFRNCzv@f&`TA!diJO{xFWv8(D3Z4Zq zjt6%k$NOz#^1k<5b-}o5Oj49oj}1gf(V>zEr$L5OWzan8GYLW3e|GX;e)Et2rP-8d zM$~?1lc6Mgg)+z9E12_Ji#5Y#@Jh(>1gkguw2CGpg^M7-5h^q;V>!}E#ZN$g-O4@#48Q%SSpzk(Q7gWHhfJ(6SN^;cvfRHz;+ z$xtpr>E|g4tQLo=?2x1L!n1#CdFXmhhXdDgxFDNMMvHa7b)}N*zn(b{SgJW5V*Pps z^(ovfzAG{1Gf6|HXk6Ib1l`T(;wn1qS!>#x| zl+UUV<4SN7a=gPIgZ@ITn4Ss9G@A77(^%pe-vktw}~peQ9eh-{8S;x zFz{Ouh#vn?b4NO>wjCY81vrOZU|tZMHFz&cdI2%6mx8-z!Xd`-Dl{;EPmPvT5+p6e zPyR&N8eAY!sn8;p3X)D@zD|$@`R78RSEY&PPb5(3I!gO4%@^e_0{9EIWsbP`s(e{W zuRH{4K@nE)q7dT>YLI~(Z;~84?!3mF=F|@93xZzxJo;WbSt>};c1`UL7bLB(G?@~pDM8ZrBr(1}{FTJ( zX2Cwo^H!u!Q!aK>`$OqiB4ShJSFztij8i~vt;p=_ZKHF)>k^a4Jm;S=zFtT|G!YdV ziIAiS85**m)xvy_)hWM*Y^V{$fKF%boaBP^n|p-0uD(zzNJ!Ftd^B?z8mc)yGY&BS zCCNgXtiN&{dHqC%z7>1qO1I{}C;uRLDxyJL-tVjL$Pmhrr0Ifd&0hGEum(hre}@<& zWkjKR9SS*46JXAO6lz5)5*5jSqA)r+Ne)tR(0of|p>%>*WkJ%u1_3TFo`2eJ{%2vS zxSs3FGEsIg{_hGBVjKi=h^A53z9u;aKuD%VNYSBjR45mrUGRR$5ll9u>Gsr{5X}J* zEf*a6e+?HNXP&6$6V4$g4J7IG-d5%^Y@MdKjoQK*El*J;@Yt<2$n$3^xK^|z1NP*1 z0T0!1jmmymEgXUrP$MkODlu}YB6_52kFiovy*>mn&JfT?gYr@n6_2F27`QNLzI;-P za+JOcmPAG*BXcKHLg9I)ZJL@${5)@>WdmiRlnRcot5hE5mXE3Zu~fvS_PJudhZK8) zOrp`QYrpFfoyXem$YKs?h1OM*RjSA4im+IW`qN{1$?VSEETxPDH(kY{v24 znuabw`$}+_9M4=vY|xzgvk$Yzt1b#@z9sDr^1Vofw=3r8m2L%MTm$Zj@Qcs;P4)0h zp&Ut?#nIz=tMFc&^c}>wUk*Bl$3l*?6`;FnYH3hjYJ#L_iAz8lO-Z9Do&`$j(_T#CMSDvhc2RX!=npT3KbEU%OXm7^&CY9ckYKa?il zA5qy0HeCs`Aw?%}7F=~j!%WAh+<%cA16HL-t`+L5#tPMArKnIYLc0&BLB)^}aan9t z=400H(u4hwON4;&yhs|wkVFh5GWx7El|_)!v`GS#7cMkGzbLJbe;TpkyZH zlS?X5GN1Cu-^ zE>Zfb^;A@=jUq#ph>)tyhzgZtXg3jZEMX6Odsn(nj-RDJ`VwLZ6EH?vGR@=iLYl|r zGbvt+&ocK}M@w*<63bl2ZqpRcsk>S8jj2LfY)N~8{BKraNktvuMtchOgKHw{#peCG zS_BDFnOa-u+M{(5zKXbxTZ8)cZs!xJVdllxRH!~2v?zr28T;E z2}sc!WUAp7mGiBdAZw#hsxAa)4L(bfet;P1wn76?z0Mhx_H-NMNG;e>L?>EVi;U8v zlT=N96s^$r!&NrI(@@Hv+IN&BrJtxE84{0NxDooim{SgvUy`6)c1r2;T?-KikMbv% zBVUIkOZik1C9OyMCz{ONA6ibjA1}&!z{_PlNU<+SB5>XDE|+7=jaN$>QA80TRhK0Z zp^^wmhNZ;geLU;5I*~2Uc%)-#`lBBq6lw*QLtSSSM{sF>;Kp;nr1&g3$GqmBD#2sM zKIS$tQd8=qdr>Pqs7geesGQ%JxQ;rhTn?fnQHXIQNEHzfpO3Cv;^@(SQ>s;B9bgbC2|}W$L#df zpLvAMOu5JAU42->;ETG3nZ**QKVe<9qImu2?O?#9bWXUz>MuWUK%JQfnA?Xjn$if- zZ`;UhA%O>P+(!OMk|KJQ*I|Yfoj|f0XubXejyD8pj3e%`w^oj{KCZ_;haB(7LEpzh zh>>nDl(G?@&3(>Hc@>>4U0VqlmH1b`?lbqe96N+(GY7ivXsHY(BIH6Wc^wL2jtWa7LM0K}L56l?V+|P-%SIn5U^6b=H(=t$`&p33Yzbx$tKaAJ-J(9H?=dNz z_vSJGwHFNVoO6h|f3!za+MsJp;O<-@ozLI9ihVjS;AnaM%$n>eSO89nK=t~TD0*}W zy=avfxl~(jN&gyhd?p8tGtb9lKHCpDQU`GkU5j)mukx^^YbybZO8_T`BM8@Ddpz7C zGUs=lF$e|GE-b_Fx}hj4)Jaf5gd|4Du$0|+znk@mOk>k76d5t#!hPyoJwuO0{RI1; zD~bS2PSY({@T}#_CHSpMWbSCN(UQjNQ&1 zmm|G;Og~(5y-->`Hi`%ZA|x5=7+p$>C1=>=((hX{y7pGUnM z>DA+73`U_)J(fp=1|l3?Dnl+s{_2Ell|znPXQR*GRitln(GtjG94Paks3R{weh!$N zw!5-ftH{g}T4LY@j}Y8xGv|nAG{Y#{T@T(A(&fxu8n%r_Clb-jnlnzmF2ooP;zW4H zm7+qq2z8%FMH}%3t~-u6Td2tJbA|M{ zf>EHgH9yYLsZSFClhgC?P1ZI#$AAX77Fm1lG0pH^c9wNTUrZ5Qj@|hi_K%)`C}M&& z`z~Dbqe%@t(%L?ea=*CxoSEJ&Of9w3I(I3nkMrwGVVk2Bp7qq}Y>jtE5})XDKZP88lp zv8Is6&@*?03_4p#x8(J-RuJPeJ`WzB@r=nCn*4yZM-^BKMU=JYozRpHd-K&LdEo&y z)`&Q0?f&!io9!tW0U|^M#N{8YA*)_f!;BaDENT%wqTk~$9b@v0fG~f-eb@KL=X`Nd z!Z5A&+eA9%3X-aJA?2JWn;@&WAm>1iy9lR}v@*`QN*ROx;G9<^Ls-MwQ{_80z>v%g?UblHEMb;Lc}QoNU)W3}g>)Qo0$ zhKp(xdG_Ad*iWizE27m&2{A4M+eCQ9Z*wb8K~mbZuUa9xUk#Vl2jSObqL}FOT&*aa+vE+awJy^Kx%` zsj0cYdI6Z60cVRC-CZN4DW2M=r69d==Z>l%PZm;Z_U}{}WO@(PmuR-PpdQ4ySq#Z$|=6eg? z0P96Gj?a&(xwG`PLVm}L_k1+WScwm+=xrl&SbN;NUxHqisB-`Ge%jOPe*=R zzFN4xNRSjQDn&CiZ@7iqFn4jAAeGC!(gisHVx;@6buns)D)as4+TfGN@a$ZaD@He7#EQ%San$WV_WlZyTh zLDE=}O5~b*hKZJg7-xVrBD{C!UXezRuDD~yS~2qV8>4Zr9o=9+K#~L(B|3)x&)Ce* zZ|F%IoB7Pg+I_WfeUTs|R0K&?!SbkT+XPwF1gVc6L-9&odp0{qWbf{@1JR?NMtBgs zw0f*8LI_eOLW9r4Ajx5o%w>Em>$D#Y7Kd-BQfK!y%}29=7z0Iu9CZG^k-S>%%F*>i^WBP&Yg_Dk#WN5aUKp^Bvg_I_=1Y zAhTKE)(qx64xPRYqC?}TutbJ>5!yqBM!Xxznr}~K9S+@4C17uU0rFxnI=EK9LXjZf zzxqT`US3PiYAQ*px{JH}-2SCgV$shkl(||5L3RO4L^Q>NyJ|_Q*LBdKqJ0O~Q3mn(r_8Gx9=@bZBSckKC-qA;@ec zNlwfAm7=%;HeJ?KkZRTJF6b-S@$6kS${evkjKjeK5qPx130}Q+fzY&)BSnv`@TAMu z=mo)PgZD&WbneU1sZXAikoKgjSqD-3h13K|TcbZFQ4~p4Z7nC8Agh=l$Kk|S!)df5 zJGzZn<*&R>bVI58vilCFn;;H4{f?5z`h#Ia@*K~K!=&IK9PHRR$ z=q1()4SiC$DEpl#{NwvqAAhN5x5YcA-3q1zNn1)(ktD6J)npT7RS_gk2!F6zO1tgZ zf8~Po#Uorxs>vlXER6`2L|81w@M4CP(qJ=2&Doc)idU@e7PlkdBdvaI@^m1_Adw(P zUV5x3ullQ!G?QeVg{M^o*$IRD1|Gj7q%7qELAC_5M4)=T&`w{yuCoXo!b47JMvHBk ztR-q9I&dAbm5v;0r1@zSZvWJA%n_uis&=3T`Jg0hf~-P<{6OEe zhr0Ky@ZFJhw&S)eMzr6Y&K%J)&8x@CGBg&UREqZR>|jJ~JQ%Cto;+3R$L0MEd7wwh z(!jiLYV>1MOhzQg53hZxC@-oOYZ*mS<4>1^w-o7wYIgghs#tu@3t}7uW{B|Fol7G& zG3U?=7~}JVX80{j!ayL|tOexA;j$cf`tF}cUV8MEo_XbYyv<1496?etYE|trMfVDM zvk9^)2ofhmL$Fjz>up)T;ezy?bzD+SHX*_x#UfO~0oNpbqOw`NxI9(tcV*`yFUjCM zDA)UMMb{xooNr?Ead{P(8dfOs(@K(bU9&R=uTY{B=C?bfipAAAh;b5_CIU|`%&)l> zBlXNat|?8oq@z(Ts|1}aL5G;Umm@Ab_@j=C4>HUUSa~mEt{~MSJi101qCrM&f~+Ef zq-&3hIWFN@n>&UfdIaq^A$ml^j6pQWtIGlr>g4z~vE2ao)yd2gBWkOG>y9)yYbv-* zk*o$kGSwv1u0x8d*;@9Aq?YlH2{(nbgOJ^Z+ zW17(bt>Dd26X7(+d(NQKcmF;-`TjRLQmntvUW4C@C4%(F_C&4PG@rh=X0i#gvIPkV z`GN&fnr_Rw#s%p+6VaoEBGid-upy2gVs!49YpS?KU;7dH$N`xk9gMK%*JaMS)AN5F z1eg-RIm*@SwlO)HMUWJUw%&b>b-);rO0?gHmVVqDF2bCB&0u%qc2I72MkP82QKt zRK?B)-q!rO%w2aH?#UL>ETLGGnN-#O*Cm_L02XtPYl0`9#Z4j< z(JCgF35m`%>snXqz1Qo3_e3;{%b(;u``ACcFq*rT1Z+rUjkjbLBN1h<12_IX<1CelF~9P^FGP39e&u3)J;bxMTMwP zL4+hjIkiSb2_H5uc?kW_$OA=>Mc^*D4aS=DYt~|!$r;Z=08?Vj73I5XI_$~EDC5U8 zqd6L{MAhh4(YdT89=j^QtZE#@*Z~X^QD@DCM_wN#FfFNvK9r3%XRt=-LrXN)LjzU3 zefkc^qU)$y9B&h8ltqw~q-Z@7XG9tEZ4+c=2~rcfdbA;rj`qN290^ z0vu#4!4fGB*vjl>RL3=~YTGl_3D5eyUXaxH@jkc*nwRlMW$|hsqo=@do-M zDs|{?uo6!`oQ20RXg~wpo821M8kJ~?2(m>4T78wF2*u5;Olgtxi za%zeHx~nVzf+TdJ)YAvImk!3zcHqU}lXt9b$S}F@7A{a$LDFW5R3gbB3-h5?b*DU< zJ82V~99GqlCA%Fgrw_)mNpk>Km`BQY}2|cgVx9 z!LKBuEbm8q8l5p?FFsBHOo{t?$8lTcm(>wHy39Gw>P(8)jJjx) z^V^VC0vBb^y%;tA;C14~TZgV!wW>8QLTd#{Q6fc)BB7{8`?BL`iy$lG8YBet9+)hp z(bkNky6Exa!-mlyMTQ0vq|c=ywC{)aGVCDO8mlj=Qhm*(pAbFLn1lZVD2n`ldQldY zMQ~cIGcxf36Vd17T@1^1%ZT>q#O;P_cUn*zg9kT_%~PZa?ptnxOCJL0gjAf`?!E2` z-jL(6J@XRDk?@$jht-)Frx^`aUt+%N)0nSBl7YB=u@A=n)NvrcDpswv5+tP)q#{U5 ze&04hR5M=tcH=cy z#e`l}jL6#VyWR+NmEsYRy|#v4j&^wXn#Y9Qnn9h+8$jd@NRWCO<6i!ty}6w7tdf=G zu*=u6HiG2oMW{&9+&>7LAS*$Ty&xmHwXg(-@T_TpsJ=D$=umNVs3=2Y5tfLt55DWO zQHLFdH>-(J$JMB@^1{Ec{XYTf`$!Lll8Vym2U3;wEP40zz2Ko}kP*@8@NL{1eanaz zc$T&sqA)G+ntzHlf*=)vARDc^B0}*xB*fSrbmY{Jx;CyRHJs@M={)lY^B5PS4HIKo zy_FYP{dK828UR1YvKzWE^@__OUi6N?R#mIkT1JqRmReQ&1->_@9LiIWRO_Jht%d#w z%4@3FYmg9;BN!v6;g*b;wz#P7NG;R~5urhZD3KwTqV6@Qn3|s)V>RcWQ>9v1Iz5d3 z8$bi%8i=yIO4^)shn&3Y_6`Q95b-9u`?SH}5lVQ?Im%o=h|`3IJF}QSZp)NIovs6x zUlL(-9TFYST|qley6G@k+AZb6V`c&b9Yy~0FBNYs2u_-;P!L);hRb`O~ zB{H-R+5jn*LXTpo`Ppezs;|88Pi&v6*MHSZ@-Grm9<1ymHt6V`M8K37fcYfHRtf}! zU1hF##*P;FU{H^S(btSYOSb=#ivliCRAMkejP*cEqVev$ZfLRoyhM(OAnh08cFEDZ zv|(&C^I3(_#}VZc^+40jX{`IMOip5DA6qlGucaVqleFJV00~rWvI(*xtJo0HKrlj1 z%M};@VGs@KtIIqpl#9^*O-NCO9fVrzqa&==%#%WD&OY@^wH=w?Vfz$8QVX_Rl;h0& z5trLy0}kH~9&qS36VVBBY$>2Nu0y(xjjDOLkb ziAKBgIzf(4rE;YA&glDCd(=)%@SKgYBSWw1AVK<}H$;~mX{=j#I`RDV$PB7-z6z7p zf~5VXKQARo(tNdYu?e!G1PK}W02-LW0I#sjkh*w`tNAiq4>^L~hS-gaX0@i{o@*7V zue$IO+b2POt4A(M*Q#&^^-H+<0x%_dV}5f8QVG8e7n$qmXie~0e~C3f!;Bo?Wfz&> zykrp}5Gv;om$-PKoE-Owu#1zokjXbP=LaRHzrBf(%RK*lQiLC$O&F zhXLFs3Qy-C;?`Mf3O0H{Rm^5$T>kyW4S}sTW>@e#na~~0|37+%MGA{^{AW^p5 zaG7=7a)t9MTg}b6ea?~}>pkuh(kT9!pN6!b36_>ze z=Dp$~YmFy9masZGu)+?Owi_x>L7J*+%gf9r$nq8>gk%T$DDY8D42~*~2t^_!8R~eW z6#HJNn!kqz9u;bSdV-=y5+s+Tk|-N$`y1CLwQf7FJqJugBSeu6AxS0N(W34A&UUMD zLrZg=@Js_d@lY+FX{Qu%dk-Th={tcdz1CQBk-5FS#d_Y0WVO*Jlki%8fi+upp0(gC zKOd~k5vY_@%3F|B)fPpOMY27%39p5T|8tyA#^$*@qq|Snq%z5Bet8p2-o4KQL zMv4cjv~?$)(BU=l7`CxXCr83=>^?<7)(Ay6q=6f)CwL!oLw6&0$T5IM4aZXuiN^Cz zC05x3X@15kLXeajiWZe3$rqT<&k36#%TtgL5smxUU4gsR@nHC3Axa`bxd`d=>#LRN zw;i`8;-nPdFeI9yM-rq|lJaP>(gvn5_k0Q5nizu#SiZ>UXEOn@@SIo!+s@BsJ5Fk65C=l zY!Ut#T*4cYRKg2)k~m?sBdb9q>VgIvmu(ooQIFflCyI}OK5~@4*81qEfgo!vPhu|p z)>#LlPnQKr%za@JYcTVe?xAjaiu`6BdnJyg87Edn3DQ(mo37)E@?sOD83)qjA`lSW zd)-;aGZJJ?j6P!&9m+*WG9He)Y zq`d%4iIx~K+Xq!`FGx}ecRaPw370dhX5=pBjK}8^PBS4#1u^n@4sS;p2-5LAbUW(1 zw#+yq@^qekin%UGWZu&cGoOzSaf<5CIP!l*j%w24QJu;2&VmJ~+@7mcwIc-I*#v1p zAuBElLDd8uWUL}N5_a#RfrSfDAVP<(DrN}>$6cK<4R z{T6Feg8>skPwexAAVs)N-N&48d#1HG;yFK#V}Xhs`FFgr#_p3t%sFU{RX9yaV6LCx zj+rTVEbNDP`e89e-ct|$Uy@@*3zD`+)kRTNo32MHePwtDLs6)0+kn*U| zNQ7L7C9f-(L+2IDu{)}ZDuT2h9!u#f36jstvUcApYyD=SR}z}yRyq;BJF=JuB&kSz zE^{6h&YaMnwCt>Bj&JQWOO7N-iXv-ZKwKwOt(|+XE;Ek72bk-pCz;0w`=k_kPTpTt zBmjRie>W9b#pYQOMT#PE+5~Cl!4xhAN%?^`5;k)=R$rY`92pu(kmePO zP?0XHO-7H@IH2E-k|gPIu2uDT#re6)8ks z6paO%Jjmkfe&tcc=GjrJYTF`6<0zVfKiF3MM0j{ImeT7KEhf7zuXz(jcM#0cY%q!O->q$4_zJK}n;S-cLJ_S>lw zqZG%1v8E;2KZ-f^--SQ56_)d6D03XWn>j&@&ZBmh@_sBuW1@+ExD?%R=yR}SVT$6U zpUeIu7q2Ob07*2@&DdaNM39uN;R;eO&j?bKwl(vu6PNM9p+6<1{8+ag$jJ#bldwxF zu9{Q!TP#P1E~`|dN4p98mE_3hshw12{AR1xA`O>bbn{z$p$IS$o~UlSK$41(B*#QE zN0h0Mt`HP8gbmtjBu6ev)ku=$$ff!wqR;Nj&Bn1WZa;+-9fqTyYbh@rMns#E8woH( zAHi{1(hr>G(?q|6@{Gi28={knpJ;(5eod7nNXk~CnUbVDnmd^)4{L-dU)MTuRiGff zAjBqm-jqg&r~y@YW{g@a`_T9J3)x zE;&=jfld-GK{}D21A3+P>3w+%@`VY>m^z6oQ;=@RyQ%z>lx%`D^($2S9#ZlJ0ea5J ztG#ypH(Mykm1U7*!mE6}(cJR)nV%fwGcV~mU`lvHpl;z=%n_1Q!U2yYaC{qm8M~nY zrY{;`R3b)sHQPXx{Sc7`L6nf8(sDFkC*!V56mbZ`NKHfV$7vWfKF8!{C|nb?P>yK17DSJN zYmOwwz_RB5Rb8)cNK&v@NMfQF2D4S74n*mSXUaIn=P`%vkRoo?a|*_-hy&0F(jCL2 z0q35v=unP^m;vHywSoNlV>v8O>y@O@1J?~z(r|opD+IZeId)!*`|#1f6;H8Lh!_*g zAVz-Z#sjHz$Fyx`CiNv(JYT9>FI=-QPQ`!)-`HC`4v9&VAxT z73pDK8qQdbukZBMo=_b~)2U~a4#$DJLml3U355)a5MZkMY0j~w-vC95G*Nm4&Et_M z|IVDW%DN0~Lmq{ZbWBB$JkMO}($|W}H{V|>FQq*2d6pa}%C}ltT7IYX`7g?Q*|!5p zxdVFs%zH}FWA&xyEfOS;9vy>rU{KusOy7-%gqR3IY*H-iIc!_6oJ)X9nNG66a^99L z`B$X`^bsVrVo%jabrji^P-D)qP(+YsV?Qh=v}VW|T0e+*Rm#DZPg@DtO2AeE@)G#} XNl>H_#!I&g00000NkvXXu0mjf_+q#M diff --git a/tools/POIFS Browser/Resources/Open.png b/tools/POIFS Browser/Resources/Open.png deleted file mode 100644 index 6cec6869a67199844a8ee2707df6cbf1f2e7fe9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 523 zcmV+m0`&cfP)Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUziAh93R5(w)ldW#UFcgLJ1`QmmSz!am7BrRBaI9e~OUR_MnoKII z*~((Ft*nNHfrW;HfrEa7vwIycYPVKX$s&D{mH7NzTS=1tc$P19uC#$#5gUu;x>i@^ z77LB(0_sQ;cE$vq0C%d<`zLZiv)l$win1qF`Gw|L9;0^F;V2#G^ zeR;O(x41K)jh;luVm81>1w-G=Ms;Wf&8#Y%Gz$hSh{>H;5ye@48E{H4h%{D%>CeO{ z8Yi}hO+uShY{T_@tAjy%pv`n9XsN*^Xfz(rZ6(Ut)FizDnXtIolR?;YG=ihGlfXg3 z?{l;!Bk`RlgDl}Xo@PEtchMjS2^Kba5PO?J%>`!ngYKGxf$OTfCZ-<=(R8US^xF(> zm7{Fw#H3CkjA$>d=+Wso}TKFoRrGwE)Pebd5(yZz*3jtk; zE`2Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUznMp)JR5(wilc93MFc3ujDftA$Lcu^mvj~<|pdc)Qfr4Qb469&S z1PcWV1q%hsuDjf0L3Aw9ob!pCL zk$CL#*Xds(b^>6^s$YN*n)>rwQI)b1e)rzR*dpWT09peOnF;)U2K+jx46n3L0ALKb zXKh*~SOC7NH-sSRoKeL}N=emPQ-F2l4gihP1XsY4fJUD379}Su?W2lkvr*7as|4&c z0q${5pob0Hj*=sc)k|+PguO|phP!J3#za6mb%juzOgfhta0%6` zx31kUD@c3yfMWFC$>s252Zzz56UGNKmx+MU80tQ4WcOTGL80+5Y`>@+xEdK`AlN)x^Hi}tff;bmZBx@2Hsmt?j d{O`v+d;_QNOmAmJ^y2^k002ovPDHLkV1h{R;eG%B diff --git a/tools/POIFS Browser/Resources/arrow_refresh.png b/tools/POIFS Browser/Resources/arrow_refresh.png deleted file mode 100644 index 0de26566d4102eec080253c2d08985ec58b14838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 685 zcmV;e0#f~nP)`!iy8(2_#ButL^3%VaH2WCpD^U)OZxp@C)2#hU)y+@T%ZNzJigNk%37 zz-WYJwT%teVfiEI+B*@v4ey@58(ld4VY_&5-ox`e@AKg+0U-I`y79bmuw_~y6+4rZ zBG5EdFDS+@M0OSE`>d7SUDOzKZ&h*4eB1iX7tOd9RiYtW2mQ--bUahxr1`i{RG@dM zL#}_X=DDO1{;UI$pFu=dLYT_=5d8WC-sLfjr7UO-HKMAwa=!>)kEhvuwre zuW3yF@ZxFCkI*+ad|5kOX%5zu8IQjhan)UqgSrFGA_0nQFn@Z08DSEUToCSz4Z1ls z&fDbq$T&7|6iq$_uDI$@q1_kQ@dfqk*0>{SDL6V)94@)ete)j++*>bIc9sj}Y;R1o z#OpH+Yt-^4wfv{nern^iVag8 - - From 545fdbc81037a7c4a16b0fb1d540ae280ca461b6 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 6 Feb 2022 08:30:36 +0800 Subject: [PATCH 021/159] Delete nexmeetup_qrcode.png --- logo/nexmeetup_qrcode.png | Bin 145271 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 logo/nexmeetup_qrcode.png diff --git a/logo/nexmeetup_qrcode.png b/logo/nexmeetup_qrcode.png deleted file mode 100644 index 82ddba8037d75edec856128a7c3450be83fb2dfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145271 zcmeFYWmH>D^ze(jL(xEj7K*z=aA}Z2i(8Nar4)Df;83KvODQQ(plFLr@B+o%O3@<4 zB{zNk@4cVj`{}+P?yPkdNtiWrX3suzw)}RiuC^L60X+d48XB>Ny0RV`8hQol%ErS% z{nBHe5rF!G?xClqh*mQW*h786vRBYjKtubIOn48)Mt#S3RX6rPLnG<^ccBlue6&SF zLzZhOE4=izILyb+GMKr&)LB5c_F>w}v!PjGq<-cNjR38z5L2)eeM|?H)8b{p@Seqi zR>p{gAy=ahpVy-8AD6DaMTSb{xqLooTl>ED-B6~wAVAjX^Y<&uv$M19jeqTh?t1J{ zFRGP-yD8IRP)#i?{3!=1swHD!>p+>294PtC;Os(-QRidOXJv+PV?_WnV0cMn3d|W8?p97}JkC9Mf6!6ax|DT@N`s2Od6iU4eu(FZ)yW-9J;(v{^g0u*RbQ>C2Hd zY?Fqmj753^e6QH7_TQ-FX=8Ns3JZ!f7*RBx!Q8Nk|B(phii?waUhZS^c0A?=jQl^v zi5W}{{om6ZDFSG_1q=DuALO$~_7g2(E?KvMzeQY&&-3pa>fB`U6Pq&g*QdR+FA8kd{cr-967A_Fnm?Dzq>Kew znD;d46$G)$_zev&pD$zTq*4MmBI=9-R(W~^>xp{KPUXeK#9$0rmghE)!Q3gibqFTy zCD)?s)+G7OSZXnoETyP&l~u0VlS}h7K~5T6_1FOKBk<0_#%Vwg<@Aeau$u(?DfiCX z;T<1cKwk`nN>%DR5qTWe4-;~C5LVTMA5OL3E4m|aad%5q6Q_O@D~US%0DZsRucnuD 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 Y9A5`xZV?>dK0^Og6g1?k Date: Sun, 6 Feb 2022 08:48:05 +0800 Subject: [PATCH 022/159] recover the update column width code --- ooxml/XSSF/Streaming/SXSSFSheet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ooxml/XSSF/Streaming/SXSSFSheet.cs b/ooxml/XSSF/Streaming/SXSSFSheet.cs index 72cae76b7..532299f50 100644 --- a/ooxml/XSSF/Streaming/SXSSFSheet.cs +++ b/ooxml/XSSF/Streaming/SXSSFSheet.cs @@ -1375,8 +1375,8 @@ public void FlushRows() var firstRowNum = _rows.Keys.Min(); // Update the best fit column widths for auto-sizing just before the rows are flushed - // _autoSizeColumnTracker.UpdateColumnWidths(row); var firstRow = _rows[firstRowNum]; + _autoSizeColumnTracker.Value?.UpdateColumnWidths(firstRow); _writer.WriteRow(firstRowNum, firstRow); _rows.Remove(firstRowNum); lastFlushedRowNumber = firstRowNum; From aca40088850f47e92fe340b1ccc1c5408e53e9b3 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 6 Feb 2022 09:07:47 +0800 Subject: [PATCH 023/159] Validate SheetName for all the entries #619 --- main/HSSF/Model/InternalWorkbook.cs | 4 ---- main/HSSF/UserModel/HSSFWorkbook.cs | 4 +++- ooxml/XSSF/Streaming/SXSSFWorkbook.cs | 1 + ooxml/XSSF/UserModel/XSSFWorkbook.cs | 4 +--- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/main/HSSF/Model/InternalWorkbook.cs b/main/HSSF/Model/InternalWorkbook.cs index 3213b6da7..8221c238a 100644 --- a/main/HSSF/Model/InternalWorkbook.cs +++ b/main/HSSF/Model/InternalWorkbook.cs @@ -656,10 +656,6 @@ public void SetSheetName(int sheetnum, String sheetname) { CheckSheets(sheetnum); - // YK: Mimic Excel and silently truncate sheet names longer than 31 characters - if (sheetname.Length > 31) - sheetname = sheetname.Substring(0, 31); - BoundSheetRecord sheet =boundsheets[sheetnum]; sheet.Sheetname=sheetname; } diff --git a/main/HSSF/UserModel/HSSFWorkbook.cs b/main/HSSF/UserModel/HSSFWorkbook.cs index 596a2e176..30f3d26e0 100644 --- a/main/HSSF/UserModel/HSSFWorkbook.cs +++ b/main/HSSF/UserModel/HSSFWorkbook.cs @@ -877,7 +877,9 @@ public ISheet CreateSheet(String sheetname) if (workbook.ContainsSheetName(sheetname, _sheets.Count)) throw new ArgumentException("The workbook already contains a sheet named '" + sheetname + "'"); - HSSFSheet sheet = new HSSFSheet(this); + WorkbookUtil.ValidateSheetName(sheetname); + + HSSFSheet sheet = new HSSFSheet(this); workbook.SetSheetName(_sheets.Count, sheetname); _sheets.Add(sheet); diff --git a/ooxml/XSSF/Streaming/SXSSFWorkbook.cs b/ooxml/XSSF/Streaming/SXSSFWorkbook.cs index b85f4b444..676ddc31f 100644 --- a/ooxml/XSSF/Streaming/SXSSFWorkbook.cs +++ b/ooxml/XSSF/Streaming/SXSSFWorkbook.cs @@ -648,6 +648,7 @@ public ISheet CreateSheet() public ISheet CreateSheet(string sheetname) { + SS.Util.WorkbookUtil.ValidateSheetName(sheetname); return CreateAndRegisterSXSSFSheet(XssfWorkbook.CreateSheet(sheetname)); } diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index e489ca337..c8f3fb798 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -596,6 +596,7 @@ public ISheet CloneSheet(int sheetNum, String newName) else { ValidateSheetName(newName); + WorkbookUtil.ValidateSheetName(newName); } XSSFSheet clonedSheet = CreateSheet(newName) as XSSFSheet; @@ -880,9 +881,6 @@ public ISheet CreateSheet(String sheetname) } ValidateSheetName(sheetname); - - // YK: Mimic Excel and silently tRuncate sheet names longer than 31 characters - if (sheetname.Length > 31) sheetname = sheetname.Substring(0, 31); WorkbookUtil.ValidateSheetName(sheetname); CT_Sheet sheet = AddSheet(sheetname); From d1699ede97aabdab281f0f828177dba6f33d4d60 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 7 Feb 2022 06:41:52 +0800 Subject: [PATCH 024/159] avoid memory consumption while calling CloneSheet --- ooxml/XSSF/UserModel/XSSFSheet.cs | 7 ++++--- ooxml/XSSF/UserModel/XSSFWorkbook.cs | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ooxml/XSSF/UserModel/XSSFSheet.cs b/ooxml/XSSF/UserModel/XSSFSheet.cs index 13a5641b3..bdefc6c39 100644 --- a/ooxml/XSSF/UserModel/XSSFSheet.cs +++ b/ooxml/XSSF/UserModel/XSSFSheet.cs @@ -4773,10 +4773,11 @@ public ISheet CopySheet(String name, Boolean copyStyle) try { - using (MemoryStream out1 = new MemoryStream()) + using (MemoryStream ms = new MemoryStream()) { - this.Write(out1); - clonedSheet.Read(new MemoryStream(out1.ToArray())); + this.Write(ms); + ms.Position = 0; + clonedSheet.Read(ms); } } catch (IOException e) diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index c8f3fb798..5222ab0d1 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -636,10 +636,11 @@ public ISheet CloneSheet(int sheetNum, String newName) try { - using (MemoryStream out1 = new MemoryStream()) + using (MemoryStream ms = new MemoryStream()) { - srcSheet.Write(out1); - clonedSheet.Read(new MemoryStream(out1.ToArray())); + this.Write(ms); + ms.Position = 0; + clonedSheet.Read(ms); } } catch (IOException e) From 031ce711b0c21ec3c982a29eedc32d25d86e37ca Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 7 Feb 2022 07:05:02 +0800 Subject: [PATCH 025/159] add leaveOpen argument to WorksheetDocument.Save --- .../Spreadsheet/Document/WorksheetDocument.cs | 4 ++-- OpenXmlFormats/Spreadsheet/Sheet/CT_Worksheet.cs | 13 ++++++++++--- ooxml/XSSF/UserModel/XSSFChartSheet.cs | 2 +- ooxml/XSSF/UserModel/XSSFSheet.cs | 7 ++++--- ooxml/XSSF/UserModel/XSSFWorkbook.cs | 2 +- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/Document/WorksheetDocument.cs b/OpenXmlFormats/Spreadsheet/Document/WorksheetDocument.cs index e5a9a1638..e6fe1b673 100644 --- a/OpenXmlFormats/Spreadsheet/Document/WorksheetDocument.cs +++ b/OpenXmlFormats/Spreadsheet/Document/WorksheetDocument.cs @@ -29,9 +29,9 @@ public void SetChartsheet(CT_Worksheet sheet) { this.sheet = sheet; } - public void Save(Stream stream) + public void Save(Stream stream, bool leaveOpen) { - this.sheet.Write(stream); + this.sheet.Write(stream, leaveOpen); } } } diff --git a/OpenXmlFormats/Spreadsheet/Sheet/CT_Worksheet.cs b/OpenXmlFormats/Spreadsheet/Sheet/CT_Worksheet.cs index 2881e80bf..78cf6949d 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet/CT_Worksheet.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet/CT_Worksheet.cs @@ -183,10 +183,10 @@ public static CT_Worksheet Parse(XmlNode node, XmlNamespaceManager namespaceMana - internal void Write(Stream stream) + internal void Write(Stream stream, bool leaveOpen) { - using (StreamWriter sw = new StreamWriter(stream)) - { + StreamWriter sw = new StreamWriter(stream); + try { sw.Write(""); sw.Write(""); + sw.Flush(); + } finally + { + if (!leaveOpen ) + { + sw?.Close(); + } } } diff --git a/ooxml/XSSF/UserModel/XSSFChartSheet.cs b/ooxml/XSSF/UserModel/XSSFChartSheet.cs index f6d94f641..563f3b3f6 100644 --- a/ooxml/XSSF/UserModel/XSSFChartSheet.cs +++ b/ooxml/XSSF/UserModel/XSSFChartSheet.cs @@ -96,7 +96,7 @@ protected override NPOI.OpenXmlFormats.Spreadsheet.CT_LegacyDrawing GetCTLegacyD } - internal override void Write(Stream out1) + internal override void Write(Stream out1, bool leaveOpen=false) { new ChartsheetDocument(this.chartsheet).Save(out1); } diff --git a/ooxml/XSSF/UserModel/XSSFSheet.cs b/ooxml/XSSF/UserModel/XSSFSheet.cs index bdefc6c39..d12ea9a9c 100644 --- a/ooxml/XSSF/UserModel/XSSFSheet.cs +++ b/ooxml/XSSF/UserModel/XSSFSheet.cs @@ -3752,7 +3752,7 @@ protected internal override void Commit() out1.Close(); } - internal virtual void Write(Stream stream) + internal virtual void Write(Stream stream, bool leaveOpen=false) { bool setToNull = false; if (worksheet.sizeOfColsArray() == 1) @@ -3804,7 +3804,7 @@ NPOI.OpenXmlFormats.Spreadsheet.CT_Hyperlink[] ctHls map[ST_RelationshipId.NamespaceURI] = "r"; //xmlOptions.SetSaveSuggestedPrefixes(map); - new WorksheetDocument(worksheet).Save(stream); + new WorksheetDocument(worksheet).Save(stream, leaveOpen); // Bug 52233: Ensure that we have a col-array even if write() removed it if (setToNull) @@ -4775,7 +4775,8 @@ public ISheet CopySheet(String name, Boolean copyStyle) { using (MemoryStream ms = new MemoryStream()) { - this.Write(ms); + this.Write(ms, true); + string decoded = Encoding.UTF8.GetString(ms.ToArray()); ms.Position = 0; clonedSheet.Read(ms); } diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index 5222ab0d1..dac856280 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -638,7 +638,7 @@ public ISheet CloneSheet(int sheetNum, String newName) { using (MemoryStream ms = new MemoryStream()) { - this.Write(ms); + this.Write(ms, true); ms.Position = 0; clonedSheet.Read(ms); } From 47398319555a7ae8c7b09e3d3e241df8f14c4c00 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 7 Feb 2022 07:05:40 +0800 Subject: [PATCH 026/159] remove unnecessary code --- ooxml/XSSF/UserModel/XSSFSheet.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/ooxml/XSSF/UserModel/XSSFSheet.cs b/ooxml/XSSF/UserModel/XSSFSheet.cs index d12ea9a9c..d5c82446e 100644 --- a/ooxml/XSSF/UserModel/XSSFSheet.cs +++ b/ooxml/XSSF/UserModel/XSSFSheet.cs @@ -4776,7 +4776,6 @@ public ISheet CopySheet(String name, Boolean copyStyle) using (MemoryStream ms = new MemoryStream()) { this.Write(ms, true); - string decoded = Encoding.UTF8.GetString(ms.ToArray()); ms.Position = 0; clonedSheet.Read(ms); } From b5168c85a861b5bde90389948539fa7896a52707 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 8 Feb 2022 02:55:54 +0800 Subject: [PATCH 027/159] remove LoadAndSaveBack tool --- tools/LoadAndSaveBack/LoadAndSaveBack.csproj | 81 ------------------- tools/LoadAndSaveBack/LoadAndSaveBack.sln | 44 ---------- tools/LoadAndSaveBack/Program.cs | 54 ------------- .../Properties/AssemblyInfo.cs | 36 --------- tools/LoadAndSaveBack/app.config | 3 - 5 files changed, 218 deletions(-) delete mode 100644 tools/LoadAndSaveBack/LoadAndSaveBack.csproj delete mode 100644 tools/LoadAndSaveBack/LoadAndSaveBack.sln delete mode 100644 tools/LoadAndSaveBack/Program.cs delete mode 100644 tools/LoadAndSaveBack/Properties/AssemblyInfo.cs delete mode 100644 tools/LoadAndSaveBack/app.config diff --git a/tools/LoadAndSaveBack/LoadAndSaveBack.csproj b/tools/LoadAndSaveBack/LoadAndSaveBack.csproj deleted file mode 100644 index 9c86660af..000000000 --- a/tools/LoadAndSaveBack/LoadAndSaveBack.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {222522DC-3C3F-4D75-B72B-98B736FB202A} - Exe - Properties - LoadAndSaveBack - LoadAndSaveBack - v4.5 - 512 - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - 1.3.1 - - - - - {10fa8538-157a-4380-a4f6-8e2c3ee92cae} - NPOI - - - {6beed965-b9a0-4ffa-b96d-0f380a97331a} - NPOI.OOXML - - - {c9f265b7-ece3-4755-b0b1-79536575c2a9} - NPOI.OpenXml4Net - - - {a6874784-2875-4f40-9e8f-8385a640f5d6} - NPOI.OpenXmlFormats - - - - - \ No newline at end of file diff --git a/tools/LoadAndSaveBack/LoadAndSaveBack.sln b/tools/LoadAndSaveBack/LoadAndSaveBack.sln deleted file mode 100644 index f464d77bc..000000000 --- a/tools/LoadAndSaveBack/LoadAndSaveBack.sln +++ /dev/null @@ -1,44 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoadAndSaveBack", "LoadAndSaveBack.csproj", "{222522DC-3C3F-4D75-B72B-98B736FB202A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPOI", "..\..\main\NPOI.csproj", "{10FA8538-157A-4380-A4F6-8E2C3EE92CAE}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPOI.OOXML", "..\..\ooxml\NPOI.OOXML.csproj", "{6BEED965-B9A0-4FFA-B96D-0F380A97331A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPOI.OpenXml4Net", "..\..\openxml4Net\NPOI.OpenXml4Net.csproj", "{C9F265B7-ECE3-4755-B0B1-79536575C2A9}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPOI.OpenXmlFormats", "..\..\OpenXmlFormats\NPOI.OpenXmlFormats.csproj", "{A6874784-2875-4F40-9E8F-8385A640F5D6}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {222522DC-3C3F-4D75-B72B-98B736FB202A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {222522DC-3C3F-4D75-B72B-98B736FB202A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {222522DC-3C3F-4D75-B72B-98B736FB202A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {222522DC-3C3F-4D75-B72B-98B736FB202A}.Release|Any CPU.Build.0 = Release|Any CPU - {10FA8538-157A-4380-A4F6-8E2C3EE92CAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {10FA8538-157A-4380-A4F6-8E2C3EE92CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10FA8538-157A-4380-A4F6-8E2C3EE92CAE}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {10FA8538-157A-4380-A4F6-8E2C3EE92CAE}.Release|Any CPU.Build.0 = Debug|Any CPU - {6BEED965-B9A0-4FFA-B96D-0F380A97331A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BEED965-B9A0-4FFA-B96D-0F380A97331A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BEED965-B9A0-4FFA-B96D-0F380A97331A}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {6BEED965-B9A0-4FFA-B96D-0F380A97331A}.Release|Any CPU.Build.0 = Debug|Any CPU - {C9F265B7-ECE3-4755-B0B1-79536575C2A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C9F265B7-ECE3-4755-B0B1-79536575C2A9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9F265B7-ECE3-4755-B0B1-79536575C2A9}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {C9F265B7-ECE3-4755-B0B1-79536575C2A9}.Release|Any CPU.Build.0 = Debug|Any CPU - {A6874784-2875-4F40-9E8F-8385A640F5D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A6874784-2875-4F40-9E8F-8385A640F5D6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A6874784-2875-4F40-9E8F-8385A640F5D6}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {A6874784-2875-4F40-9E8F-8385A640F5D6}.Release|Any CPU.Build.0 = Debug|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/tools/LoadAndSaveBack/Program.cs b/tools/LoadAndSaveBack/Program.cs deleted file mode 100644 index 8d5eaeb7e..000000000 --- a/tools/LoadAndSaveBack/Program.cs +++ /dev/null @@ -1,54 +0,0 @@ -using NPOI.SS.UserModel; -using NPOI.XSSF.UserModel; -using NPOI.XWPF.UserModel; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace LoadAndSaveBack -{ - class Program - { - enum RunMode - { - Excel, - Word - } - static void Main(string[] args) - { - if (args.Length != 2) - return; - - string src = args[0]; - string target = args[1]; - - - RunMode mode= RunMode.Excel; - if (src.Contains(".docx")) - mode = RunMode.Word; - - if (mode == RunMode.Excel) - { - Stream rfs = File.OpenRead(src); - IWorkbook workbook = new XSSFWorkbook(rfs); - rfs.Close(); - using (FileStream fs = File.Create(target)) - { - workbook.Write(fs); - } - } - else - { - Stream rfs = File.OpenRead(src); - XWPFDocument workbook = new XWPFDocument(rfs); - rfs.Close(); - using (FileStream fs = File.Create(target)) - { - workbook.Write(fs); - } - } - } - } -} diff --git a/tools/LoadAndSaveBack/Properties/AssemblyInfo.cs b/tools/LoadAndSaveBack/Properties/AssemblyInfo.cs deleted file mode 100644 index 03e69dd21..000000000 --- a/tools/LoadAndSaveBack/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("LoadAndSaveBack")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("LoadAndSaveBack")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("a4ac1d0b-c61b-4db0-bc67-5854ab2ef441")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tools/LoadAndSaveBack/app.config b/tools/LoadAndSaveBack/app.config deleted file mode 100644 index c5e1daefd..000000000 --- a/tools/LoadAndSaveBack/app.config +++ /dev/null @@ -1,3 +0,0 @@ - - - From 2cf5ded3e183ee51873f4255d25936bd5e43bc05 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 04:44:00 +0800 Subject: [PATCH 028/159] Create 52447.xls --- testcases/test-data/spreadsheet/52447.xls | Bin 0 -> 54784 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testcases/test-data/spreadsheet/52447.xls diff --git a/testcases/test-data/spreadsheet/52447.xls b/testcases/test-data/spreadsheet/52447.xls new file mode 100644 index 0000000000000000000000000000000000000000..470692d8a75a0c92a1523d86dacd55a075b13856 GIT binary patch literal 54784 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 HhX?)-DRU>y literal 0 HcmV?d00001 From 7a2cb9ee0cb58853b31bb0e302cc2a29184986de Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 04:46:26 +0800 Subject: [PATCH 029/159] Delete 52447.xls --- testcases/test-data/spreadsheet/52447.xls | Bin 54784 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 testcases/test-data/spreadsheet/52447.xls diff --git a/testcases/test-data/spreadsheet/52447.xls b/testcases/test-data/spreadsheet/52447.xls deleted file mode 100644 index 470692d8a75a0c92a1523d86dacd55a075b13856..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54784 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 HhX?)-DRU>y From d96ec548d6d29b8ea45d9a346840bc72f18d2587 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 04:54:04 +0800 Subject: [PATCH 030/159] fix compilation issue --- main/HSSF/Record/Aggregates/RowRecordsAggregate.cs | 5 ++++- testcases/main/HSSF/UserModel/TestBugs.cs | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/main/HSSF/Record/Aggregates/RowRecordsAggregate.cs b/main/HSSF/Record/Aggregates/RowRecordsAggregate.cs index 8c98f632d..cfd926e42 100644 --- a/main/HSSF/Record/Aggregates/RowRecordsAggregate.cs +++ b/main/HSSF/Record/Aggregates/RowRecordsAggregate.cs @@ -48,7 +48,10 @@ public class RowRecordsAggregate : RecordAggregate // Cache values to speed up performance of // getStartRowNumberForBlock / getEndRowNumberForBlock, see Bugzilla 47405 private RowRecord[] _rowRecordValues = null; - public IEnumerable HyperlinkRecordRecords { get => _hyperlinkRecordRecords; } + public IEnumerable HyperlinkRecordRecords + { + get { return _hyperlinkRecordRecords; } + } /** Creates a new instance of ValueRecordsAggregate */ diff --git a/testcases/main/HSSF/UserModel/TestBugs.cs b/testcases/main/HSSF/UserModel/TestBugs.cs index 8bb6fa2b6..cae0f1265 100644 --- a/testcases/main/HSSF/UserModel/TestBugs.cs +++ b/testcases/main/HSSF/UserModel/TestBugs.cs @@ -3489,11 +3489,14 @@ public void Test53564() // follow https://svn.apache.org/viewvc?view=revision&revision=1896552 to write a unit test for this fix. [Test] - public void test52447() + public void Test52447() { - using (IWorkbook wb = HSSFTestDataSamples.OpenSampleWorkbook("52447.xls")) + IWorkbook wb=null; + try { - Assert.IsNotNull(cell.CellFormula); + wb = HSSFTestDataSamples.OpenSampleWorkbook("52447.xls"); + } catch { + Assert.IsNotNull(wb); } } } From 124e26a15dad899ddf451d348cbe7529a95e41f4 Mon Sep 17 00:00:00 2001 From: Pieter Bart van Straalen Date: Tue, 8 Feb 2022 22:29:52 +0100 Subject: [PATCH 031/159] Added table caption and description parsing + unit tests --- OpenXmlFormats/Wordprocessing/Table.cs | 46 +++++++++++++++ ooxml/XWPF/Usermodel/XWPFTable.cs | 53 ++++++++++++++++++ .../ooxml/NPOI.OOXML.TestCases.Core.csproj | 18 ++++++ .../ooxml/Properties/launchSettings.json | 10 ++++ .../ooxml/XWPF/UserModel/TestXWPFTable.cs | 50 +++++++++++++++++ .../test-data/document/table_properties.docx | Bin 0 -> 12421 bytes 6 files changed, 177 insertions(+) create mode 100644 testcases/ooxml/Properties/launchSettings.json create mode 100644 testcases/test-data/document/table_properties.docx diff --git a/OpenXmlFormats/Wordprocessing/Table.cs b/OpenXmlFormats/Wordprocessing/Table.cs index 9c0d769c3..137ab626b 100644 --- a/OpenXmlFormats/Wordprocessing/Table.cs +++ b/OpenXmlFormats/Wordprocessing/Table.cs @@ -1176,6 +1176,10 @@ public class CT_TblPrBase private CT_ShortHexNumber tblLookField; + private CT_String tblCaptionField; + + private CT_String tblDescriptionField; + public CT_TblPrBase() { } @@ -1216,6 +1220,10 @@ public static CT_TblPrBase Parse(XmlNode node, XmlNamespaceManager namespaceMana ctObj.tblCellMar = CT_TblCellMar.Parse(childNode, namespaceManager); else if (childNode.LocalName == "tblLook") ctObj.tblLook = CT_ShortHexNumber.Parse(childNode, namespaceManager); + else if (childNode.LocalName == "tblCaption") + ctObj.tblCaption = CT_String.Parse(childNode, namespaceManager); + else if (childNode.LocalName == "tblDescription") + ctObj.tblDescription = CT_String.Parse(childNode, namespaceManager); } return ctObj; } @@ -1256,6 +1264,10 @@ internal void Write(StreamWriter sw, string nodeName) this.tblCellMar.Write(sw, "tblCellMar"); if (this.tblLook != null) this.tblLook.Write(sw, "tblLook"); + if (this.tblCaption != null) + this.tblCaption.Write(sw, "tblCaption"); + if (this.tblDescription != null) + this.tblDescription.Write(sw, "tblDescription"); sw.Write(string.Format("", nodeName)); } @@ -1454,6 +1466,32 @@ public CT_ShortHexNumber tblLook } } + [XmlElement(Order = 15)] + public CT_String tblCaption + { + get + { + return this.tblCaptionField; + } + set + { + this.tblCaptionField = value; + } + } + + [XmlElement(Order = 16)] + public CT_String tblDescription + { + get + { + return this.tblDescriptionField; + } + set + { + this.tblDescriptionField = value; + } + } + public bool IsSetTblW() { return this.tblW != null; @@ -2776,6 +2814,10 @@ public static new CT_TblPr Parse(XmlNode node, XmlNamespaceManager namespaceMana ctObj.tblCellMar = CT_TblCellMar.Parse(childNode, namespaceManager); else if (childNode.LocalName == "tblLook") ctObj.tblLook = CT_ShortHexNumber.Parse(childNode, namespaceManager); + else if (childNode.LocalName == "tblCaption") + ctObj.tblCaption = CT_String.Parse(childNode, namespaceManager); + else if (childNode.LocalName == "tblDescription") + ctObj.tblDescription = CT_String.Parse(childNode, namespaceManager); } return ctObj; } @@ -2818,6 +2860,10 @@ internal new void Write(StreamWriter sw, string nodeName) this.tblCellMar.Write(sw, "tblCellMar"); if (this.tblLook != null) this.tblLook.Write(sw, "tblLook"); + if (this.tblCaption != null) + this.tblCaption.Write(sw, "tblCaption"); + if (this.tblDescription != null) + this.tblDescription.Write(sw, "tblDescription"); sw.Write(string.Format("", nodeName)); } diff --git a/ooxml/XWPF/Usermodel/XWPFTable.cs b/ooxml/XWPF/Usermodel/XWPFTable.cs index 7c9941cd7..5b64de3d0 100644 --- a/ooxml/XWPF/Usermodel/XWPFTable.cs +++ b/ooxml/XWPF/Usermodel/XWPFTable.cs @@ -654,6 +654,58 @@ public int CellMarginRight } } + public string TableCaption + { + get + { + CT_TblPr tblPr = GetTrPr(); + if (tblPr.tblCaption != null) + return tblPr.tblCaption.val; + else + return string.Empty; + } + set + { + CT_TblPr tblPr = GetTrPr(); + if (tblPr.tblCaption == null) + { + CT_String caption = new CT_String(); + caption.val = value; + tblPr.tblCaption = caption; + } + else + { + tblPr.tblCaption.val = value; + } + } + } + + public string TableDescription + { + get + { + CT_TblPr tblPr = GetTrPr(); + if (tblPr.tblDescription != null) + return tblPr.tblDescription.val; + else + return string.Empty; + } + set + { + CT_TblPr tblPr = GetTrPr(); + if (tblPr.tblDescription == null) + { + CT_String desc = new CT_String(); + desc.val = value; + tblPr.tblDescription = desc; + } + else + { + tblPr.tblDescription.val = value; + } + } + } + public void SetCellMargins(int top, int left, int bottom, int right) { CT_TblPr tblPr = GetTrPr(); @@ -807,6 +859,7 @@ public XWPFTableRow GetRow(CT_Row row) } return null; } + }// end class } \ No newline at end of file diff --git a/testcases/ooxml/NPOI.OOXML.TestCases.Core.csproj b/testcases/ooxml/NPOI.OOXML.TestCases.Core.csproj index e4570c653..4f602d91f 100644 --- a/testcases/ooxml/NPOI.OOXML.TestCases.Core.csproj +++ b/testcases/ooxml/NPOI.OOXML.TestCases.Core.csproj @@ -35,6 +35,24 @@ + + + True + True + Settings.settings + + + + + + Always + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + diff --git a/testcases/ooxml/Properties/launchSettings.json b/testcases/ooxml/Properties/launchSettings.json new file mode 100644 index 000000000..16ac5dba3 --- /dev/null +++ b/testcases/ooxml/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "NPOI.OOXML.TestCases.Core": { + "commandName": "Project", + "environmentVariables": { + "TEST_PROPERTY": "..\\..\\..\\..\\test-data" + } + } + } +} \ No newline at end of file diff --git a/testcases/ooxml/XWPF/UserModel/TestXWPFTable.cs b/testcases/ooxml/XWPF/UserModel/TestXWPFTable.cs index 30d7718fd..50770c8ab 100644 --- a/testcases/ooxml/XWPF/UserModel/TestXWPFTable.cs +++ b/testcases/ooxml/XWPF/UserModel/TestXWPFTable.cs @@ -21,6 +21,7 @@ namespace TestCases.XWPF.UserModel using NPOI.OpenXmlFormats.Wordprocessing; using System.Collections.Generic; using NPOI.XWPF.UserModel; + using System.Reflection; /** @@ -173,6 +174,7 @@ public void TestSetGetMargins() int r = table.CellMarginRight; Assert.AreEqual(450, r); } + [Test] public void TestSetGetHBorders() { @@ -266,5 +268,53 @@ public void TestCreateTable() } doc.Package.Revert(); } + + [Test] + public void TestSetTableCaption() + { + // create a table + XWPFDocument doc = new XWPFDocument(); + CT_Tbl ctTable = new CT_Tbl(); + XWPFTable table = new XWPFTable(ctTable, doc); + + //Set Caption + table.TableCaption = "%TABLECAPTION%"; + + //Check + CT_String tblCaption = table.GetCTTbl().tblPr.tblCaption; + Assert.IsNotNull(tblCaption); + Assert.AreEqual("%TABLECAPTION%", tblCaption.val); + } + + [Test] + public void TestSetTableDescription() + { + // create a table + XWPFDocument doc = new XWPFDocument(); + CT_Tbl ctTable = new CT_Tbl(); + XWPFTable table = new XWPFTable(ctTable, doc); + + //Set Description + table.TableDescription = "%TABLEDESCRIPTION%"; + + //Check + CT_String tblDesc = table.GetCTTbl().tblPr.tblDescription; + Assert.IsNotNull(tblDesc); + Assert.AreEqual("%TABLEDESCRIPTION%", tblDesc.val); + } + + [Test] + public void TestReadTableCaptionAndDescription() + { + // open an document with table containing Table caption and Table Description + XWPFDocument doc = XWPFTestDataSamples.OpenSampleDocument("table_properties.docx"); + + //Get Table + var table = doc.Tables[0]; + + //Assert Table Caption & Description + Assert.AreEqual("Table Title", table.TableCaption); + Assert.AreEqual("Table Description", table.TableDescription); + } } } \ No newline at end of file diff --git a/testcases/test-data/document/table_properties.docx b/testcases/test-data/document/table_properties.docx new file mode 100644 index 0000000000000000000000000000000000000000..b68438f88173e2c130f780847bfab67b38504114 GIT binary patch literal 12421 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 JjQ;1-{{uP-JL>=d literal 0 HcmV?d00001 From 4660d5039d1c70eadca89e05abf4c20d9a18c162 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 05:32:27 +0800 Subject: [PATCH 032/159] fix SheetDataWriterTests --- ooxml/XSSF/Streaming/SXSSFRow.cs | 49 ++++++++----------- .../XSSF/Streaming/SheetDataWriterTests.cs | 6 ++- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/ooxml/XSSF/Streaming/SXSSFRow.cs b/ooxml/XSSF/Streaming/SXSSFRow.cs index 2fbf5b18b..d12cd7cc7 100644 --- a/ooxml/XSSF/Streaming/SXSSFRow.cs +++ b/ooxml/XSSF/Streaming/SXSSFRow.cs @@ -31,11 +31,11 @@ public class SXSSFRow : IRow, IComparable private bool _zHeight; // row zero-height (this is somehow different than being hidden) private float _height = -1; - private int _FirstCellNum = -1; - private int _LastCellNum = -1; + private int _firstCellNum = -1; + private int _lastCellNum = -1; // use Boolean to have a tri-state for on/off/undefined - public bool? Hidden { get; set; } - public bool? Collapsed { get; set; } + public virtual bool? Hidden { get; set; } + public virtual bool? Collapsed { get; set; } public SXSSFRow(SXSSFSheet sheet) { @@ -46,7 +46,7 @@ public CellIterator AllCellsIterator() { return new CellIterator(LastCellNum, new SortedDictionary(_cells)); } - public bool HasCustomHeight() + public virtual bool HasCustomHeight() { return Height != -1; } @@ -60,14 +60,7 @@ public short FirstCellNum { get { - try - { - return (short) _FirstCellNum; - } - catch - { - return -1; - } + return (short) _firstCellNum; } } @@ -93,7 +86,7 @@ public float HeightInPoints } } - public bool IsFormatted + public virtual bool IsFormatted { get { @@ -105,12 +98,12 @@ public short LastCellNum { get { - return (short) _LastCellNum; + return (short) _lastCellNum; } } - public int OutlineLevel { get; set; } + public virtual int OutlineLevel { get; set; } public int PhysicalNumberOfCells { @@ -138,7 +131,7 @@ internal int RowStyleIndex } } - public ICellStyle RowStyle + public virtual ICellStyle RowStyle { get { @@ -165,7 +158,7 @@ public ISheet Sheet get { return _sheet; } } - public bool ZeroHeight + public virtual bool ZeroHeight { get { return _zHeight; } @@ -249,14 +242,14 @@ public ICell CreateCell(int column, CellType type) private void UpdateIndexWhenAdd(int cellnum) { - if (cellnum < _FirstCellNum || _FirstCellNum == -1) + if (cellnum < _firstCellNum || _firstCellNum == -1) { - _FirstCellNum = cellnum; + _firstCellNum = cellnum; } - if (cellnum >= _LastCellNum) + if (cellnum >= _lastCellNum) { - _LastCellNum = cellnum + 1; + _lastCellNum = cellnum + 1; } } @@ -319,12 +312,12 @@ public void RemoveCell(ICell cell) { int index = GetCellIndex((SXSSFCell)cell); _cells.Remove(index); - if (index == _FirstCellNum) + if (index == _firstCellNum) { InvalidateFirstCellNum(); } - if (index >= (_LastCellNum -1)) + if (index >= _lastCellNum -1) { InvalidateLastCellNum(); } @@ -334,11 +327,11 @@ private void InvalidateFirstCellNum() { if (_cells.Keys.Count == 0) { - _FirstCellNum = 0; + _firstCellNum = -1; } else { - _FirstCellNum = _cells.Keys.Min(); + _firstCellNum = _cells.Keys.Min(); } } @@ -346,11 +339,11 @@ private void InvalidateLastCellNum() { if (_cells.Count == 0) { - _LastCellNum = 0; + _lastCellNum = -1; } else { - _LastCellNum = _cells.Keys.Max() + 1; + _lastCellNum = _cells.Keys.Max() + 1; } } diff --git a/testcases/ooxml/XSSF/Streaming/SheetDataWriterTests.cs b/testcases/ooxml/XSSF/Streaming/SheetDataWriterTests.cs index ca58d8fde..c2d5add08 100644 --- a/testcases/ooxml/XSSF/Streaming/SheetDataWriterTests.cs +++ b/testcases/ooxml/XSSF/Streaming/SheetDataWriterTests.cs @@ -16,6 +16,7 @@ ==================================================================== */ using NPOI.SS.UserModel; using NPOI.XSSF.Streaming; +using NPOI.XSSF.UserModel; using NSubstitute; using NUnit.Framework; using System.IO; @@ -32,7 +33,10 @@ public class SheetDataWriterTests [SetUp] public void Init() { - _row = Substitute.For(); + var _xssfsheet = Substitute.For(); + var _workbook = Substitute.For(); + var _sheet = Substitute.For(_workbook, _xssfsheet); + _row = Substitute.For(_sheet); _cell = Substitute.For(); } From 04c402a0431f5e085e3b2b2348e3e18341e62f1a Mon Sep 17 00:00:00 2001 From: Pieter Bart van Straalen Date: Tue, 8 Feb 2022 22:41:34 +0100 Subject: [PATCH 033/159] Cleanup --- .../ooxml/NPOI.OOXML.TestCases.Core.csproj | 18 ------------------ testcases/ooxml/Properties/launchSettings.json | 10 ---------- 2 files changed, 28 deletions(-) delete mode 100644 testcases/ooxml/Properties/launchSettings.json diff --git a/testcases/ooxml/NPOI.OOXML.TestCases.Core.csproj b/testcases/ooxml/NPOI.OOXML.TestCases.Core.csproj index 4f602d91f..e4570c653 100644 --- a/testcases/ooxml/NPOI.OOXML.TestCases.Core.csproj +++ b/testcases/ooxml/NPOI.OOXML.TestCases.Core.csproj @@ -35,24 +35,6 @@ - - - True - True - Settings.settings - - - - - - Always - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - diff --git a/testcases/ooxml/Properties/launchSettings.json b/testcases/ooxml/Properties/launchSettings.json deleted file mode 100644 index 16ac5dba3..000000000 --- a/testcases/ooxml/Properties/launchSettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "profiles": { - "NPOI.OOXML.TestCases.Core": { - "commandName": "Project", - "environmentVariables": { - "TEST_PROPERTY": "..\\..\\..\\..\\test-data" - } - } - } -} \ No newline at end of file From c863524baf4c9c7f4bc4f9c13b260e55583f6856 Mon Sep 17 00:00:00 2001 From: Pieter Bart van Straalen Date: Tue, 8 Feb 2022 22:43:56 +0100 Subject: [PATCH 034/159] Code cleanup --- testcases/ooxml/XWPF/UserModel/TestXWPFTable.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/testcases/ooxml/XWPF/UserModel/TestXWPFTable.cs b/testcases/ooxml/XWPF/UserModel/TestXWPFTable.cs index 50770c8ab..1bc83ff05 100644 --- a/testcases/ooxml/XWPF/UserModel/TestXWPFTable.cs +++ b/testcases/ooxml/XWPF/UserModel/TestXWPFTable.cs @@ -21,7 +21,6 @@ namespace TestCases.XWPF.UserModel using NPOI.OpenXmlFormats.Wordprocessing; using System.Collections.Generic; using NPOI.XWPF.UserModel; - using System.Reflection; /** From a76949e70e7ef8126dce2e3d4111541cd8a606ad Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 06:40:23 +0800 Subject: [PATCH 035/159] fix CloneSheet regression bug --- ooxml/XSSF/UserModel/XSSFWorkbook.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index dac856280..dcafea1c8 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -638,7 +638,7 @@ public ISheet CloneSheet(int sheetNum, String newName) { using (MemoryStream ms = new MemoryStream()) { - this.Write(ms, true); + srcSheet.Write(ms, true); ms.Position = 0; clonedSheet.Read(ms); } From d398fa7c6edc1593238a7e5ad5fb54d4a9707abc Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 07:10:10 +0800 Subject: [PATCH 036/159] add new param leaveOpen to IWorkbook.Write method --- main/HSSF/UserModel/HSSFWorkbook.cs | 24 +++++++++++------------- main/SS/UserModel/Workbook.cs | 2 +- ooxml/XSSF/Streaming/SXSSFWorkbook.cs | 8 +++++++- ooxml/XSSF/UserModel/XSSFWorkbook.cs | 2 +- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/main/HSSF/UserModel/HSSFWorkbook.cs b/main/HSSF/UserModel/HSSFWorkbook.cs index 30f3d26e0..75925a154 100644 --- a/main/HSSF/UserModel/HSSFWorkbook.cs +++ b/main/HSSF/UserModel/HSSFWorkbook.cs @@ -1348,19 +1348,7 @@ public override void Write(FileInfo newFile) fs.Close(); } } - - /// - /// Write out this workbook to an Outputstream. Constructs - /// a new POI POIFSFileSystem, passes in the workbook binary representation and - /// Writes it out. - /// - /// If {@code stream} is a {@link java.io.FileOutputStream} on a networked drive - /// or has a high cost/latency associated with each written byte, - /// consider wrapping the OutputStream in a {@link java.io.BufferedOutputStream} - /// to improve write performance. - /// - /// - /// the java OutputStream you wish to Write the XLS to + public override void Write(Stream stream) { NPOIFSFileSystem fs = new NPOIFSFileSystem(); @@ -1374,6 +1362,16 @@ public override void Write(Stream stream) fs.Close(); } } + /// + /// Write out this workbook to an Outputstream. Constructs + /// a new POI POIFSFileSystem, passes in the workbook binary representation and + /// Writes it out. + /// + /// the stream you wish to Write the XLS to + public void Write(Stream stream, bool leaveOpen=false) + { + this.Write(stream); + } /** Writes the workbook out to a brand new, empty POIFS */ private void Write(NPOIFSFileSystem fs) diff --git a/main/SS/UserModel/Workbook.cs b/main/SS/UserModel/Workbook.cs index 7bed60150..977f39208 100644 --- a/main/SS/UserModel/Workbook.cs +++ b/main/SS/UserModel/Workbook.cs @@ -241,7 +241,7 @@ public interface IWorkbook : ICloseable /// Write out this workbook to an OutPutstream. /// /// the stream you wish to write to - void Write(Stream stream); + void Write(Stream stream, bool leaveOpen=false); /// /// the total number of defined names in this workbook diff --git a/ooxml/XSSF/Streaming/SXSSFWorkbook.cs b/ooxml/XSSF/Streaming/SXSSFWorkbook.cs index 676ddc31f..4d9d7befc 100644 --- a/ooxml/XSSF/Streaming/SXSSFWorkbook.cs +++ b/ooxml/XSSF/Streaming/SXSSFWorkbook.cs @@ -752,7 +752,10 @@ public void Close() // it's a newly created one XssfWorkbook.Close(); } - + public void Write(Stream stream, bool leaveOpen = false) + { + this.Write(stream); + } public void Write(Stream stream) { FlushSheets(); @@ -968,6 +971,9 @@ public bool IsDate1904() { return XssfWorkbook.IsDate1904(); } + + + //TODO: missing method isDate1904, isHidden, setHidden private class SheetEnumerator : IEnumerator where T : class, ISheet diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index dcafea1c8..7ddcf937a 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -1775,7 +1775,7 @@ protected internal override void Commit() /// /// Write the document to the specified stream, and optionally leave the stream open without closing it. /// - public void Write(Stream stream, bool leaveOpen) + public void Write(Stream stream, bool leaveOpen = false) { bool? originalValue = null; if (Package is ZipPackage) From b875ad9033939bb29489e0d82f438e08277ae82b Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 07:11:21 +0800 Subject: [PATCH 037/159] add new param cloneExistingStyles to SetCellStyleProperties method --- main/SS/Util/CellUtil.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/main/SS/Util/CellUtil.cs b/main/SS/Util/CellUtil.cs index 86395f7a5..ba8bd19ec 100644 --- a/main/SS/Util/CellUtil.cs +++ b/main/SS/Util/CellUtil.cs @@ -366,7 +366,7 @@ public static void SetFont(ICell cell, IFont font) * @param properties The properties to be added to a cell style, as {propertyName: propertyValue}. * @since POI 3.14 beta 2 */ - public static void SetCellStyleProperties(ICell cell, Dictionary properties) + public static void SetCellStyleProperties(ICell cell, Dictionary properties, bool cloneExistingStyles=false) { IWorkbook workbook = cell.Sheet.Workbook; ICellStyle originalStyle = cell.CellStyle; @@ -395,7 +395,10 @@ public static void SetCellStyleProperties(ICell cell, Dictionary if (newStyle == null) { newStyle = workbook.CreateCellStyle(); - newStyle.CloneStyleFrom(originalStyle); + if (cloneExistingStyles) + { + newStyle.CloneStyleFrom(originalStyle); + } SetFormatProperties(newStyle, workbook, values); } From bdd39712ab7b01c501c9c506a152e04887a00fb2 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 07:36:44 +0800 Subject: [PATCH 038/159] Add RecyclableMemory to NPOI.Util --- main/HSSF/Record/Crypto/Biff8EncryptionKey.cs | 2 +- .../HSSF/Record/DrawingRecordForBiffViewer.cs | 2 +- .../HSSF/Record/EmbeddedObjectRefSubRecord.cs | 2 +- main/HSSF/Record/EscherAggregate.cs | 124 +++++++++--------- main/HSSF/Record/ObjRecord.cs | 2 +- main/HSSF/Record/Record.cs | 4 +- main/HSSF/Record/RecordInputStream.cs | 2 +- main/HSSF/Record/SubRecord/SubRecord.cs | 2 +- main/HSSF/UserModel/HSSFPicture.cs | 2 +- main/HSSF/UserModel/HSSFShape.cs | 2 +- main/HSSF/UserModel/HSSFWorkbook.cs | 18 ++- main/NPOI.Core.csproj | 1 + main/POIFS/Storage/HeaderBlockWriter.cs | 21 +-- main/POIFS/Storage/SmallDocumentBlock.cs | 6 +- main/Util/RecyclableMemory.cs | 43 ++++++ .../UserModel/TestXSSFSheetRowGrouping.cs | 2 +- 16 files changed, 143 insertions(+), 92 deletions(-) create mode 100644 main/Util/RecyclableMemory.cs diff --git a/main/HSSF/Record/Crypto/Biff8EncryptionKey.cs b/main/HSSF/Record/Crypto/Biff8EncryptionKey.cs index d69ae4f38..13631e065 100644 --- a/main/HSSF/Record/Crypto/Biff8EncryptionKey.cs +++ b/main/HSSF/Record/Crypto/Biff8EncryptionKey.cs @@ -135,7 +135,7 @@ internal RC4 CreateRC4(int keyBlockNo) { using (MD5 md5 = new MD5CryptoServiceProvider()) { - using (MemoryStream baos = new MemoryStream(4)) + using (MemoryStream baos = RecyclableMemory.GetStream(4)) { new LittleEndianOutputStream(baos).WriteInt(keyBlockNo); byte[] baosToArray = baos.ToArray(); diff --git a/main/HSSF/Record/DrawingRecordForBiffViewer.cs b/main/HSSF/Record/DrawingRecordForBiffViewer.cs index 66f9f2f1e..be6a0facb 100644 --- a/main/HSSF/Record/DrawingRecordForBiffViewer.cs +++ b/main/HSSF/Record/DrawingRecordForBiffViewer.cs @@ -48,7 +48,7 @@ public DrawingRecordForBiffViewer(DrawingRecord r) private static RecordInputStream ConvertToInputStream(DrawingRecord r) { byte[] data = r.Serialize(); - using (MemoryStream ms = new MemoryStream(data)) + using (MemoryStream ms = NPOI.Util.RecyclableMemory.GetStream(data)) { RecordInputStream rinp = new RecordInputStream(ms); rinp.NextRecord(); diff --git a/main/HSSF/Record/EmbeddedObjectRefSubRecord.cs b/main/HSSF/Record/EmbeddedObjectRefSubRecord.cs index 3fd6ec0aa..acf790bd9 100644 --- a/main/HSSF/Record/EmbeddedObjectRefSubRecord.cs +++ b/main/HSSF/Record/EmbeddedObjectRefSubRecord.cs @@ -183,7 +183,7 @@ public override short Sid private static Ptg ReadRefPtg(byte[] formulaRawBytes) { - using (MemoryStream ms = new MemoryStream(formulaRawBytes)) + using (MemoryStream ms = RecyclableMemory.GetStream(formulaRawBytes)) { ILittleEndianInput in1 = new LittleEndianInputStream(ms); byte ptgSid = (byte)in1.ReadByte(); diff --git a/main/HSSF/Record/EscherAggregate.cs b/main/HSSF/Record/EscherAggregate.cs index db2d7f3a3..08329cb03 100644 --- a/main/HSSF/Record/EscherAggregate.cs +++ b/main/HSSF/Record/EscherAggregate.cs @@ -435,84 +435,86 @@ public static EscherAggregate CreateAggregate(List records, int locF IEscherRecordFactory recordFactory = new CustomEscherRecordFactory(shapeRecords); // Create one big buffer - MemoryStream stream = new MemoryStream(); - EscherAggregate agg = new EscherAggregate(false); - int loc = locFirstDrawingRecord; - while (loc + 1 < records.Count - && (IsDrawingLayerRecord(GetSid(records, loc)))) + using (MemoryStream stream = RecyclableMemory.GetStream()) { - try + EscherAggregate agg = new EscherAggregate(false); + int loc = locFirstDrawingRecord; + while (loc + 1 < records.Count + && (IsDrawingLayerRecord(GetSid(records, loc)))) { - if (!(GetSid(records, loc) == DrawingRecord.sid || GetSid(records, loc) == ContinueRecord.sid)) + try { - loc++; - continue; - } - if (GetSid(records, loc) == DrawingRecord.sid) - { - byte[] data = ((DrawingRecord)records[loc]).RecordData; - stream.Write(data, 0, data.Length); + if (!(GetSid(records, loc) == DrawingRecord.sid || GetSid(records, loc) == ContinueRecord.sid)) + { + loc++; + continue; + } + if (GetSid(records, loc) == DrawingRecord.sid) + { + byte[] data = ((DrawingRecord)records[loc]).RecordData; + stream.Write(data, 0, data.Length); + } + else + { + byte[] data = ((ContinueRecord)records[loc]).Data; + stream.Write(data, 0, data.Length); + } } - else + catch (IOException e) { - byte[] data = ((ContinueRecord)records[loc]).Data; - stream.Write(data, 0, data.Length); + throw new RuntimeException("Couldn't get data from drawing/continue records", e); } + loc++; } - catch (IOException e) + + // Decode the shapes + // agg.escherRecords = new ArrayList(); + int pos = 0; + byte[] buffer = stream.ToArray(); + while (pos < buffer.Length) { - throw new RuntimeException("Couldn't get data from drawing/continue records", e); + EscherRecord r = recordFactory.CreateRecord(buffer, pos); + int bytesRead = r.FillFields(buffer, pos, recordFactory); + agg.AddEscherRecord(r); + pos += bytesRead; } - loc++; - } - // Decode the shapes - // agg.escherRecords = new ArrayList(); - int pos = 0; - byte[] buffer = stream.ToArray(); - while (pos < buffer.Length) - { - EscherRecord r = recordFactory.CreateRecord(buffer, pos); - int bytesRead = r.FillFields(buffer, pos, recordFactory); - agg.AddEscherRecord(r); - pos += bytesRead; - } + // Associate the object records with the shapes + loc = locFirstDrawingRecord + 1; + int shapeIndex = 0; - // Associate the object records with the shapes - loc = locFirstDrawingRecord + 1; - int shapeIndex = 0; - - while (loc < records.Count && IsDrawingLayerRecord(GetSid(records, loc))) - { - if (!IsObjectRecord(records, loc)) + while (loc < records.Count && IsDrawingLayerRecord(GetSid(records, loc))) { + if (!IsObjectRecord(records, loc)) + { + loc++; + continue; + } + Record objRecord = (Record)records[loc]; + agg.shapeToObj[shapeRecords[shapeIndex++]] = objRecord; loc++; - continue; - } - Record objRecord = (Record)records[loc]; - agg.shapeToObj[shapeRecords[shapeIndex++]] = objRecord; - loc++; - } - // any NoteRecords that follow the drawing block must be aggregated and and saved in the tailRec collection - //put noterecord into tailsRec - while (loc < records.Count) - { - if (GetSid(records, loc) == NoteRecord.sid) - { - NoteRecord r = (NoteRecord)records[(loc)]; - agg.tailRec[r.ShapeId] = r; } - else + // any NoteRecords that follow the drawing block must be aggregated and and saved in the tailRec collection + //put noterecord into tailsRec + while (loc < records.Count) { - break; + if (GetSid(records, loc) == NoteRecord.sid) + { + NoteRecord r = (NoteRecord)records[(loc)]; + agg.tailRec[r.ShapeId] = r; + } + else + { + break; + } + loc++; } - loc++; + int locLastDrawingRecord = loc; + //records.SubList(locFirstDrawingRecord, locLastDrawingRecord).Clear(); + records.RemoveRange(locFirstDrawingRecord, locLastDrawingRecord - locFirstDrawingRecord); + records.Insert(locFirstDrawingRecord, agg); + return agg; } - int locLastDrawingRecord = loc; - //records.SubList(locFirstDrawingRecord, locLastDrawingRecord).Clear(); - records.RemoveRange(locFirstDrawingRecord, locLastDrawingRecord - locFirstDrawingRecord); - records.Insert(locFirstDrawingRecord, agg); - return agg; } /** diff --git a/main/HSSF/Record/ObjRecord.cs b/main/HSSF/Record/ObjRecord.cs index be8fef792..54e9651c7 100644 --- a/main/HSSF/Record/ObjRecord.cs +++ b/main/HSSF/Record/ObjRecord.cs @@ -92,7 +92,7 @@ public ObjRecord(RecordInputStream in1) // throw new RecordFormatException(msg); //} subrecords = new List(); - using (MemoryStream bais = new MemoryStream(subRecordData)) + using (MemoryStream bais = RecyclableMemory.GetStream(subRecordData)) { LittleEndianInputStream subRecStream = new LittleEndianInputStream(bais); CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord)SubRecord.CreateSubRecord(subRecStream, 0); diff --git a/main/HSSF/Record/Record.cs b/main/HSSF/Record/Record.cs index 6779b837e..67012a504 100644 --- a/main/HSSF/Record/Record.cs +++ b/main/HSSF/Record/Record.cs @@ -19,7 +19,7 @@ namespace NPOI.HSSF.Record { - + using NPOI.Util; using System; using System.IO; @@ -110,7 +110,7 @@ public Record CloneViaReserialise() // Do it via a re-serialization // It's a cheat, but it works... byte[] b = Serialize(); - using (MemoryStream ms = new MemoryStream(b)) + using (MemoryStream ms = RecyclableMemory.GetStream(b)) { RecordInputStream rinp = new RecordInputStream(ms); rinp.NextRecord(); diff --git a/main/HSSF/Record/RecordInputStream.cs b/main/HSSF/Record/RecordInputStream.cs index b51d1bf5e..02c36d912 100644 --- a/main/HSSF/Record/RecordInputStream.cs +++ b/main/HSSF/Record/RecordInputStream.cs @@ -543,7 +543,7 @@ public byte[] ReadAllContinuedRemainder() { //Using a ByteArrayOutputStream is just an easy way to Get a //growable array of the data. - using (MemoryStream out1 = new MemoryStream(2 * MAX_RECORD_DATA_SIZE)) + using (MemoryStream out1 = RecyclableMemory.GetStream(2 * MAX_RECORD_DATA_SIZE)) { while (true) diff --git a/main/HSSF/Record/SubRecord/SubRecord.cs b/main/HSSF/Record/SubRecord/SubRecord.cs index 2ccfd9fa8..673c8fa74 100644 --- a/main/HSSF/Record/SubRecord/SubRecord.cs +++ b/main/HSSF/Record/SubRecord/SubRecord.cs @@ -65,7 +65,7 @@ public static SubRecord CreateSubRecord(ILittleEndianInput in1, CommonObjectType public byte[] Serialize() { int size = DataSize + 4; - using (MemoryStream baos = new MemoryStream(size)) + using (MemoryStream baos = RecyclableMemory.GetStream(size)) { Serialize(new LittleEndianOutputStream(baos)); if (baos.Length != size) diff --git a/main/HSSF/UserModel/HSSFPicture.cs b/main/HSSF/UserModel/HSSFPicture.cs index ce53506d3..98123b624 100644 --- a/main/HSSF/UserModel/HSSFPicture.cs +++ b/main/HSSF/UserModel/HSSFPicture.cs @@ -208,7 +208,7 @@ public Size GetImageDimension() byte[] data = bse.BlipRecord.PictureData; //int type = bse.BlipTypeWin32; - using (MemoryStream ms = new MemoryStream(data)) + using (MemoryStream ms = RecyclableMemory.GetStream(data)) { using (Image img = Image.FromStream(ms)) { diff --git a/main/HSSF/UserModel/HSSFShape.cs b/main/HSSF/UserModel/HSSFShape.cs index 7bcf5c43c..9d8bf20a4 100644 --- a/main/HSSF/UserModel/HSSFShape.cs +++ b/main/HSSF/UserModel/HSSFShape.cs @@ -387,7 +387,7 @@ public int RotationDegree { get { - using (MemoryStream bos = new MemoryStream()) + using (MemoryStream bos = RecyclableMemory.GetStream()) { EscherSimpleProperty property = (EscherSimpleProperty)GetOptRecord().Lookup(EscherProperties.TRANSFORM__ROTATION); if (null == property) diff --git a/main/HSSF/UserModel/HSSFWorkbook.cs b/main/HSSF/UserModel/HSSFWorkbook.cs index 75925a154..4e297bd53 100644 --- a/main/HSSF/UserModel/HSSFWorkbook.cs +++ b/main/HSSF/UserModel/HSSFWorkbook.cs @@ -1403,7 +1403,7 @@ private void Write(NPOIFSFileSystem fs) // going to be preserving nodes List excepts = new List(1); - using (MemoryStream newMemoryStream = new MemoryStream(GetBytes())) + using (MemoryStream newMemoryStream = RecyclableMemory.GetStream(GetBytes())) { // Write out the Workbook stream fs.CreateDocument(newMemoryStream, "Workbook"); @@ -2028,9 +2028,11 @@ public int AddOlePackage(POIFSFileSystem poiData, String label, String fileName, } } - MemoryStream bos = new MemoryStream(); - poiData.WriteFileSystem(bos); - return AddOlePackage(bos.ToArray(), label, fileName, command); + using (MemoryStream bos = RecyclableMemory.GetStream()) + { + poiData.WriteFileSystem(bos); + return AddOlePackage(bos.ToArray(), label, fileName, command); + } } public int AddOlePackage(byte[] oleData, String label, String fileName, String command) @@ -2062,9 +2064,11 @@ public int AddOlePackage(byte[] oleData, String label, String fileName, String c oleDir.CreateDocument("\u0001Ole", new MemoryStream(oleBytes)); Ole10Native oleNative = new Ole10Native(label, fileName, command, oleData); - MemoryStream bos = new MemoryStream(); - oleNative.WriteOut(bos); - oleDir.CreateDocument(Ole10Native.OLE10_NATIVE, new MemoryStream(bos.ToArray())); + using (MemoryStream bos = RecyclableMemory.GetStream()) + { + oleNative.WriteOut(bos); + oleDir.CreateDocument(Ole10Native.OLE10_NATIVE, new MemoryStream(bos.ToArray())); + } return storageId; } diff --git a/main/NPOI.Core.csproj b/main/NPOI.Core.csproj index f6533e4f5..5f7f0381e 100644 --- a/main/NPOI.Core.csproj +++ b/main/NPOI.Core.csproj @@ -23,6 +23,7 @@ + diff --git a/main/POIFS/Storage/HeaderBlockWriter.cs b/main/POIFS/Storage/HeaderBlockWriter.cs index bc3d6f5d0..b6146fb77 100644 --- a/main/POIFS/Storage/HeaderBlockWriter.cs +++ b/main/POIFS/Storage/HeaderBlockWriter.cs @@ -172,23 +172,24 @@ public void WriteBlocks(Stream stream) public void WriteBlock(ByteBuffer block) { - MemoryStream ms = new MemoryStream(_header_block.BigBlockSize.GetBigBlockSize()); - - _header_block.WriteData(ms); + using (MemoryStream ms = RecyclableMemory.GetStream(_header_block.BigBlockSize.GetBigBlockSize())) + { + _header_block.WriteData(ms); - block.Write(ms.ToArray()); + block.Write(ms.ToArray()); + } } public void WriteBlock(byte[] block) { - MemoryStream ms = new MemoryStream(_header_block.BigBlockSize.GetBigBlockSize()); - - _header_block.WriteData(ms); + using (MemoryStream ms = RecyclableMemory.GetStream(_header_block.BigBlockSize.GetBigBlockSize())) + { + _header_block.WriteData(ms); - //block = ms.ToArray(); - byte[] temp = ms.ToArray(); - Array.Copy(temp, 0, block, 0, temp.Length); + byte[] temp = ms.ToArray(); + Array.Copy(temp, 0, block, 0, temp.Length); + } } } } diff --git a/main/POIFS/Storage/SmallDocumentBlock.cs b/main/POIFS/Storage/SmallDocumentBlock.cs index 1dc6c8247..71137ced2 100644 --- a/main/POIFS/Storage/SmallDocumentBlock.cs +++ b/main/POIFS/Storage/SmallDocumentBlock.cs @@ -148,7 +148,7 @@ public static int Fill(POIFSBigBlockSize bigBlockSize,IList BlockWritable [] store, int size) { - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = Util.RecyclableMemory.GetStream()) { for (int j = 0; j < store.Length; j++) @@ -156,11 +156,11 @@ public static int Fill(POIFSBigBlockSize bigBlockSize,IList store[j].WriteBlocks(stream); } byte[] data = stream.ToArray(); - SmallDocumentBlock[] rval = new SmallDocumentBlock[ ConvertToBlockCount(size) ]; + SmallDocumentBlock[] rval = new SmallDocumentBlock[ConvertToBlockCount(size)]; for (int index = 0; index < rval.Length; index++) { - rval[index] = new SmallDocumentBlock(bigBlocksSize,data, index); + rval[index] = new SmallDocumentBlock(bigBlocksSize, data, index); } return rval; } diff --git a/main/Util/RecyclableMemory.cs b/main/Util/RecyclableMemory.cs new file mode 100644 index 000000000..6f4e79576 --- /dev/null +++ b/main/Util/RecyclableMemory.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; + +namespace NPOI.Util +{ + public static class RecyclableMemory + { + private static Microsoft.IO.RecyclableMemoryStreamManager _memoryManager; + private static bool _dataInitialized = false; + private static object _dataLock = new object(); + + private static Microsoft.IO.RecyclableMemoryStreamManager MemoryManager + { + get + { + return LazyInitializer.EnsureInitialized(ref _memoryManager, ref _dataInitialized, ref _dataLock); + } + } + + public static void SetRecyclableMemoryStreamManager(Microsoft.IO.RecyclableMemoryStreamManager recyclableMemoryStreamManager) + { + _dataInitialized = recyclableMemoryStreamManager is object; + _memoryManager = recyclableMemoryStreamManager; + } + + internal static MemoryStream GetStream() + { + return MemoryManager.GetStream(); + } + internal static MemoryStream GetStream(byte[] array) + { + return MemoryManager.GetStream(array); + } + + internal static MemoryStream GetStream(int capacity) + { + return MemoryManager.GetStream(null, capacity); + } + } +} \ No newline at end of file diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFSheetRowGrouping.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFSheetRowGrouping.cs index b353be187..d9c77ed0f 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFSheetRowGrouping.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFSheetRowGrouping.cs @@ -333,7 +333,7 @@ private void CheckWorkbookGrouping(IWorkbook wb, bool[] collapsed, bool[] hidden + sheet.FirstRowNum + "-" + sheet.LastRowNum + ")"); for (int i = sheet.FirstRowNum; i < sheet.LastRowNum; i++) { - if (collapsed[i - sheet.FirstRowNum] == null) + if (i - sheet.FirstRowNum<0 || i - sheet.FirstRowNum>=collapsed.Length) { continue; } From 561974fc3b6f8bb0ae3143d7d093c3b2e354537b Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 07:42:35 +0800 Subject: [PATCH 039/159] call RGB instead of GetRgb() --- testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs | 8 ++++---- .../ooxml/XSSF/UserModel/TestXSSFCellStyle.cs | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs index 9e461da5e..3d0506098 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs @@ -964,7 +964,7 @@ public void Test50784() XSSFFont fr = wb.GetFontAt(cr.CellStyle.FontIndex) as XSSFFont; XSSFColor colr = fr.GetXSSFColor(); // No theme, has colours Assert.AreEqual(0, colr.Theme); - Assert.IsNotNull(colr.GetRgb()); + Assert.IsNotNull(colr.RGB); // Column 0 has a font with colours from a theme XSSFCell ct = r.GetCell(0) as XSSFCell; @@ -973,8 +973,8 @@ public void Test50784() // Has a theme, which has the colours on it Assert.AreEqual(9, colt.Theme); XSSFColor themeC = wb.GetTheme().GetThemeColor(colt.Theme); - Assert.IsNotNull(themeC.GetRgb()); - Assert.IsNotNull(colt.GetRgb()); + Assert.IsNotNull(themeC.RGB); + Assert.IsNotNull(colt.RGB); Assert.AreEqual(themeC.ARGBHex, colt.ARGBHex); // The same colour wb.Close(); @@ -1483,7 +1483,7 @@ public void Test51963() XSSFName name = wb.GetName("Intekon.ProdCodes") as XSSFName; Assert.AreEqual("'Abc,1'!$A$1:$A$2", name.RefersToFormula); - AreaReference ref1 = new AreaReference(name.RefersToFormula); + AreaReference ref1 = new AreaReference(name.RefersToFormula, NPOI.SS.SpreadsheetVersion.EXCEL2007); Assert.AreEqual(0, ref1.FirstCell.Row); Assert.AreEqual(0, ref1.FirstCell.Col); Assert.AreEqual(1, ref1.LastCell.Row); diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFCellStyle.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFCellStyle.cs index 81f64074a..ade463252 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFCellStyle.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFCellStyle.cs @@ -415,10 +415,10 @@ public void TestGetSetBottomBorderColor() clr = new XSSFColor(Color.Cyan); cellStyle.SetBottomBorderColor(clr); Assert.AreEqual(clr.GetCTColor().ToString(), cellStyle.BottomBorderXSSFColor.GetCTColor().ToString()); - byte[] rgb = cellStyle.BottomBorderXSSFColor.GetRgb(); + byte[] rgb = cellStyle.BottomBorderXSSFColor.RGB; Assert.AreEqual(Color.Cyan.ToArgb(), Color.FromArgb(rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF).ToArgb()); //another border was Added to the styles table - Assert.AreEqual(num, stylesTable.GetBorders().Count); + Assert.AreEqual(num+1, stylesTable.GetBorders().Count); //passing null unsets the color cellStyle.SetBottomBorderColor(null); @@ -456,10 +456,10 @@ public void TestGetSetTopBorderColor() clr = new XSSFColor(Color.Cyan); cellStyle.SetTopBorderColor(clr); Assert.AreEqual(clr.GetCTColor().ToString(), cellStyle.TopBorderXSSFColor.GetCTColor().ToString()); - byte[] rgb = cellStyle.TopBorderXSSFColor.GetRgb(); + byte[] rgb = cellStyle.TopBorderXSSFColor.RGB; Assert.AreEqual(Color.Cyan.ToArgb(), Color.FromArgb(rgb[0], rgb[1], rgb[2]).ToArgb()); //another border was added to the styles table - Assert.AreEqual(num, stylesTable.GetBorders().Count); + Assert.AreEqual(num+1, stylesTable.GetBorders().Count); //passing null unsets the color cellStyle.SetTopBorderColor(null); @@ -497,10 +497,10 @@ public void TestGetSetLeftBorderColor() clr = new XSSFColor(Color.Cyan); cellStyle.SetLeftBorderColor(clr); Assert.AreEqual(clr.GetCTColor().ToString(), cellStyle.LeftBorderXSSFColor.GetCTColor().ToString()); - byte[] rgb = cellStyle.LeftBorderXSSFColor.GetRgb(); + byte[] rgb = cellStyle.LeftBorderXSSFColor.RGB; Assert.AreEqual(Color.Cyan.ToArgb(), Color.FromArgb(rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF).ToArgb()); //another border was Added to the styles table - Assert.AreEqual(num, stylesTable.GetBorders().Count); + Assert.AreEqual(num+1, stylesTable.GetBorders().Count); //passing null unsets the color cellStyle.SetLeftBorderColor(null); @@ -538,10 +538,10 @@ public void TestGetSetRightBorderColor() clr = new XSSFColor(Color.Cyan); cellStyle.SetRightBorderColor(clr); Assert.AreEqual(clr.GetCTColor().ToString(), cellStyle.RightBorderXSSFColor.GetCTColor().ToString()); - byte[] rgb = cellStyle.RightBorderXSSFColor.GetRgb(); + byte[] rgb = cellStyle.RightBorderXSSFColor.RGB; Assert.AreEqual(Color.Cyan.ToArgb(), Color.FromArgb(rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF).ToArgb()); //another border was Added to the styles table - Assert.AreEqual(num, stylesTable.GetBorders().Count); + Assert.AreEqual(num+1, stylesTable.GetBorders().Count); //passing null unsets the color cellStyle.SetRightBorderColor(null); @@ -580,7 +580,7 @@ public void TestGetSetFillBackgroundColor() clr = new XSSFColor(Color.Cyan); cellStyle.SetFillBackgroundColor(clr); // TODO this testcase assumes that cellStyle creates a new CT_Fill, but the implementation changes the existing style. - do not know whats right 8-( Assert.AreEqual(clr.GetCTColor().ToString(), ((XSSFColor)cellStyle.FillBackgroundColorColor).GetCTColor().ToString()); - byte[] rgb = ((XSSFColor)cellStyle.FillBackgroundColorColor).GetRgb(); + byte[] rgb = ((XSSFColor)cellStyle.FillBackgroundColorColor).RGB; Assert.AreEqual(Color.Cyan.ToArgb(), Color.FromArgb(rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF).ToArgb()); //another border was added to the styles table Assert.AreEqual(num + 1, stylesTable.GetFills().Count); From b207777878dfb63fa1500764bb1bd1c049cf4d23 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 9 Feb 2022 07:55:56 +0800 Subject: [PATCH 040/159] using RecycleableMemory in XSSF --- main/Util/RecyclableMemory.cs | 6 +++--- ooxml/XSSF/UserModel/XSSFChartSheet.cs | 20 +++++++++++--------- ooxml/XSSF/UserModel/XSSFSheet.cs | 2 +- ooxml/XSSF/UserModel/XSSFWorkbook.cs | 2 +- ooxml/XWPF/Usermodel/XWPFRun.cs | 2 +- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/main/Util/RecyclableMemory.cs b/main/Util/RecyclableMemory.cs index 6f4e79576..ce1f842df 100644 --- a/main/Util/RecyclableMemory.cs +++ b/main/Util/RecyclableMemory.cs @@ -26,16 +26,16 @@ public static void SetRecyclableMemoryStreamManager(Microsoft.IO.RecyclableMemor _memoryManager = recyclableMemoryStreamManager; } - internal static MemoryStream GetStream() + public static MemoryStream GetStream() { return MemoryManager.GetStream(); } - internal static MemoryStream GetStream(byte[] array) + public static MemoryStream GetStream(byte[] array) { return MemoryManager.GetStream(array); } - internal static MemoryStream GetStream(int capacity) + public static MemoryStream GetStream(int capacity) { return MemoryManager.GetStream(null, capacity); } diff --git a/ooxml/XSSF/UserModel/XSSFChartSheet.cs b/ooxml/XSSF/UserModel/XSSFChartSheet.cs index 563f3b3f6..2b4daccf1 100644 --- a/ooxml/XSSF/UserModel/XSSFChartSheet.cs +++ b/ooxml/XSSF/UserModel/XSSFChartSheet.cs @@ -103,16 +103,18 @@ internal override void Write(Stream out1, bool leaveOpen=false) private static byte[] blankWorksheet() { - MemoryStream out1 = new MemoryStream(); - try - { - new XSSFSheet().Write(out1); - } - catch (IOException e) - { - throw new RuntimeException(e); + using (MemoryStream out1 = RecyclableMemory.GetStream()) + { + try + { + new XSSFSheet().Write(out1); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + return out1.ToArray(); } - return out1.ToArray(); } } diff --git a/ooxml/XSSF/UserModel/XSSFSheet.cs b/ooxml/XSSF/UserModel/XSSFSheet.cs index d5c82446e..e99e78f6e 100644 --- a/ooxml/XSSF/UserModel/XSSFSheet.cs +++ b/ooxml/XSSF/UserModel/XSSFSheet.cs @@ -4773,7 +4773,7 @@ public ISheet CopySheet(String name, Boolean copyStyle) try { - using (MemoryStream ms = new MemoryStream()) + using (MemoryStream ms = RecyclableMemory.GetStream()) { this.Write(ms, true); ms.Position = 0; diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index 7ddcf937a..c03dd8ba6 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -636,7 +636,7 @@ public ISheet CloneSheet(int sheetNum, String newName) try { - using (MemoryStream ms = new MemoryStream()) + using (MemoryStream ms = RecyclableMemory.GetStream()) { srcSheet.Write(ms, true); ms.Position = 0; diff --git a/ooxml/XWPF/Usermodel/XWPFRun.cs b/ooxml/XWPF/Usermodel/XWPFRun.cs index 9cf6ee837..b5f6917cc 100644 --- a/ooxml/XWPF/Usermodel/XWPFRun.cs +++ b/ooxml/XWPF/Usermodel/XWPFRun.cs @@ -1153,7 +1153,7 @@ XWPFPicture AddPicture(Stream pictureData, int pictureType, String filename, int prstGeom.prst = (ST_ShapeType.rect); prstGeom.AddNewAvLst(); - using (var ms = new MemoryStream()) + using (var ms = RecyclableMemory.GetStream()) { StreamWriter sw = new StreamWriter(ms); pic.Write(sw, "pic:pic"); From 7d7e6742c0496320a7fba83a7b5f3e8fd11abcda Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 12 Feb 2022 20:31:32 +0800 Subject: [PATCH 041/159] Add Write(stream, leaveOpen) --- main/HSSF/UserModel/HSSFWorkbook.cs | 2 +- main/SS/UserModel/Workbook.cs | 2 +- ooxml/XSSF/UserModel/XSSFWorkbook.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main/HSSF/UserModel/HSSFWorkbook.cs b/main/HSSF/UserModel/HSSFWorkbook.cs index 4e297bd53..0b72923bb 100644 --- a/main/HSSF/UserModel/HSSFWorkbook.cs +++ b/main/HSSF/UserModel/HSSFWorkbook.cs @@ -1368,7 +1368,7 @@ public override void Write(Stream stream) /// Writes it out. /// /// the stream you wish to Write the XLS to - public void Write(Stream stream, bool leaveOpen=false) + public void Write(Stream stream, bool leaveOpen) { this.Write(stream); } diff --git a/main/SS/UserModel/Workbook.cs b/main/SS/UserModel/Workbook.cs index 977f39208..13827b1b3 100644 --- a/main/SS/UserModel/Workbook.cs +++ b/main/SS/UserModel/Workbook.cs @@ -241,7 +241,7 @@ public interface IWorkbook : ICloseable /// Write out this workbook to an OutPutstream. /// /// the stream you wish to write to - void Write(Stream stream, bool leaveOpen=false); + void Write(Stream stream, bool leaveOpen); /// /// the total number of defined names in this workbook diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index c03dd8ba6..dec830c3f 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -1775,7 +1775,7 @@ protected internal override void Commit() /// /// Write the document to the specified stream, and optionally leave the stream open without closing it. /// - public void Write(Stream stream, bool leaveOpen = false) + public void Write(Stream stream, bool leaveOpen) { bool? originalValue = null; if (Package is ZipPackage) From 120b98db4dabcb0591585aad07eb0450d58be7fa Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 08:26:06 +0800 Subject: [PATCH 042/159] Add RecyclableMemory.cs --- main/NPOI.csproj | 1 + main/Util/LittleEndianByteArrayOutputStream.cs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/main/NPOI.csproj b/main/NPOI.csproj index 01351d704..4e93a6da9 100644 --- a/main/NPOI.csproj +++ b/main/NPOI.csproj @@ -1308,6 +1308,7 @@ + diff --git a/main/Util/LittleEndianByteArrayOutputStream.cs b/main/Util/LittleEndianByteArrayOutputStream.cs index ef0dfbdf2..eecb1701e 100644 --- a/main/Util/LittleEndianByteArrayOutputStream.cs +++ b/main/Util/LittleEndianByteArrayOutputStream.cs @@ -123,5 +123,9 @@ public ILittleEndianOutput CreateDelayedOutput(int size) _writeIndex += size; return result; } + public override string ToString() + { + return System.BitConverter.ToString(_buf); + } } } \ No newline at end of file From b0e60b12f4e6c87242fcdefaa2c7427d82ad8f44 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 08:37:37 +0800 Subject: [PATCH 043/159] TestAmazonDownloadFile --- testcases/main/HSSF/UserModel/TestBugs.cs | 14 ++++++++++++++ .../ca_apparel_browse_tree_guide._TTH_.xls | Bin 0 -> 622080 bytes 2 files changed, 14 insertions(+) create mode 100644 testcases/test-data/spreadsheet/ca_apparel_browse_tree_guide._TTH_.xls diff --git a/testcases/main/HSSF/UserModel/TestBugs.cs b/testcases/main/HSSF/UserModel/TestBugs.cs index cae0f1265..1c6ed5e6c 100644 --- a/testcases/main/HSSF/UserModel/TestBugs.cs +++ b/testcases/main/HSSF/UserModel/TestBugs.cs @@ -3499,5 +3499,19 @@ public void Test52447() Assert.IsNotNull(wb); } } + + [Test] + public void TestAmazonDownloadFile() + { + IWorkbook wb = null; + try + { + wb = HSSFTestDataSamples.OpenSampleWorkbook("ca_apparel_browse_tree_guide._TTH_.xls"); + } + catch + { + Assert.IsNotNull(wb); + } + } } } \ No newline at end of file diff --git a/testcases/test-data/spreadsheet/ca_apparel_browse_tree_guide._TTH_.xls b/testcases/test-data/spreadsheet/ca_apparel_browse_tree_guide._TTH_.xls new file mode 100644 index 0000000000000000000000000000000000000000..fc5d5630a77f4191c41b6978c930a6a788a123ea GIT binary patch literal 622080 zcmeEv37lL-@&7xyZx9erhWpND&+KxCTm(WAl7(=?WOuSz*o&Fnghdb(P?SRiMFAD? z;!r^ZK~7OYQ4zfF1MmC3&-|-icWu8n^X65*Yi9EM{0T`mv)}EmuCA`GuBxv7;)~nA zbms%R{m`iT?`Xp|{@dxo*M_h-BDGUGY;*M`e(Fg<|b8gt9T#gP{I#uG>?{8vL&%{O*7 z?f{$`$0kO9aqakMkp{Arfg{90`r29x|K4y}OOL#OHe(MsM(QWQz&9v3k7XbNKP9 z@ow+oJ8JN#cTJ=AbAhjIN8oEe7x-HK3w-V8NT-2Ay4w2!{t)vr^Z9e~b7yBy59|p4 z43m=?zWK&`ljRuOU2bo{zqh}96YuXGE_b}Ycf5R4?{DLB!~1)u%iRq(8b1Q|;(xGF z8lIUU|3I6=`vktY90Br=%i-_K@gMNT(@#GQe_d|ea?1wemd-WM#`DJWfL{Z@Ho)h` z8u<6~*dTO?zwk@1J+B3?4eSMe$oskH;paDOxW%{y{=MNA_*rOo!}GTooohB2ozFiH zp930y-vYnaK=W&EF>b#3W&;35=PmF-C;Sh;Z}EP^XPxk~@WEA}W&iX?2Rh%%SUgsl zEKdzg4v&vjt}+fVuKyc;zNay7$+G_A7cX5@*uOA$-n<1XS1wyoIIyF8R{N}u*&Xc% zU1jWO?0Ea80Nv79QCc%RRvImhO+pJ>7`vUbIn0k`%s=j0!-20%<37{Dubf?=UmJeJ z^7e7t`x^cUg8NGR%fS^b|0|YD#UpT@@jn+32jLgnRw3GZJG*<^J3D8$w|iiOGvJO6 z0TwiH;5h;;NM*q7vSl1AOGdk-*#&xeaF-xs69erej*M;r-qgU^;lMotyqTvYII!f# z=Emk4E%%C+w*ZOY$>d0PV& z4+HL$G;il^VjNh~yuE>{lmT~0LE6Eyn>nx)Bv`wA%bil5ck*m9u4O58F9YM8r&&tf z&c@C>xLvYt7qBly%ZJ0?M!}~FCX3~v(qwUJaJV!!P`aQry?(qrSn2BNn%&*w$t>yx zp-Ms&$>dtlT?n4j?ae>xt`Y14w9bL}*EYVcP%m}$baZw0N?F;}*bN}{xih5+>F(>ABM1BnPb=1MS%9Ui>}eDtTb|v~*U{Z2Wo0k4nj&cKX@?=sk+QP4 zu}>t}8(^=T{(X)8cyO0oIs3zQBghD4bMKtKuFejLJ%rodx%`OS$O4ojPJdkjH(qC6Bv|ZWi3zEy?IH=4gQBdh9j& zSa2WhT!$O5iJ_Q>@&-&nwd zX|XOe7O~*I*>Zr#8;eoXLZGq$W7aIGYFeNo{+MaV`%Q zTaRI!XB0KSa)7Ii0Uq2dX&y968emED8e@nD_ek(sW0(iietEuefd*I(aKsqp!F^Jk z$Bc0v4BNbSR+um@idaBrVngReF&x|d) z;6B;%)y5k%z;b|ZG_KJA%SpM`xQ+#bIEw*ZZ@h^G_x4HIxWRZc5AK$HzR|de1^3OC zdgCp|TX}H1l#QE>xAEZFQg6K70KWxeLG;a*E9F+>HXhtA2lx)-of=>{-S0APXTiOF zl8ifyJ6Uiac>>;TyhnoT@0sMhc(3t3zF|80zTdcu2hWysa<_305AKlQdyNn9;C9K3 z4;mlR0881w&$wTLLz#hxsxGqAK5RU|HrxvbB~KB2#CR|Y+|l0I+t<@8W$q#4VGXck z#v{g~QQ&rQ0+Z|HG2?L!uq5LN5RBsV^8JRJ%4juPZF_=NFE z2@ahT?*!H<*VLzsXLzh6;#uR4SyhN&eT2uWNwi{C> zrUqDY<6FkJc`&(Bzhiut2Y1RD{GRcB9!wk64~&<1Fr5v42)=t}x_kP%<|Q z=KfEOpYh-hIVnFkexU)Dlk!XBR~lf+x?dZ=k>H5cDzU#ce#c`a#lJWHz!O2n{vVA$ z@!(FW2mWketd?2ZWGepE_!|$VW5nN$fAHW=sbc^d-~|;vx~W_23XGE z%gx<*Fr5r`H}}v0OL=~U`AQy4$KpNB0uLq!?q25JJh)3v_de#n8el0%`?YWyX15qWFE|e$&Yo2d8h_his)hHEFMg*#i8IYJBf)U| z0lARiv&^$3xb6gYkn%d)Je$W#iqA36;jvOM&Na_vv7~L!Gtc9(a$OhAB9E2RvD#eC zV-h^M&SxJeb&AHp@Ji;tCbB!h=bWC(TJ7+%6?!%AC>wOV+J3*J*&IXskEa^I&pn zUSwXR0hStT+MLz^OL4x~yqE!Fa)exeuQFf7fI%Ts3d1GlB|MmX4wssjYJla&{%Z5p zJa~>=R+pKV@nCXZU2a~k0hWAzjrkf4u;lY=&DZi^vhlAluh0NXg@2`aB@ZU6_A2u# z9!w7J*O{;5!E~~Gz4>|ujDjS&akY6h14coTGx!Gc4Gb7tmdomm<{LG@a=~6>Uc-Qa z404ZOYhKHPDbMCQ^SVgzoQ^r&b4Yz%Z(gqfmaKb|`6dmpR9`okH}K#N+47ssH}hah z%em3KQ3EXbe3N+-5AK%Wx0r9?!IX&dR`abqn6&84=FL2qtn0U#Z{xw_MSHvXb{ zo9~YTw|h#4&Kq}`cWHp7)ZJ~~tpS!&caM3G23RVYd(C?_z>*suFh8IHmV)#_^Me{- zxlMe?{16W&KHq2F$AihQf4_NuB-lG2P!Q+C=7%-FlFtvA5Aa}$%Y4NAhz3|r%7f;E z8eplPA2J`}!Qfc&!f+ooKdJ$iQuna=Fb~EfKi+6~#C(JYQ)K32=Erz2`EDOIALYTM z3LY~b^$IXv3MD+D` zQaa|-=F@z`a;`sNeu8NjvOLKj^-1%SJeb17pE5tC0hW^gjQI=?rl9$==CeGQyu6<_ zKdk|lTh(XG&uD<9qI=GKP6I3#;AhRxYJjC=e9ruw23V@2&zqmu0L$rq-h5sIEQR?C z<`-D-Y;f~>()@z?0uSzytb5UXQ3EUo_(k)J8emEDm&`9|faRoo+5EBwShDUb=2v*| z9JynD)%>akSPK8w%&%#HCF{O!eq94Bx$zD28yaBA=Wm+dehzHZ1sUMp^=D`#i{fYS#9!zQ6KQ(_U!Sy!5t`0gl{>=Ot-!KL5 ze{TMq2UCjdFU()?V9M3_rTI%9OxX#)GJnN_yD8V^*XFM^z;eC*#{7*2SkCru&EHCJ zIGcOW1t`VncjoVyhWk3YC{*@)^YBM&B@@1M*+MS?|Yqg>m6 zHvg;vmTdlu`IksAo(wY|^c2gXy%hueC1^rf}VU)_y#gay$38_GiFoipT*TU>(4K!4#pu$${2^ zJeWM`2U!R4U`o9>*gBX2Bh6AjA7UNCfPrS(Ef2L0Wx&|7T(E~(hcRF@%%q(&%bLZ5 zDLUI`weetzg3q>QM}jd_0K1^rt=g@24X_mc4y!{0Ecx7Nb!vbmH@d7Y9!ycgZmXLI z({|ir_3&W2pFhW%qXCv1P_Nai0hX(~&+6mBumz*3%%w~lAQbI9|)*jlUsmMx!PouC1h ztUJ*;@oz;e1zwNB;1birqpwMqjlSNCbw zX&PWDqNiJ@^I%epXIN+OU`nz)(>jv}Q$2>Wth0D9`I^tR&gQ|C?skrK4iBcBt#hq& zc`yYp&$G_c0L$qvT15s7o_F#HueMf8a6KPLM>l2K3|Ip^R+=V*)*z3Sl3%h)JXT8F z8fy)Yl>#?p4M}W-Pk}Dut+m!NU^GJ|6~orB23V4DzI8qW?w!*?-qH)K3wSVH_8757 zG{91ik6NP|U^yjY)))^a*Xp=6t^t;-S))Wt>u=YA@9SrAJ(9U8(_=Quk`>)jYUQ&fsO%WjvTpcb8k2Yk;Lu{u=8wJeXXq zueDywgDH@Eg>{7l*VE%&9b{%-XS;1c9Yjxuj9dVZRqvZ>v=Fm_O7PCEN&wZ7eYJJWFQoNjVN-eTRN0hVmO)w-1d_qF%Y z$>%ofHXckTpLbaA&;U!(c&GJF4X~V(cUkY!084#zyLCGUh9HA%`3~z29!w|9JFPo8 zaC;veIo@r(n+MaquJ>5);lZTo-fO*A11$B?`>gjd;NCgC6lQq8^?n8nr!TTh?y~M; zz}T|1E$_DO=D`%Ny2rYQ0mG`IyYBZ|_cCB0gVO3hV10lA!vH%Z>pp0GkOz|m_#x{< z3>e5DwQ-+yp9Wa2l>4pwHNbLpf7tpk52nb$1J(l!7*qjmKOeC^!hwNxl8gtf2U&18 zITRkU9%8`2I?62msP$0|u-soCwjO4{C`gj#N32IUFepaZ^2e-?abQ>}QX@TTJ*okg zE9EikF%7V^S|7I_XTZQZiv2ucJ;8%1o#{#INe!?R(Wk7Zcrabn`ndIR4X|waY3u1o za8Emww5AC9C#+9Ig3aFbSmu|kFY#bHW`5cFG6P0k zCui_0)>jxXkU_D`uUcPaz&Jq3y02MZjF<_h&xl(>;{ZIoex3(WyKhgk8*8SM}F#|@L<(~2r>n98tt_rY$&l*n=hn|Pz;XtEVf}&u1I^^;{H66v1`K3S9@4L@U$Nj0x`q5} z>(@M(e1^ZVexm`Fg7jPKw-Q|Ma18D#IVZofe#bXVxnaMze$O_Hyu&+gf3W_*VmNK;F6n<-|KzY8v|s|Y)1nu*Qjf|3>aya3vUy969x=c4sl~sds7}vhTLZMW(*j~kZXE# zdvgX1+@Q$)7WNh_xRabPTiRP{faQ|f%HE0x(*ba6dutZlMIM`N>}_~3ZG79>+iHO2 zbZ=*Grva7&+}_@v2h+y4gS`U_?x57(9qk=?FonT)vUg&@C>xUIm)S34z#tnmDLdOc z^I%d1yV$#EfTbGW)!vl{lZp6p`{fK62PoCoZuV{x9J;gj^~~w&ra9T&-koU}nIYB2 z9`+tQm{RdyVZVY0(}kT^+OK56os=54r@bc&p1Dy{unQVs$+^Aky)?j5$LwwI&4N40 zs@=!lhX<3Dv9G-^3+|v@Vn2I728$9y}t%nO6URh0UBU=SUk`^kOxy{??LuK z8eplA4z>^0080@)#6Cm=EH|q|?L#%dQZ^2=57PikYh#u@ivi=Zk}bE{Z5m)XgR||~ z8el1+?RI-4*h`8ghkA$I5efDz2&%2qX?OBq%0}+8yBIKPFFC+&yITV+MYPB6VZdN+ z&?#+>J%o_a)5{1hiic43?5-0p#hfMIMO~+11uNpQT9?ImtIVUICCoy24nH+yh?WG!Ex&JJ)muZ0IdR=ZW zXThBmraIX^nFWIrMhMahdj$_BqhO`Ik_VFqx!>;R!DKz3VxOV`maIF~K2-xOSIR1T z6%VF>?rHXE8epkoPPb3j088ON!#+a;ET!&D`%De6l#R3OvoyfcHaXiqTLUcle2#q% z3+|w^@wxW7JeU;!dG>i4U^&2|U1Y$Zo~h;4_UcHmXPZ!3?SMU?0hSx=pgpJomdmPS zmv}IFW7pVgG{AC88M23XFr8`E+G}|*88XB6Fb^ij*ZKDOJeWN77uXl@V6x&z>=7PJ zCeo-q%7ZEIXUrbs!IU>VZjbX|@)S(i6Fiu9*bD6oc`&71m+i6!Sg!7hUC{tbd7iW< zc`#}IDSJu-EXi1Buj9d#owMFv&x0vx{v!J#9!y)?v^}i>mNZ{%U(AEa(fKO-RT^Na zMK7^0(E!VB;!^uk9!z;ZueM*!gXsWtnSB`#CNIn7_T@a7H2-Vt*YIG{&#$##s{xif z>=pJE8eq9iTxnma0hYplm3SS_3RK{~PQ#@L+Pz zzR`Xo52lQwYwT+@z>?Q3~3Wg=c@U#9_)> zo9#D8gL}IBx+sC{M*GG{u;<>O)Q6kwn|Lr?Tz-rF79LD#1aGz9%7f|l#m)B38eqxi zx7lyg086vy?e^OlFq&#|rQBlQ!hqp$Oitok?OS;;-JiM5zKsXdM*j}`9W1zm_LO(p z?~Dd_b#}IsAic|eS2P&Ia+F_nyL~$YhLa`*f$y;I&;ZK~=uZ1i28?9L)%|Y!-4a~S z2?5sxrD(jzeh-h8lJQ>qy*yUZ@IL!}48gz(QqJ$U-_L;Y>@RouyX?CdFt#j*cei~v z1ICu+?sJcQj|7ME&5MqZZob#PmuVQiLXwIP*dJiPID1lqe$f6P1BUStGd^U0C=x8{ zwaGcT&%Q4b?CCj55W3&KUjr;T_hI|PEVz?Q_y_C zm*L~~;|v&SmVAD~enNsnSCw#y$_aeZev)Yz#z&bZPuWkg;F)xnkJ}&T!Q{z$+J0IC zELHO->`!Qb<>LON{Yee5ob6B9pV9zJAK5eZGZGxiW}&>~WIt;^%QoCW>6@RnKh1za zg_2MEGxleAFkQ=e&VG&u)5E8qwLhx?mYn;X{W%S=oWRf9pVt6Oee}HjyarhE`3v?J zG{AD5ykNf|!J!lAJ-SU{h8OJ@*@k;4|L2SL7a1@{!sH(DCHqS}m>fu7w!h4S$x-(e z`zt({97tcazsiHjf%G-|Ydn~a5ns2zt^t0=_y5uUBMa`P zTfl#^|0KZ?TZ&X0f42Y3H%tc7U+llI;4Zr0@>lz>JQY$A{LTIwkCit0-|fG%Sju4e zhy4#8D>c_Y?SJxE$+dsk|KhQ7rT*LgH;cV`7E9~&zxIDwESr*_3G*Hg>w-vzfCQ1BN;TlxefMvpGXWM>hqAws5xKv2t;2>1@ek zrD465vlWk(rt;R#))HHP@@7)Ww{fX2I}0 zoiO-zadu(AKnB@xyE?n_V9I@Zx$|-!Ou^ROoZWaZ`HgpXc4xsehV>rK9+6=0{xe;# zd4=|264X|9W`#Sq-faL)9bN15!OC__vvp)kyOG_@;1DpdQ!8qM>=(6;I&Vi9&Zw*p% z*Fnxf3>ep-WZl8e!3-E^rbyNy&LKRQj-ZD+hw@+&(Zig>Sn$jfx3ipC3>bu;^hTT0 z#)5n35zN`nYzEv5H6)}UwL9$$7(G`~_&b~q77V93A^e?Arv_L`U6<3vfMLPXb&hVQ zTLUaD;2x((11wF-InEpw+(V(dUZ9;}KNSe1vm^23T_A zNasi%Od+SEoTGR!t-+(6qj@k{UdK4c@L)QE9_t*-gUN)L>&(>vOQ}1~IgSTYs9>Hm zj|Y^NzRRb(1WtFpv2h**%)11>- za5otzr#q)>fTbXv;he#M(KwOdGo3Rvz;b|RIcG6om~OiMf3|Zr14jEH zz>?2voHdbP?*v4T7Y#W>kzh25A%{+^!L`m>9!%B2hMi#++)Wna`Of(~m}1ozI2W+s zF7mF9I3o-gSBlgdqs}M;MsCO%9COAPFtkjeg>h$`0fR|QS%MSJ1Oo<>nC|af=v>Hv zVOdejWv3ho_GE)xmKCQW!Sx-}ln6HIOhz{B9jypFxjw087^0;@lDm7V<0yc&l@3BpBrx0>#4Wxy`vv11!1m4(A;V z7>xqi@;jY(GGI_LWc9quc^3jVea?M6n9c|HJNNTo zIu3r=`LG69o@pL%9^k=rcKnF*5gts3wg;UDc`&W+hn$CaFl}ugbw0|1d&nj8u=6kr z2B8x(_=xie52ia+A9Fs&gULJlsPiZfrtSDK=P?#M6F_*}d7K5$#6O>Kp5Vb$_vA_E zNghn+u&11-crfKwecbst52i!S)6Ua8m~8$}IG>0HLu&RMI^liN`D8R0H9sAWKjnOi z2h-N}jPnc+rre`voo6+`a<}@l^JyMT9*fU7pW(q2TzbxVP6I5Blg~Pz)d0(V@N>@R zcrYErKJR><2h%y_dFOczu-w|d;Cw*?EO)CHoELa76&QWdc~Ju_x$#Bki#(W;A;08& zNdqh=<;%{OHNesq{fhGy9!##>uR34V080`5n)5XcurzzV?tGmGlRxqs&Nno`a=O3i ze3J)LNb_6Hw|FpJAo#ZPZ4Iy#r0+Q2(E!Uy`L6R_7ChrC_@47U7Ce(=@qOp}8elnt zKX85!4aS?Pq-0)lUWx>Jr(-%z{LuL!4K7aU@tA(xv_U6Xz!y zU^#<7b$+S=mTT~5&d(Syo`9q(___0Q28_;2N%Jq9U$EerOIW{jei;cy*3BVz)~}pj zMS_uaGX-OR?fjYrchR!?jq{sGus4I`NBXVv+ek3_H#*2-{GIbV7TihKI)3l`o(0cb zz5Rpp2Odlz`ae2<)BwvF{FC!19!!D9KRbWc0L!DtU!1@2U<&^K)%mLiShoB(=WiNd zxl;b_{GA7r)cwQx2M;Fa-anmx@?Z-7|I7K823T%D|91Wz4eo>z?3Bj%ALl>OV9c1K z)5L$B|FYl?GB5w<{7(Zc^~MHgg9cb0Y9Qm;jm{#*WhJ>`x+V`MubSmrEVz@T4$3gI zU>Ke_h(Rth52jPb9ZCGGZm0`cX#K(q@VY2_u#=~Z@j{N z1q<$^+G($JU#S6>`{17Lo-BB##zw&{u;5NAOtqK0mj+m#CiZssX2Big2H(fshX+&U z%f9ZuJeXpd`?>q^U`kln-`$@Dcas140QUeE+(E9P1Kk68Fp20v?m;}5vPceg4`#tL zZ*Uyq9>Rh<$a{IHdngN@xfXqxdl(O8F(lxW)+0kH7=5V4A zn!nv`j|QVbMOHzF+rffoDlc@pojjO4u3c^y52hC+yWMUU3@`tQ0rt2(EVzTh!E@X> zJeWMLy>2fLra(rY+sA@uF25b_9?pX)&HM=W2o~Hy8Tv=MNAh5bq#xxT#e*qN=4kh5 z9!!3uW87m{a3{r6j&+Y^!5!qwo$Jo!!E_DiIQKXf+)24h^W1qXxPxvL%y;MWV5-Nt zz+J$Cq1cp==Y{S<7CciBdXc+`2UEc4c=vc7OtFc@?qUtFR5B;HCuo4B1Ls8dL>Amh zUWg^`5*9r3{=`Y{Nj#WLwWaP-7TigpsAcXl9!xpu%iZNH7z}7J-6y*z^I*F7y~16= zgUN}y(p|}dJLw8SzuV7(+sUzTihBwR?w~x)Q{7Wpa3{IoR=KNKFyy%k)}7{_#)Iiy zp3~jac`&JhGu$(HFvX6~bkF3$BuHnuXYpW)(Vy*}tpS#&wsYKbG{92Hoa>&;gXti4 zo_ih-rodd$Eoy)z8LQpZ8eloV0e3(HEM;TR9pu3jT`IXH7TihqLe{uzSTG#Bg@_Kh zLp+%7E39?bYJjELGwcrYV6sKech6_RGnLsca4*mR%Sjn=M_6zNd5K5eQ65YYu`zdy z1w-zh;PbdU&VoCM&lBzh14eJWZ23a>LKfUb9;&iiX2IR`)=tH(u;3nAgOlzg1IDO_ zTvk)=6a$6<(muG(U8ez-%WA#5o&f_HJs-79xE-LOWjK)wtl68E;`S=+I=+xhHN?VMPBA!rU902+{@j|HNbN6 zUgN%o0b^F8l%dzUuVuh^dsmWig?ohtSdww2dnE&gm>jviu5z!^0827n=e|w@EC={{ z_w^cJsphVBuhsxdmGK7m4Lq2_5^r?hr~#I&yT-kS1$R+ZuWQ|FS@2A?*z4TuBEce@ zDHqE1?)8yiFPuqg?oIBSSa3Hv0dH_`hy)8VPQ?oE+k zL57@^x43W7086QRtNYeSuvdqKvbb(`Z)U)-QfLjn&3zjS?jb+O+ugUb;BHE9y~VvH z66}?irSQ+K?yZqvAscd8-R9n=0hVh29qv0cz;X?~(|xA|*V}u#I_V9^ce(H4u~Nm{ z?%vK40iix&mE7Up!GKZkOUby?y^{gMrcG4_-|fCz11#6cd))VEfF+mT>%Nx(qoENlR<@wxgYE}4z*6WwORUe45Cl#$%1<*;P@%` zQ;}d#EfUSoxX(m_y&Gy|06yzJ%Y(^d|7rKrJeac1KI49d2UDc)IrljYu+)p6bw8^C zmU{7X?&mbXa{Kwb`*{tpwCSFApXb5ksrZ8X1r4y&xG%UbM1sBPCfoT%_eBr=UtgFUFHBWRg~_$0!g1yC_3&kXxl}4Fni?J~U1b>kYfJFaRHZmn zDvXU!mMVpEXTH97lWszOlYo z9>i{nW7FefUMq#w<5OdUh1JuAxueC4;fDr_2UH3J<6~=})3M3n;>hsibfGu|gp9(! zXF&>80scGGFpet@TrgDjetNO$w6K3+QE9AH2D0Xt)(npgBPA6Rerh`tM^HPmO5woc z`WICW!cHfP!(-m~%TpsTzR9)4Nktbj2R;MpE0y7)F&y^b)WD?o(Q3i3f#PIoXdHM^ zDU6Se43Cv&6$*>T3d56y>G7$;`r(n00?;&A7%fgr437;}3ad+#>r17vLha8N0Vewf zhKvkXCJWRJg`=@TpSsJNraBdL>dxXnJTX-4wPVGU^wG2 z8;ZtR=XsM_%aX!yrBH+)9v`9MLhm>NXk@fh9#{*+4Od_Qf!->G(wa4;fyv=@rIBeG zf4^bO6HJ-LwvcEw`eM0MSU+CApn?qpJ7Jb9&@+r2zf+l7eLggYKQ=x#G>+YYz(Kzt zB~Jk5<1<6KxI`oNTB>2@p^}mC^CLJnQxoH3g~8%v5&H#x6i3Ff?Sb*~Aaq}Wo+l;% z=!vBq?__0G0maArDG&tjdvcWkx7K)a=<&D&|G=3*dZ)&K8RZHHBlWUoxHK|&`0j>r z*b3-x6l8j^G$`6xIzCuxN8#S3-gi6BfMEj>UL!b36XO$8Bf#E4kWK8b=ob)@86ha1 zEDx3{1Lfh>C=G+d>xKuX#On3Lc-D=kF|!7xtgnSWyb;4343taIQvsLU1c*1tq7wE2 zkUeCHSOt@?SOvQR;wC0_vJ7ixstjo07Vv!-ezq`CoLmcR2l>Di2kb3_4k(Tl#)|8P zhp^W*RZC|Ie3JZ#`8Uo+OriDC9;25xZR$(r+h8?c;=6tnpK;8ma zT`rD6bNB;?3aRwI7DNJK5~c!~J3NL%@Q9N%sZK!yuJL|q7A-Su4x|hOTxd9$Oi!4g zwc#?c5J82-^DDDJ78Z|HJf@){LnT@`1=w0B>}nWGi9NWofS9q;MZllp>cb6V$vBS2 z`&15V+T*eyesu{JGzmi`u!?7$Z5YeOP+@rb7pXh6P#Oa@B3c`pf)z15P*_(SnL<%7 zi=L5Q6vz?KHQv;Qi}zt2vkD+)lar|1$Hxw@+9IT8mBRsbFb$23Pfdc#0M7d62c#WH zgfUMSKv>q{`X3%GmWM&{ps0?(muuilKyj2YnhY}5V z_@cTmK%;JuYZVn6ZgXDarHcki6F3lWg~QK!^6OW@2g1fe`yXrrAi&=4S17J44v!R9 zkIZN;-{@v-Wf%*Av89WOqZ1>g!}l_b(?R);m4F#ybg%)fnLfO*5;l>-{PHv`moo)s zL`^%2(BXx4NSy2GY;P|tDVAa1o{NeNq`FjTTUD$8H--u)!IFWUsscM_|I{c_BwB3q zwO&Li^hPWDE*?JQ{4vGvogH+%QgA%O3bipPfdP5NnZMbk4v;>oj|D$lh^kExb zt1#WALUEux?(Ii~;^3fXNWe6EE7O~1PcBAE>x!V1uoL*|OX7?`R|0_#A1gP@KT%djDMT}ZK!V@2sfjzNE=F)$`jWmMLV zdo~eB{RAkO5_~x8e$!hAe#Tmkp-P10xoq{mKB*z=*%&PUnm8QZe^RQy$pY~eumWvSK(%>u$&GQos&r0z(f_-}Zf!zp7L#SqD zCk=q8kK*oz+x$prjkn8t96J0+Xz?&ljlh0bI1}{=ej&8=%)C@fHW768BK6}-i?F88 zobQ=HptM1EjNvD`Egj*tv|@(s1<~Frxp-(`{x%f{&R*>OG;Dlee!;3PoVgGUL6l*! zd|+0mCPv1Kpy6RQ$mXIg8aX|qTh6{lv^9Wx53HP1aVLgT4Ct)Y<8Yq3s04e?nf>1J z09VVPw`NSQwS~LTT2}tO6*FG8`+<9Vku|2k^j$7TKcX&=x1)+5ZKp8V94rzdSuY zp)BLxcqo}HLb_<_DpEb&F)%Py2KfcG8p>Q@52$$(k||^teriOl9PiL2xF$L5O_0zr z{lGYwlVLeCg?Zk*95;RuEHV`H(8h37!YVA%yo%)p=Bd}wtj)pRL*ZWz2JN=ESCW=5 zECIE$sr>HZ`8yltJY&QdHztj>#;`GF48i$+fA~Gu7=T|TqXNIijWT=>zur9_yv;by zC>pDcX=A^nKd720Kx5<3RK;k|vaz;0L+xoqCpfxcU{D2!h&2(4E$S=9pn79c-L)C> zVN%E8*9c5D5izgPL@WfUDj9<_l1GHhZ!{tO9wGikK%^YiC{m_iLMC7`)OxJI#56=I z7I`XQowvlr@;?ctWDZl zPI)?a1iD5|i#j%AO@K;s&{LaJ-kcJ9mqEuR<1k}6d|L8UeZg315az^e$DfP{R9Dr+ zFK!LO7J9mK(&JslXc3}T08yi`W=bH&6`0ygbq7ubY6R?}Vsz%&c$;wojG^eMo{YEQ z;3(RRCD73j{1T>5#mKq4WuAH|!`MdPSMF?F?lBQnYFEDVwGymHv=Xtaj9EAjnnv@p z0*&KtL`Lc=PejT_#+sVzuFY5kBVF&w=F!&TYaCD8b^H!Y~D(3}$8v}N0k&;q#0(~`_s25LKK zgHy>kj1K4eP#Bq?jYk?=7fiA$PlX|)rmh(0&v@b(HuWoDV$p6Gm=WS7twy3+I2rFK zVcc5@yuigEmI85yw8;ti^Cp^rHiJkifGpuT02NBchphN~QDiAOI=T70QoK~W5)+$~ z3rld0tWeRFa$1pnPMcpYx=Oe6HlO?mLi<7R(aT2UEz4s9^~v)eoWcp5lP!TO_X!Gx0|_BwK9WNhoJ=1JvM^Pmm3*kN!BtOat0 zJXMKq(2UJ29Wro9pAWxB;kWSbs}oSh-un3D&>CtwA!1Qy?x)t_h*~!pyI59&*x_l0 z?3w-;%`hLa{DAjP-DBFyF==dU-j6vW= z#h8`#+>_%(ETx09Z+;loY352|J!qg&L-^rP6%2YiwQ~FlR-zvUgIcJ$GWN}`1e-;W zfae4BIu*bhJLTymB8L1>83!m-*C;w@43K1IZlBQ@smlU*E{0)@WDND@BQChwMAynF zq76QC!>4+TwOtDD4*H$T5Xm|vV6$g#q3V!RPvnG)FXJ^#2F-$O=bDZ$f%d3m&xW7j})Qm!qW6IS-pD1geB@_;sVj>o=eqh7$KOfhn7LT zRN;nANO;y@CjJSX2?^#6W^zu|>vjsZks;4FaUM;hA}GO^q0cj3Z=aEa`%UnNm9r}|MGf6A(&n(}0 zFi@Pi)=_>6JpE=qo5AKWl8iyh>6y4`<_xXw5Z&^a?}N@iyrr`YzQ#KW75D`GHkq6l z{WBWOY!nM7ER8O68&kJqvCXTUrip|ENzTOAo@6S}Yb!jEncH8%5!ROm;W);8{a_~O zimVMneS)HLL4(PQ8KvO+r>~tP18E5oP8ITQt6*m) zSxV896OVIkkZqz8J~PQu%9gxf%4a5daM=<_IfN=%FO!_!Y>8B9pP6qVVM|5j3Syv# zI7HF6OwtZG{zQ|YaxDF+pP6K&W<{8ir_6V__)~_PFXi&8Q=HhW$#b3gF-wrCo8*|2 z?$Yg^PXWy&UAIcvHc-?|CnX3@mVKYet_6r*|ER4Z%NTjTI z>35lL<^GPsvo-zMnVGL+)qTDE%uMRAg|k*=XlA|zsBzWiXlCxC^>-$+G&Ao)W%ZH% z$y~qIc0t0gGBqRT2~z^h+H!)}il`5$zQUA#jM-u?VH+fVS0|yWh)3oDxUkNmQZ_U9 zY=jY@KJzDSVi5)sU%DZN1=B`>cNI{x=sHeRu2EE2JRrV7Cw{PAx@Ll6RM8Ehz?5K^ z7;{RHAHg+_*5QbE!(|n8gQ2ap@ORlrAahB(5=#PO&I3S75sXy~{U*4Ik(_{fTu@e3 z0@F1)ISYUTcmr_?N+7KPUL;7WO5Qn_c$q-yCMSk+9upiYlXsdr(~;|Xfmc0;b}@0L z$NYoZjU$1ZExXRqsK=#<8aBZekS!r=ffT$Rru)GfDt`uroSqKQ+zmTg5E_h|hRdK~Hm|L6R7{QT5i&zLc$`oD` zN^pOI>j=kF@sf$in8GSfW^K)m`h}lyGvc~ImlMujq$OCpmg6)nuvoaJA)PIS@!%bN zlv*5Ff&-5()0A18;69GO6QmXWTC_eBq}@d|*9MEzUnel7992{6kJcq{2}Dy){{NKr zVlB=7b2z!8#c404-5cH&C9r1Bd*!W8;4cXfUH&{Y8rAb^nvN+jnDK%77=jzIP0vAR!Q`H%lv{p*Pwsst~UHF{~evxTH*SRfd zm0pxWFoFtS=r{DU*QR`?I0NZ-QoRGESZ{GU4IlAd$;~KL<9fg3jnrlgrLewg8}sKZ ziir=V{Uyk#BTW#$v$!QZ6QpM~jyN@#bF!R$Zo*WzVmejjhww6}etbA0g}O{40{*Pn z6e~YT*Fn)w;R#IAIpu1q?85}1x-{agwvvoBj7ne(%Sla;78^Cn=+45kPcxorT@utx zCkS1q;ejHsUPftyIYI;}3p9l{Ml3$vSmrLh%F(VY(o|E{qXg!DVc|Z8;kgC0>lH0--zucJSi*o zMw70)da0(dy=D23Av6!>i7k(7lI2B^NHM}h7BP*hKpA2U!AkrG#quI?AxIgf2#Es_ zl7|Vtu_HNol82Af2<3(K1_hzQRd!;lOf=OVBx|$3RS2t1qQxXQ%t|9tTW0lAc3$k- z6M4xV#*2|lBd}jj8>hC)lr901hGwRZjvte-lVIi?DVW$9oq2KbB#^fX5Y|FEk4zE4 zyAy$DsPkH8&7#4NuZm+p0JDlw=i$jilwm`Ip=CC>e$Tq6?QNwu)o61j$SBTB{_qtg z)OQtlL)DCzftw#mn8||^jBc-%Q7o%q;uKkEP6?SqH#ZiY#zS4jIJq^sh`v?4*(Usd zEn~k_^Hdz-aZf>sY++tvB}`O4ElnieZ$=+-1tz~G*EXKP@x(5kO~TdQGP?|(Mermd zHbFGL(0@7MDRgmXAa>G0T76ml^9?sms|mmp@1Pe*Km*|bV~@B}rf$|AnEXfUox5X) zw-q(@n#9l1IaU4mpb(OifED5*U~Ct?PS@&wX>7l-PV+dqVK+=;`^nRECU({8JEpM( zJsk=4IH6)Bu}dqYBWQuQ)u?->vG3fb>%R7od=jWZG`CBhg%&&MHeYhE$T&Wxg;_lb zPE~&5yqth==b}h#4O=k5{z*0X8P;Y585rB=>!&e3dE`lmy@93e7EPu>Fhc3X5j%q{ z9Rb1n!eU}pfj$@a9CKOI`d3q9C-&*^Lce>Fa8U7LL=@G z$1bsyHD@uwruvnh*jLhYh(f=)2>poL)xotF`@*A6m!#Ij!lvuXu@i}OxWoH&deE9he@<$J5jOeMg+Ce| zJ0XW3OFwLy(U*Z!TRF9(i*p?~b}o(1n9+j6*iHqsvWQj0&Sv1dQ5g+lF3a(=Q<8Mz z&jZHQh8aV+v!-@#)Xrn=2>mybC!kvq{EmIgE!nsgIsQeHgLYB5sp3b?hrb zei2cZwNq`0CxFVtSgKN;VxEHUGsaG*^iwM0@k8Em4jt;T%gOi~tG%fjJEttVIpNm9 zz#iE)!CSUV(^&bn)N|eWuRG%{7w&p=>^M(4Zp0-|;m>VtJ}N>nvWrz6(KsA~J&^Xz z*vWk^@6Ja*z(T0STemG47L5xu9>aIO$Z%4$gE z&=c!G_hNdgph3K@_&e~aB5`qvK_61|E%=7=Mm=UM#8F%&P(jjER%7*()2Yr=)eA@X zhVc?h#n>l}A>yBrRQ^cCOENqCF11wY1pF=XRE{XV`SACkQW-8fo#Ld|D?gCxz!|@D z@+&vETe{nmN;&I{Ruv&gwM>FIEt;wR%qnh<56Q-u+vC z)S&Q`p^W;dDh8<>G0`OWea5L&w(@caM|!I8Qf*_Xw1XGoGwz`FN_S4{(u_l$76yER%Ofp8LGG|G!=0hq6b{OTs=D89Ru`8zfb##*M zX~r7fF|D>#N73*RV?Kn~_%X3jWk{s*g@tvcK0~XJl#nnJ>S+ji_-wbh+uKQCHjl-R zm5Lv$cYP)HDKfgbdbeR>XHaq7(9IW|+$k^B2k%@asB5F!Q6+wI58w1S9ADHsF|?Op z{C(6rBdvz9iJQ2wy~cN*Hs)YC41GcsU)ue1uBY|;cfC!_>I}MxcJcuP;dPe3{0TItMO>@#J&ler4Uq; zh6Kg-HE;y^?{13CCU{r!#H{6v>rkZVg>T9JpUvE8^EV#G2@yTQ$MtaA#-oq_Ct}|x zOgl0DYp*D@NR)_t#P-vqCnYE|u}_^G0eIC6O?522R)zx!?%kq_XXZz&@LO%Mqvtv! zQaP`5c@ED4ScntvSKy!G$>)%usF2Tk@z6A#qu#?C!}vCXsPc|}q1e%kV;%bn z1Z&NB1(K3?Is&@@c8V`sVsx5DjwO8X>>Jy0m7J7u*qlV&paML#DY8x*+WmTO#8@u3 z3Ec~K15QOm_gc_hRr%9U#oQ(+ryi5)-e}PJeMFMDMBF8D~V=+_$;KHY@ZBX!((n2R^w)5CQ^5`y9=6USvI=T&`@0ij7C%g>_&nl_> zGPQH3#(^<4|sP70M9r)Wl9=;OJL(H8PedmCl=L z?+RlM$&v8eWGqAdpkSkmQ>3ybYscdkR63avssO8B2(dWZXRKOO*XoW`DpQ8&rrpV3 znt?vcc*j!t4%C6aQ~!?D@Q&#gRK_byWl<*<2mP`Di6h7QT~u%AiPu-0F4b=27m-3z zJ6IJuh+Un7G_<&9hdPY}Ik7{ywkctO3%8iC>Vk?TakM^uS0YADnnDn>94y*0#P)H; z;K3a)FPGv!uTr0t-1zm0Qg`l`Cl!`Fd4?w(biHD|l#+3FYmyk0y@K@6p-WPT1s<%Y zj{7DXSK5-}|8KHCtLR*Bs zu0D+{Yov<7P`x;pMUf=-dGW^1SYr(cT>|F=N6{TWYBYGc=<)AVSld`&QtZLiQ=T|4 zF+VA6GgbkynCGw>2uEcl@*OJ4!_P^GrNED>!4mtvQ5yS29SRdC0wx*tDlpkG6IkpE zcnc7P8&hx-=YAw5qYU$&%gKrA$cQo0JX2SRBdcNKjP5GD+EC(<2@>Gt1c)dW4G2B8 zV=$?B4FCH#Y%l&B}pDoeryMV|8alKc&W63BSrtM0d7NmUxi^k18QiBTJE)%XQHY%)=)(F&VLX#&Q#pV5e%<5~q2` z8D)avbV|tzqm$C8Vwc3J*AVUHgP!WactQoLG9=#9gH)pW7kPG6H>i?kzpQRtt%E(& zP>c?S#I_nn$3zVytmIF(1`@|HRdaZlsCb#;i-3nzttiD_Ms)h^B-(bcij}x2nm7f4 zc3ZqG*$>MabMZv;*dg7nPwbo1Yd&azi|gg&;yTP%Y}-##V&}9jNy>!xU&5^zi8F|F zJI3VZG0#|?hRH~L{MPM`D`S&5NUKYLDqxd%|IqEKx^zuqgI&Kv)XkWpML&@^_)iT) zuT;zn^Ka1pC6hREUz>uGIFGcpDN*PEO$xkQl8HKw?j%9gKiY=F8&(@gVgU|oc#&6U z33DRF#X?fdSlk9r##p~F@v%Fs!(i2%#DdB<%@yQHe8VeA*U?1`6Q}hjA%&KB;`2dR zr|PqcZ%2n1(0PZCFyKq!=qp?aSEv2&)|>(oSu$ep%=%5^vW(P4E@X=aA0!6Z>jH_MAg!069l1pdOkoS<1oK0%Gnr|4wN>ZttwCzFofrFB zLOv3we4VJQTZAaQ?Sg4~IH}V{i>ysiPzkp$aeh!1OYBtne5P`#x4W)|<$!q}>%odi zP_8T=(#XoIKtf|n7*_h`Xp1ObiZeCQl=trg7%&=yU!sJAP`h}}7n>Ia;m>W2$<`xQ zc%y>@h<%A8pXH-FfFQ1kJ}JMbC@&h(k5B_+ROBs!Izx!FNQnZkBb@4w7-89x?biddKm&eIYm& zu~0NR%O>GOfrs--{M#e>l8c9MOs&U1F^M8q%@rJqlBfFIr0VxA$hTmf_*_qAN%*E- z?4c0ma*#LpL4a>XBrYgsZUU_ zMoH|v^q&ZT0wvt3jkiSHFvy zYm_cHD4mH$LhHNYT22f&?^Z+oSvqpd5gc^KHPls<4f#QbT(0rEB+?!3?K-`Y8;}m4 z!^vy|U$}bLp`rGM4$&uqiktQVN8**|23!3)BnEwj4Ly|TkQek8=6dBIYVp+f7dF)9 z_j4@hG0ZjbJBl-YpJ78oXntPc6E}3}8uO@8sKw)B{?tZ9k$W@OWW8v9)h{3uyYOGI zqvR%=GC2%#O@<84MbNU0CRVvKn<$m!&=dmTug%j$uGJ?Bi?+~r zo3`yM5^A}ImvyPKW`kWaLCqNS%{J{6O>2-iCA_n_X8%U5$Z&G}{@JF9eLd2uJ+w{R zZR-=Le6)Da8OsBTyG%{9+v@vjn<#lfZ>?Am;ypz4v?X|{H7Mw!!SGAwwHbkaWl`&u zY#NtpNfF^ytZXRqrki$<4kJy-8D?jRJ4d1-2d0a}u9cEzGR1xR*mWS%cUjNN*HH6N zjJ_Tzyoa=*i836q)oGm#4af7ug}ZJW8n)0SvI3@J1EVQCS@pfi4b^EJp$ouur5=w)#-uqP?jx0HS)dGMd-Fns{)C4{|v-_Xc$Qoj46YYmMJaRk>!)>_JX zuYRY!q2W}HcUZ+4>mXtg>O%(~0cdC@QGGrkf$I85Q{o0;T+=B5RCCuQ z&ucEKv{FMCz3R4J)U=^qDxMrZh}+PWsY7seAh)5xNIe3>gSid0mf{fF-~gl^Y4wA; z4b9WiAvWmc^9OcYZnbkk$ALzg%{sj02u1}~1{<#c1tIX6vr%wp;+@@wh9a{Wh<~UJ zy_S^?`N3#)Lmh)U0|*Z%o!4se9!@5jef6T&Yc`%Clwc5Cfj4UM<^xe=XC($GRzezKOp z?mPs)=z*bzMy~vXoCHR~BoI@U8MV{kUTEN52R|v89zl5sO(a4U0%&LmB*=;S2@$KC zH$C-5XS{sVZ^l8p2SRw;o) z%{mY35L%lih{Xl_Jx2##prgII-;?Vh__K+rNjd{M9;6v#Ny24RF*@RMUt|a4)1_F3 zXB<{DKIS9Lt9f9-jKJqpUdReRUF8L_%CMMX`lOO|(*{p4<4btM-gIDI6Pi_%p4$Sa zE4D7ohYVCMMYzk1H<>Yg7vE!yeWN3P(o|un*td%EBTjHDb^twZg4&Gvo>IU?Q87?S zoCM2Y&@<`9;#dM-qYUErW2ZsqPyUJCs+|P66t7*L4_}SK?*s*lb&2(!4iMUd^aQtn zapedJXoGiMP|qO674QPJgnseY{EThf=&~&?$J8=LZyEmd8kpmi@D(Nl;nnZhN3}+f z>Yv`&S*eX65M6rs9xN_uTs8AN1%R){R*cvQ1C1aUr+ftRNpRv(0K_}v_?5^BYe_Ni z%V})yT3%cg*Ii;eoAV{74c?3qXLT{1V%s^@Tj7%+lP#l==(3Pn;niDC%NRqoq$a2( zm-n>e5?SM|>S5oyZXvZ!T*UJ!MygQ-;jy~qrWqfF$Jg0#Q*0TNLU{vPMnNl*@HWG0 zn9gbVU4gglTEqK# zv4iW4AhTLt7zsg@UAHp9l+P z6inkW_?_Tw(xlzi`?*ElF#_F_V{qaj=V9#3zIx4z;CsDT2EOZpQSsUsNH|$-aLz$5 zQNzyP$;iWPQ8+1LPQie{;iiaW*qmd{M7z@WR{9Lda@XaBV?v~X!^~uKyQfThPz5rWq zlCzfV`lJUhbH%HwG@SgRawn2Dz(Xos&g#PeL=wG&i*Q$d{DFlm-<2hNH+b(qRE3_%?n z>?jNx6G~9j%U&if4i8VSYk(z_;Ck=Ow|SLx5xcx`ym6?G>J_7lopqd?04#lhCv8gc z3(9d{qgbvUy-VHNG_`BSzIPLsHzMYg`;JMeee>YB zx(g8wc@q3*sMrrHC1X6Qr+OU09Y&<+=B}`Gc;nyhT7o;YUUFZg z#GQrSE<0Ewy16SbjsTP*OqpE+qy_yCvCG6H72|rm@sp;E!`u%^Ivhij2`{YsgJQ8$ zjkD($sYO(dLE>6K&Vs}#!y)%W22CjbjgH&_aJr2^_{yxlyBX~XPC8w|Zm~`eW`*Hp z9`gRh&I8Yi6tw=!aQ;Vff;r4BK~hj3HEiRko-jX`Xj^RwVrUo3{phMz5Sm|jFB=mY zrj3Rf7g$H%-*G+PNdu>)nxJK#9}P9HI3txIB;GI$Q%#K8E*oZ^*G>_wmE0Y=^~5vi zXK5lQXrVStB7%XuQ((c8giOc3b(D0si6R0Quu2$dm`xW=Qtc9sodlf^ua?147c~po zEh97GLNp+PpEpb&NApo>pN5^xqX{BSRL83EQUWlADLe%rpE>oPiCctCq^4ruSICD$ zM2?=nE%pU~yos(Z2A6vj3)jFX=!voXL#V2&C@)l<<(Az_(<8(0o6B88>XJk%DRx1( zxMHhMkP1n)43|g+|m3yF0hX}t@EO{snzAx%=?yca8b)SlkHnMe)zvO}HphYF&!CJMW{& z@jJ|dZpD@%E2ugfy59vw7=0AV2}@Q%%Me5xaPCW-A)JCfHV}+j!b5Fd8 z;)vg*&E35gO$do>gLQFGLV}*YCUb<=Ohc6&PLfF74|@A3p-ZLm$9`@h&51*wte{6Q z_D$wS5n1aajD2OSaij*lgF+N)y@s(bL^f&w>HyJD8$FK9lJ;xaMdf!hwk#J(W3J9S zV&>n<;!HCUsOO42t@Vdd?VQ{rF3F0M2PXf~dSTqyM@mIay(aN$eT!lI1}KsG>HqiW37jY5RZJ(M8tMrrjdn( zAO(X!fT+VHloS?1K}WB6$B4+v#q_7fgW9nT;(Dm6aAHVPG_?EUfh$-Ca=phFRM@do zA<|6BQs{m?bV`yzJZR_=6>dQBR*+gB4V{J+FbQavsP1D&=+aCB-p3YYGDKM-A*6WF zM+-uH7CSa+C3ocIT0OGs0ru}QCs#6 z2J@CuZyU7~9}j#V2b;yeSE&+M!uKF5kfdI%X*$QJ>imSJcAKRmCY(cuWk3Has(NVl zjZ-`{br_|VnW#~y0I|?F{jgAQ(@Y&k3Ga+5;LOQ`O<1{6ECMkWf61NHrft}GqB_L% z8TI`pbx-WhLF85W%+y;^7;7g$kI-CFAz?AAm?%gJX6Hdzu)I?0m`UxF^n=`1w}dSKt(l@u7;5gOX7h@du|7jaq4^JvG%K zyMD9&TvFOs$3aElX-2rHbL$wlbfG#M-Z5==;T`XrW(I=>_959dMN$7L(2gVNL^w#J zIP#;*11meA$2FDxR^9jl??{7=DpWCo<9Nvm^Koz_slr{=ovi|)9rR8gqO;)l56W^j zQ1xKWZ7Q7=JcJ15gr5as7s~OgM*VK8&qb*=ggD)Xk9zMkGj;+916mRlBh_|&0_+>3 zUbvL@4>3ADCuGE4yyXRiwUo#GUcy(ICof%F$!Hv@Vi#yxL7;6aY75&4h!=+=tfn^z!Z8gCk_L8e zt1dwz5T6|tTNK92G2Av97{ECj!vN6Y!k1@ysbu%57n($B-$y{=_CD zHXlqr!*pNaY&xq|xsJI{C?=SCBErgod^7>ZzzdMz@xVsua#7-05U;XKFwJEMhJqep z5lAbxD(^852O}(gvGIR~iZ}|V-@G3^^8ERe1gAFy8M)#c&QPM$6iu z)iZEB2oN5eKfB|W%|3SNql|C$Znd6`ZZWXpE|t2`IeAP_Fn9z{v43S@FNSgJ9|k3xX1pX#eg zsj8AD;op3aJ&~M};AEoA!DL+vwIa-Cw5#x<1M()-HR)_gQQGg6M#~AQ-CF)AqHqO{ zz{^_>O=-BbkK*No#lVQzbkRFu@+9i1E z64!V!h)G{pZ&<0SbR`jm6(FU;Sxirxrz(Mygao|oi@PTJs|Ua~7N?DdZ{6xLaKkyE zy2h=DEQnO~%92b3`pQI%m(Eq_Ji_>|n3C8pCitCic@TttE?ne(A{(xiIIguBK?D}` z#3{zB9I0wsB;_cYg_KB+A%=}Nx;re#O;rFXl2A!kFEtGj zO1Y&f!2FRo%z zQL0jDegal`+W;PzFvx-LT!?G_sko1xB;o5%Rdq`>BQD?2=~8%jQq{SOZaU~JJtB=D z#_6cK7(uF%IPp4GB9UsNiPtHjgN4ERK^ie4n5z0id?HZf(NEvdZmB3sN?RuGXvH>y zqgBjLG43OcZ40JIo)arRY^kF1#D^E~P*3S~6kJLb2jK=^zgxZ#P^y4=8p6cSQjIk< z0YtD(8u9s+zFa?}Yl#IXutkIE9%e)nNeSlR6Eis-G@r zDrkhHs#Ozjj3SunubDKGE?g0}d#VrUcp7s~|I{g&^a~*MY{)kYbwY7j&3d`Z{iuDhIM=;D^ zDto8WTrHAXZXr3f%cd>lvs`s@kV~oj?%dP`yFzETKk+kHyQ{kEsGPr4*URGaAc~MS z2=-E_XfVJlp>)-EE53tJs#sUH$~>v~rU^Y4d~KavPj|}Io2v(1Orr2&<=m0-q~_|2i841*rMYqP9@TN! zU8Pi|WYZC&%qo4OE7kRabmT@ES*empvLuU)s+J*$w$EG-Jjz0$O%QX{McKNPPvP;C zLqQVsk>nhoQH8)oXTpntPs!u)1)VMpCtx|)8+Z*syf%&;$XTxj+1=*zes+STVxL(ZyoCguY&Yg;S1kg%MbqkqeL%+9q`(K{I@Ey9-aZOI=8$0DX7imU{ zm{j+z(sBY*3CT}eF>+M~H1k2PPpYhEe)J+KCfFse$YfrgT)kxB{6T5Kq-#vQ#!76t zZlLu@P>!Ws_pEx|S38n&b+*+f0uKXNdRa)5awFxcOY4&}1>_8aKe+-=?wGZiRv4qh z$M4YLT!CL0DMtNQf!(-X+Rt(jGxU98tw>R$JTuo(ZPPhrd2$_9$L=yaTk#zSb4W5f zgYX?IpJQg5O`J~A?4UE|gmU!4kUXPB+Eu1_u6tY*DdJ>=`n@HHA+4{7$yE)jo4;a%4JNCnx()0R;hCMo!P~_#qPC#U<{rsq_~>(IHjgOB*B6^{TvNmVcN; zqWvYeGRs3vMo9hAGfiDiT%DI-m7L6y_p;`pC?i~k!(49e27bK`i7;;^xK~J#Iy~;;g@wserJ*umlhPmp4+M9-$hr0qq zaMY`_fhkUc2f5un4ATeIcMo%21J#$PkONVPrI}iRUP?ePu#@EdfW;7TLLVNM^2VEJ zDW44EbXi=MwSj7_CQux>Yhz%;dD^gf5{?5J1pR>AANG=r)Gfe`cu?XZV*!Ff7kQ$E zcU^_kd?OzeZZ6_&8}#!`dKLiAT#iSme8sQ2b@7TLmP;Dhcn5`nKy|r#0jLptxK9*h z$K7f5#yd3e>Lb3aR)JifmKQs^(Yz6GoJeg0ttGNOr$LB{kfx(l^0^xi$FXvdxDJSS z_$kx>yp3r9;ZM{TTYm^DnW)}-@0r!42)BfB;_+p6kt4(zr{R_sWmAYOqm4lFg9KNn z>u&@asCEFg4@95S`qE!J3?YX2!g6`zBfs{oWn54A+7D&^i^HzS&K9p^~?QIjO)1Ky1}3t}(9_n1??Q`#(|#g&5K%gh_WiK3SKBHUB|jpm`&U;H|QLO+jK zD{D8-0io^^(JbM#LKhG{kh>A=FJW^;dDR=~j6^+}o7t6${7gl)A~u4JPZ1mbknTn{ zgs>-d*K7=WiIk{6(N1`y@Z3V02~Cnz4-I9%t5>_sPHQc0SCfb4MbUfLZ4fM0@gQA` zouR`>#`p$CCxqKrnAjNRME{+*^w?s?UhN>}!9H7pofRuGp@UxxVkvw*hDSFh(p8{;2xJ~#=_K({L#}vcoVfInS)t2pj08obkohGr z2)y8i5|3Fw^bUBgxu(?%l6QkelT8Wf8x_%H7`K?i5sx6K*~K%`VdY0#%;CrrT&-^! zsw$5(wkw#tfa{Ec#Jr*OXG1y(d#=e~t&Ls>?OkH`uM(YJ)j-Tv}VMS6-s)b>(^`D!Oaba{kg`sa_D{&So4b zD)|qOi&TR(vw8yIB1_!}%O=PTb(XEnkkeESDN*-xD6=;J!a3ByYqBcKQk)Zo7RjmJ zo6cxa0;=o1<*X@!>=u{7%Rs~sXwzJG!dm95jPzXdZLwo?TB}8-`~xC&g9>%!nh2{; z25L=z?YI?ro<)K(a(zh(NqygNhM10*qY?Lo%~>x#}rL zy>%)=hrzYmGK(a-2|oFXE04nI zxt`Q`qWp-=ssV;+dJ;z=CG_xUuA%GjF2(sY$(s+y<|{wpr3-`A0CQeX;Ut_0etCQY znEY8ZdAXMHsW*A5G-0l%_f$+y9th)iv9;cs3c~zF03EQjvg%atsto*Hb zD4?HCDQVP9I1$L`5jcg213Gp)DLWQ9>!~ZcMR$+N4ukH8K^<0=0!KfSa*L#9!bcu% zAmlpZA_D5qpqTsrm^%ybx~k>v?=-=KTkv2fNQ=7{DAwSG2B%JGDl{zxE-n`+BnQW17$oRfo(Lzs(7Xpn`s(HxWiyv>RSGP z+f?~eg;I~guL&rAS9DlK>nMW@y<_AHnFUl4TA? z-w6tjfcvWM^%TASH@y(w)LrU-V?2Eju+0C)q^_WKGj*y)yp9R?z@57+>t)Pu@=Vnh ze65-M^PQ!sdNq9Q#mnv`O;w0id!~NFW~vquwPzT9d&E?QY2C@z9TMQl&-6Z;-yxc+ z5LJzqjr8oE&%SnvfYrO!$%DnIb_s0{tbQFd)&CO!v~HZH)J9D z`=CKX#*7;>xOv9`?c;}ZG%q!1^vHpun+J~GZ%D_|#BMoy@bIC-hm7r*FG;o^GNifp zz5|B04;a`!q}ki;Xin6rk#M`5Wkh_NWoIr_ZNs_Tc_8mTCLa(s{#tk1eY)0BNN@p8p8h$YbUfG~g zhVvJF$u_n)=PNdZCna!W9J@1kJL`V*XGbzeSt1;*b|ecFV?H!=MM6utZw@Od6yAsF zzGoUMBP2pi7AnpSiAHcO@P%=BxYT_=g4=|fr!4VkN;Ho8!o5swkz#yPYA>g@>3+wu zTMd-7AchI*Gs#xo5i?T^(d#!+db_UBeYjbOE@sIzp8IwvxQ z`T_5FnIWmu$E5JOm*Lc#9m%vw-KJz%H;|f=WZZzU!-kC8W5D>q!-tF-G-QtvL-wCA zdhFniC6Z*L_Ths@H19QJ?2zVhqnqKqng^H*-Ismta+J$J%5ETK*o*#;w&+M^O#gBi zWj&V4)R9b+)Vb^0@R8F{)_aj_C+7(RW2sF0^PkvBMH?Xy#p@_J9Y&%XGh}2STSOaD zMp&!D_hJWh5HXl_(7qrwfP1kTCw4~Wd69+a7Dhylq^`WFLhCRa+- z)0G3Sw800=Vk8fz4A7Y!$#hBGNV0^jPWLriv!Lc~>k)N=ZyP{{TuXE$jY-{DDwiui zTrJ9hM&coWHbZsrBUuK89|m#b(J!#eIGozZH44>q1lJI6f`A=ZqJf03EI|jMd{kHL zgu1~>pWz_Dy{@GLEs5ShVNpQ$9aYrQc#I51qt82%edrU6ld<#*^f}pZ-wNu=K!w7@ z9!UQ>*`qeXFYxtIC=e<@zd|hr&_F<0(8y#3&Q3EWEBNF9V%!km&qvXZXjU#M+$j`g zG;!8I2nO&=bT=0bJvESiB~{nqicw9~!jEVxvX0WZdU8567yJy0ONANCCh$Cz{9-7j zLMu35M8>>dCu3+Mj1Yk_+7YO@Qjxh4^octHz$B2OVAKsl+qm(A1g=SG?zl_-?k!?i z7ov7Fa?t%wpnDOGT4LSV?mik%AHqN{nKYw06^&(dIVN^C4P}g4!ZNm= zGBQex^5A`HPBZBj6UYk2mHncF+RSy7j8NCqZk>%T35H&-z z(iLf_D+tEcAWDL+aNP)ZrI$NMx=>aOFBE<}p=c8PrWbV=K=jdAv_HTJTtqm0Dls0# z1rLgT#7I`VG^B=-9ke?BbT1uugOHjBt%X?u2Zk2I>~-Da5E{p$s4UK3aa+L@dR8VQ zydMmpr|RA4erY_7|}3`p20YxLaT;Az0w1QMjM(9y`x5` z#&D_##NeOlM|`23`*J&O50rKk{fkjcV;Tbv#mDS*`H@3+a}CY!0vWW3b^xW(g@+sI zNKH+HLE=P@2D}>$7@inCXjV}40rbr^5yV1008bIr0(~}!iakhDQ1M+^P&Dpc9S;Z` z=~HCj7B@WfU=VrXR~V)kF=QM+LPSPW)50IZJ8*!sA^m`ep*cY)zkkv~ziv*#uZy7o zH4bjggC3*92g6m+?WrUfs4iOGj`Bs7^s$(<30ks*xuH;-6AniK=aMht{P9G1I22>3 zInn1Y;Xs>{e)M}h{!g)JEU)zDl`F7I05k3Zaf(Ir30uf=Zy9UZ-Mwf&4i8W4(GmX1 zVp@q*%O^}2_L?tjH*(*Ma>ts^=^n-UwdRiNnwXmKfsh0z8dY<4$51Yca^F0JVT%~f z`5N+o@tlzmMRcos42-H^{8qB|C)}!wfFi@YgJy!86WMnsRf=-UqAs7UDG!LL-@Zk+ z=(*XF)y+Xo;1A&O{nzTaZ>~`lhbiCI3sy6>5kXKQ7jeMzxhb(JnRkzWFWlXvZsJ2j z(BD;ixmwO{Ni`N54e?&~V;OaH8}H534w(KrL<(}n5W)1&n$|d{Hlk#11Y@r(`&Ce` zJ2MAC)ry`y9q<+KK2p8NQq}4o_PFaPco%GrVTvHEfFwD8=1V?rHIwNt-+}+ss#a~r z{MO5s+}}<>c{v_$0o`(OP3HEdc;BEJo_0Ad5xl6@PMeK3dg2DM9YujmH4OdZkM9!-1CTQp_;>wZK(6mm|1C{ZNvCD^KqEZ zv8os-j1`A-ldf=X{_&Ln7tnVkRYwhMAV5ezNaE*N)p1%anODE6t&QP~O)K7OkGI11 z8P-3Nj1(KBk>SUAfVd57zIf+ME0HG5^qTJ7@LMwE2$UZ_!OdA zCEcn>_bB_Bizx`AwkNg4ngk)5mkccw)<638=YpbXv{Q9Z_<$&8x$59WGWbyxLpM#T zIt(A5x&V{*ajUhHh8xa)hed0Jwz1`+N>yrGK|k`V#-RPEatKrXnq}5%CGSVWw{QTb zyS%`PTN+i3P5aS!!0H@j!pM$kbr?NW^(oF6^iX599q8|F!*dK*9RX%l>BlX0?D1mH zqaY&jHq(ofs%`FZ?Wn5ql1F`?22%n%Cu4XXsoHfUGpGB~2feN>GF^oC5kS)0$%49r zjHlX!dtbNGJd~L}n^H4l6UD9b^Aes}Zf4=y7x0;Ru13n2@EI{oCu^l@7cJs5;hawh z^^dQ{Gafih5H72;Yllo+QtJX3z-gTu%Z$Tx>7T1}9pUpj7UT1t>4Apav z=rF6cj4o(JG5e4hRl>r>7tqjf-JHm*KC{WIEnNMQIkPoVTjFf!ThM>ia_>)5 z9C1oP|Aj-YFcUwJ)>OK@GUnm#M&h$H0yvc$g=MzBZGBTU(f@+K1g61e&?iM7~ zt0>j{O+gdH*UAM=klmmw@FO8uO{xp>$}X%kYLjhl|Lt%EeNt_4UC=u5<#oaE4;R>( zC0n-4u_bnaM<`!pcQbKznO)#cVma%cm=8{?sAu^~o$f#RKseh(S%>AKz0kpUO)Kb@ zaJ60~LMU6W7mO6FnEWzdSL`(>&^3F(824+k0#D!}*IMDay)Y@(G5gyh0^6Os(F<=^ zSeMwMzhLa^O5SacxV?x10ax5F{0l}~wWYc{Hz}AT)e?HaF6bS!$qQIQ?^ z>6Ly#iPKoO`#b$)5PWkuR0^8dC)LF~e&g2Ze0#6jgf8ElD6DPS_C#it6t<|_eb8po z?ag4jC7!4wS)#g=!WIQ?%;0DMi=(aS@9si|JN^vkQNgr`Z739YH6NEpwiLpe=%zyU z45P$)dQ+hwW>z)O)vDX8!EP4L(s$rhr@F*~n7Q0cu)P$oqc4T#S>Rgo+Z&lxb2bOv z;3$~6=`D^zOKy{+pizsqIl}FY0`;zKgQL&@E_}dKV=o_9DbrpNv8E@dHCv;}#IR9kL3vPv7>AH3N#(Y+U()ZqMIPeJH95Z3v)EQZm(A+$J-8YaHnP+u68=o*8HnAF~F|~=&khdDN-G;RN8@ncv9_vAa zU=C&<<)!;x>3W9?oM3kNeF`@Kvuk8lS6>~#xQmYiVs|;l-EXe8QNU}lf1cd?R1V{6 zF@#7F>yAPE|9$Cia-E}=#C^Ceag`W1sbz^$OG4fC2xd_A=E_Fyfv?+`#AcDe-0SV=HJFzY=tV4ucr(%SF82;|8;} z4_{``8R?S7cfe!=2T4_(x6rH*BArm?Dy4z4(dyt zk5gIPX-IE<@U8K*H}jzEm-?o!cC3t0*c?Tx0`ZmCwzqs7_m~HZX}x>J^G* zFiXOmLfi{h5aEOg)3dW==%i6MFSFOyO2({8lXtWj2f-u`iKd{t#Y|pX8y5FTMX``&(mkbU#EDf%MHAh&z{03M_iM_U# zv`5_emrNgB$E!q_;NkP>K{OmzHod&p?k}l@Vs7;7eoM)$PVbat!{L(N$jA33b330P zy$mUxrnsf)9?ZddCn#KgLm}Md3C3(JfnSFO5qS5GQUcixk57X6ke%pR9}(xK1fhv{6v3eNALt;$R-50OZh(BDs7zUu55TVbv z1S`M%YmS0Q5eE=Zy|CYdc->>Zs=*A|j!KKNx;39GtH$bX=T2ulZ$SEr)}Ams@LRHL z*zTA?br3$MZ)A2P%l>nNX?Ltcz&~se?D4?S!+T>8xCK9+7lk(;Nk7M9$Dp#sCTiIt z2)s_Q!TbgSA5Qo_EK44{Je#>UTdOHQ{JPbs@higJYJ>b#3UsBo4b<-hv!*POSNXuX zmL+tPO?Mf)RaEtULBL*|qgR1IGK!MrmTu{J6lhiYBb{PdFB|y2eB6_>IErQ+mMG~_ zg>y;0Y#4Y!zrBuAGxr=&AQ3)x%5`+NZWX8JgUc#i%3P*enq1rK2d>53CArM?jSPBE135h;mW$xNxGqpbdVg3RbZ|fV z2&KSt5bA2(HCvUUU~8v5LAV|XmNK;%P*3m=lIqM1+T5+MeN)o2#_9bKU4REpK#gJh zP@`TqOwvu7DOHk29a?HV+7 zbQba64lWTPlM58{YXBNpniMVBo2Oj(fS4;i_Nm>*Ik~EIqT)S5s$Lon7ljnm`j4xh zXVRFvDl_^?! zl@$+8)IFW3<X9rEsa-h>y>&ss|r)5KXwD7mi0}plC>mCxk-2Sn_u|k+5w0^m&(p}(ywf7 z!p-2^G=j&^KWC^*rQ9?E-krl%TJeMhs%9W|N}AFLZJ%n%!*kNy?5JM!^lmn-Gixny zI+=HXDYO7S!>?vo^v(#XQt3&|5I-o z0jOj3=i_spzB`F?qUjU+!J6|$ejd24$j%b)s&mSnPU!9DyoSfn+_`ZXZjqtI;(=36 zFvFy-@dr@^_(ZNXtSPO7qtTpYOgXZa~)&sHvdI-K**p{D1& zJpe2!u~5e10*@`h1>&AXPI78IZO)c2dsIfdT-lFfk8o&`p z)0%v;;`8d9PY4ZYP=GZxUiam`NQEzRhjq@Lv8Sb6eXSne=3>7v*HD6UzN0LGUuWk= zN0`(}5?={$x=`AvWg=GEnJQi0?P7-H97$Nx>=ez(RfzYcafUZ`d4E@$Lv~!}Bq_`R zOB(-hOZI@Lv=Oq2b)%!Sh`Nk&_Z4a?qB}QEH@180Pz^GFF zP&!tz*3%UNy`!gi2Bx3Jz(xh~axY(T8zt(I{1Y0vC01B7zg_DR!+DNpKU=Ui&l}Mf zK9u&Jk3n31=>=Y?6A8C5OA~}U7NuTC_dnIYAq*MGGE>@<>6(;|L!BJj2=01x4aL)y zY`E)JV^ZuT;lE)tdT^^2i>Ok!Q^T?}91%Nmn_*zr;03dX<+ZoUNslpWno>HlwdlZauIvhxkgJ?&6(zAm)g?F z;WG6N6DecN+tNXF*3JF1GxyxCLH?*P7h-Qm1Kw??W^a$=R$CtQmCjWU5W?u}d3J6l z)_z1z@BGWQOzlbCt~J6PeGm&bl&8X6{8?a)`EyJ3?vymGc~ezJd>a#9G@5;2Nz0g9 z)p!-p4Q(u>+Q}N0Qy9?Bh9BCw^-Ox@l@nR$GGra2zt*b;Iq@$xk`BhxuWdxz>RTsr zVQ;ZXXkGSsg ze<&_~x=`ekiVD7;gV3+(*Hog2!zbz=49LTlC>ns29vqvS1l&gN&ln78hGYrOL{q!a{-zve+k096-v*gB1OWQrM8O@k={b$ znwCJZ$O2OZa~3P8$hKoFiH;9Vl+!`KwCYF+w`&%hMB~{}U61HW6j22>d~-g+8&e;V zi}&-0k8yGi#k*o!R%}kC@lO+F?>W%Tmn}&7(Ji=#fmVhaqWRRy?d84|1p7P4%>y)O z@&MWlj)J;xOZuyxvEWPzPx@fnbR1blsH@ea6-BKm3g9Co_0?|qO~bT1X1xEV$sSLL zSO}ek0uQ3Xd+$rT*0m)`3l*(rVu^5Oh@5Z{#l3%&d~Y1Yt(10dMJZY|TBVQj!F81;28gp2tVhBZicmZrYY3c%;2}MH^MYHz;NUI0q{-?8No(9tHh`xn zrxwh2E1jFf6KFgS&z4WKkT!c+iWsGDGEUVb=!_LHSjG^`L zR%^Y&Sj9H(42`0qU{9W3E)y$vc89;JC0t;Gf5exeRB;Vi2OMrsk*|$q?+@}QijM=h zHJ3lm3-`20Y+}jN$9h!;RV!6%82t+k#GA;g6eKSM=!U#rRT-1{S z7lt(sqwp}L#$@4nvL;in6FaYP&FOa5@yw-sZ%MU0@ya@k2EWT!P>=Nc?W$m* zXQM5;yNv>79#5>tMsK7LI;3%XjMjEdON1dxK1OkD-gW;xU`Db(F*Bg za4x!PF~+k1;42TStdzXh*5!Sbh_OG#Yi_wSj>?G#9}u$Ry8^PI4hP)`)!j1~@q1Ss zXJjGWWpEY4Gbdy1fl+jldqJMREy>MV%>;(2TYQHLAa1F6GHrld4cs%U*{=fP{gSku z>YEST@NCDqf3(z5%FW}Xnmc3rc%jU!Buk~-wa zeYj*{FI<$Te=h-+?(PxA+;9ln&JXmo-|S{GZ9xt>ii+rS(@|C0=w2X#0fW6Gtd<7S zpV_zAa8Tfjk>8nOx0eRoYi+P-N?%5#C(P0VPwy=9rf2v8R&RK4Oz+?YgBIF|@6d&l zaHo?l{u@JuAIQshpEQ_g2+>CJmKsil)jn^orK^yB;f|XG8&QrU>0h{vf%JE5w0WhV z-PCLy(gyfib&9%C z1d}%03`5goCv8whmjGW-mKFS|}T%AOuK(8t=ImBH@5)`pi~ zejryj0uK{;t6*&<2%nb8^;NZ^r|%2r8opX09A6_|!O2C3JKo6FP~R^ZMhL+aLu2UI zFng~QcwTrQJn`911JfNN(FK!itAqd+#$M0ycRW(-AJc_S;sggB?#`KW16jts&k@$p&WX5xA`|0b+8zF%F5c2QuAAm~F|R z5sGru)lN5(=prmwb-=SAD#!H*+##s;+;y(~!en8C=ZY34$UkZm#3A{jc~J>wP#)D5 z3Oq>+^lREutJ;Wx*Mn96;)1~G>kFyzum`T7==Nf;8deMo4tSi1l?n9gfiyMHOW3i1 za}K6k_&8NOz6z8zpQ!jr;b7{8$+5DA9^wCpiGm3c!#k+T`8~m4Lm;0JN`)mM+RM52 zzWj4~Q!g0rF(X*-h5LxXP^gxyv;*Qhfu)7=3!wDQT1jW?PH1)uEm*DPYf^rvEl}7Z z6WS5_d^caB@oqXy8hQ=ai}6ugQv3MsMoI1e!J;p$LONP2nOJGm)imolN@^I7-vSR8 z77s^ph_~G=38X!Qvx4M`=%qc-fG~0XA@h==Wh-CeRYLz5tmeYP zKk{DUF|!Z62OCQHfI4t)c_3_=(BlWii5c*V z=^&sDF9HW1AnVw&OQ>MqB8yg;mLcS?7euTfFooy#n{&Zh-Fwhnv9Te5eLJi--I7!t zS1zNNa}9#pfA$D_FoMIIaVS>ySTq4x)2(+eWO>N9&h7|cSH8crWisAH)Vj8S~-WFKQjkL`kO zr*zE&KaEFE$CILA3&F3#aO=Xa%z|;_#aGoj7!>{iQpmd>iFxl6Oaq_L4_en#`mk!Q zD5I!L5f*CG2D9DY9?~Ay9jx3?_RpB6cf&M$XuiJ3J6PExEWoZn=!=8dhWfeIUay)QQw5e2zAkN!E$wEa=B$c(fr_%hg4Z4jbtF z3Uq9)>E*OA6bS~2h93US=E1PT0U@Rs1O;1GZF|m&~OJLSp4K|>nc1@q&yg3Ogkln8-a(+?H}dMVEf$-C+H7^VidmXD?1*- zTBTbn*Ti`^zoG~nVZK;C?#qoOXCLQraNzz_1U;W^lwi7QE=cOVT&^hoz4W2Iz_s=x zH-w7>IG;^h%=K{^OE+Dhv}t=eU*o5J+SddVz(Xp2!8$1qWCNIB0|&VOHX8TcqW(HF zn%@NxSjp~>*9xYG&5kq#-Rtk5KiE~y_D&!#zQ=?3AiaPIdZ0g*4&HDeWrUc(ol)jS zJ&KHAS>*|19Ah34w%>mW>YlCV4pap_VsIPe+0*21B@QJanCm|0FxRlfG5p6+-En%~ zkp$Zk-7%97euACV?is^*8m1{Cw_Nbr9aDaqPDLAG%K6YbGe)~p78Q@0o4J;Yp22QZ z_40&5f~gm5Ai0-t_u=t?(K?9dr$*rXglo7t;09Fo{U^v@IgToA@)3rzDI?2N1WrvCNrvq!R|sKB*le7oUq{eQ1@F*350BL3GKM` zQZ4zWJW3H)L8<%GPe?=wy65&NtVFksr1To|_2%OyeC03i@8v%pM4f}+qk{*gTinp2 zuQ3o+#(Z+ltNd{f$7NkDxy>TeDi);sd2BGppLpEHl|uW4mQ{H;_5g@0Q0A*X@(IUL zF4+3eajOTAG+@eMe~A%oN-|WQ#5F1Mt@DVBJ##AbNOVD60WqKna zYBde(8X)59vR1RcE;20qBT87zVanlAc?^N(B)=&^&x(b^!W-yK^}x1k^>03MLXdTR zkpbgP`pu!5lOlL>kF~hfY1;GqC_56WWSc57iTiNlj3j^WzA`b=_KFx)E=XHIt8NYe<4CWO!Sd2K12;80O-_l*qWwg9rrhBvM3#LbyC~h^*nVMnXxX zhta}8Tb#g+wY-MbkaPBdw9*lh9do74Q_FS6(mZiv(7(KcHq(k5rV ztaYPh9Q_S4I6#5xvOfwiOl3EqL*SI35O#F5R+;{PnlUY9)*2 zkGkE+Q4?OmOGgeabOhI{in|3n8iVOyw~i?FEm_NKjH5Ya6T77XBqTZ; zLwGML9_p!Ul~($B6cyMlN}@}K@F~W0^b{E- zKPTyvxjxM5c0@t5&q_viQv4&2Xl6uY(eh*QH-tB6!*x=m_p3kn7e>^PMHX7fy{Naoz z6h!81s4T9>`%x_sOJf#mp`F8ttZni5%UB5jF<;$&Saeq?jqFu{q!E9I zkKq~IN+KM05zR+DgN1D`x|!p?Y%vT=BCZOFGLKD!<01>V7WKC-(;L{4I&98;rdxEO zABs#rBtYt|_`$5!p$}dgo@|)=nF7cK{;W(f+yBsMYvufDz*OZ}p5c0iM)? z7w<*S$Ga#DgH5l}M;4;`XqQ$W-?D;tslfwfhcv`FGOp53&qf9^lzAXEtM`@53 z0<-Qm{M4Ir<0IPzkvewIK*aUHKzLaw;s}7jXJ=mo7g^Fe(7%H@BF8|mQv?;jjcTl} zFyXLI8}WKjwAcf|>0Oa*J4L2LH6zuon?wW?CNKiAD(wgW!GzQcgm+$=2#WhYV_33!-;~_DpjQau3 zCF4sLF!j?hd1Tpudq`NEKwYp07y7_Rd=w^uG{qmm?q=fbYNMNpvtu=~5nWVl?UZgN z(~acF07G!nPXCRgb}f3IJED{Mq+zE-W`91B#({hbIUJXHT$>;w5iuCP1wmwu*0rUo zlpJf&k@TOtn(Y^nk$?b&@y~ciVkYcAV6P*5&bta4dl+$GDZbKCRcgFpv={P1LsQ_`xhfpmN9E4Ym;=V58k@5+PpGdDM z@7UFsh-V7(hL=TzjsK&4BmNnvZU z@EjjADO6>sue;w8|R0NHjV%Ozrs0IS$ z{Fzxq3c_-fN_Lna`-m;#4a{ZCuo`m9?psB@bhanM-4b^(+wYcW%lpe2HO;j2PGAjQ zGm9fe&5BqJ9TeX&hy)K|d*`x za8G#DFuvknTU$dxvwMV*P&;hb^hKJQaDRQahOt|;bk#$k8e*Wr&8U2hbmz(qIXEKk zS8AA}+wTZN+V#p|4dXO@f4YYGrfXA|Q$Kc5H7zgv1?%wmE22MPSLe5>YKSCa ziPCpPYiOMGLt-`flBHOk3yGId+o`=E~({9#x7(|K|Gve=UMK(>tH(x|rCo2kA zc=%h=k^R~%t{PGb>Ika>=HG%5+4oRUaSOD_@qhx+(=-2}j`HkxK}9;&C(&C6RUn8t zHgDXrJPKhYo!-%psK++6LdR;dnf^_msHjrJHg;FcFO_uWLU`ji=flIu~P_OQE89En%Tjd`8_hskwwFyhn7bI*!dWUHoj6X`%RB0FV< zynBg=cRF#kXTM7F#O(yc#Cj1iyKahTS31uyv7fM!0KX_-EsY`u)c1=lUF4nc49q{-K8r*a>HHy| zc*+6`9KS_h=)%z8dK*BeZQ*ElH@upYKJ-jKB2M>+IL_~)zF*zCUJNU>4Y9x(7IyA1 zHL!OZy%L_TxH~b!-PJ<(izkiCo_HyAd05Lq?5aQe5{)vn5s*Cl6ingNaJysi=V1!l zF`E!iIjTBDqBGx);f-7;CD;rYMxEdvQi&|v(nc_};hhi@3`~>IT()3+0b(=1*D(Ma!>F5Is*+&*4Tjst={6%@}{`sGJQ>(f~8_pTrJ*a$`TNn?- zjp25R8qBs$VgGw$#SL8OWBr~mI#YwkLLcjsU?;e6z_{=Fj@>wo><9ZE2ybU*mDUZS z_?oz|G@)4^FcfxL(apfvfG!-B*{_?(_I9D?*V~flCd_W^%M}g}y~dcmZKJ{lb>eD}d>iC>*fZR@aMk(IaqQ*z7&Ik_aNZUsWh2 zxQd2Jjtx-wkm&icFGe1(cKNz6rt_E33P;KoH1BKqR{FwlMo(LHA62(zRxTPF*knh8 z;3^bu>t@Gl_-T5~<_BS+n}ezR-K|1pbV>XzuENz3t`5a6Xl3U~Nq$a&^+HjhE+W75 z?IX+uNS(X?UFZe#NwI&2ixGuhFici<->J|G;)nwxir_4~Mp77w`}2zI%?=EWqDMDZ zC>n*;jN_toLLZO( zTw*O=p(-ivLfJw;u4weU+pC7hG>$8=iW{dmaw(~nd&0sU)}x0D@$IM;Fu4bjWw`b# zJDj@40*-wjTmbWLye%xZJ_uG)5Ui=4U(>z5!bugwF+Q_Ta25{kmEe%UR9EQL%LO94Rw`-GF)|1cLg@xfNzt|4UD2^m<+rp7qX55r za*>x`3l#<_*<__-N;ikcP_f_|OVmL{%$+`9g*{dtKq-lrHKA8O*B}TBvStcnSUt#e z!HJ&;-Abf0zOWs$IhCxU71q$&Z)+9qe)Xfdj~0a1B7Di8M@H_hc{M3d9twEj=6@~b z)b~xbA?<@gca0MHhiKaA@A@U;B~CScWL-F=wHR(N>WZkOb_n9#m1wMSvIk??pQ}ezrr;&61f+pzz4ZN$Td;652JN;aT)xNKPZfj z8g~mFPjp~I2pbIU8hiFG3m$I8V8FW^`7N^f&6;e8hn&@Qy9SHozAcNs+Y-eY1{R3r zOW??~Ox&^&2Q^r7xHol1ZcKH@WI7Iwh)H)$smM9)?MNac4(7&i zh|`_#hlqGdG;2GJa5u{}di8oAk#5%$IHiZ`rH=z6Pk4AZ8X;w`{<)s+&odO8y3x!r z3tb?S^$L*{CPdW3KCX+*7Ia^Bi4+mcJmhpgyD*A4!&pK%Lj-~t)QCxRGP^}^Bw~#j zsbOa>maItcBTL%23Ead%9G2Vgj^RZT6dg9`4!i`Bzdtzi(#XMOA05(u35mGDg&uxL zSIJJ#kgL?+Egkc65G{Z%?%aS53lI^y_!2T&Qnej@Mj3XrBGDmK{&g$S$yX`Vx3UBR?shTiB@nFl{WoF zE|KWaSIBj1c6A)>)YvjM5=LV2ti9&fyDpdfQYAVn5TBuwqCpKd#Pwrmbz_QH#?TDI ztwBTpQRnGa)M2a_S!j9L$VPN7uk2DhIv8~A9=Qw&+jtytGcGNEuWjBN0KrK=s?x!& z5cS@;LxJI`H$N~)(K6{*Ohp1N6d{|iiZrDI#q=g}v|4F6y2|o_XGbddjo1qBDMx07 zh7{@zbx!#8b2L%esyQNcwJdJ4o86HCRf`c_A^*eV&M8NoXnIi_*>uvBIQG$ntRLzA za#TbabXn=YJ|-JLMLS_E4QnhbaabDr#+isO&gY}kClcl6;n*z3NXgy+id=@xn+ly~ ze!@lj&(*n>uYig8U6exC;(qlLahvG{bhPR5&NMW+8*6#-kF+NIb?x7%0+TM3i4-q= zFkTEniuaafS0WBUbQ-_AYEefbTX@+*4{D$=}Hb_46hR zf0rbUNs>JJYm(eDeUkjzqb~V$_9W>!OIyqT?-RhE6$>3GP>XIGjNs@E+p`XeAxd+rG ztId!kla8)SHl^QxbXr|9qDPXypRF&bEKnCFc`AdDH*Us#bJkUV(yu3$p=bTBh*=;?Ng~{%m5B5ktpM}cxOpj#j0!i}L z8$FT>DcuR5^+>j%biIGizK>^+_ZBe(rbclXa;5 zhP+>&Or-M9_#xSwjoSO8`s6Wcr?H>ZCudVVdw*7+>_zFXno^%UHy?fe9eutqwcAhi z$xKxKU4Nr;PM0K0{#~D}N%kJ8Z%Dex&V$o8B(G4qhZ-A_S1H~2*&C8$DczHEHzZF{ zKdmu;L$W5NTV#=jq?yuvy;wu?&8##&Dh!u)-;ngC{C>EF@~8Zky0am9 zg7Q22UTPmI=OYg_B!j7b3q8@m^KRE?8j?S#JqNwmkknIu?(tefLS2*W`xgC74?lPLkf~&b>8WF;oh`Y0=E>9brk|-9lVuX>jAU8<|Kariy5!J= ziYxyt*@K28d5FX4lLs5-qA^VW%`?u>ao%;L5^u06* z1r*dV^iPkZtw$S*LI1qBzW!N9n%#ra9F)vNfA&ajpGeI>|MZ|SsNOvEfA>>8{WPChXyDIKU62h#&67|+@K3Nf z5B*Mm!e?kakke;Ao1W9rxaQB0zfXt!=ix>`I{J>i)Mu#T{0a3Rr=u_bNPX5wpDjQs z+-DSSraqg6ZIZv^bhN}weKs3^Mstflqvdh3U_v8)#m}m*Q z+@AUjZekt^>D*`ZW!kCF=s|?cPau~da=jjNB)4KJBpcv=^3Ou_WkBpkFlF68<-Vyd zfp1EZ{~vu5>Tvsv?3>`K_{Ul8N4TQRcefwm$d;Iae^f`dExZgnm+5<8D&e2M`Tq~p zHFH;>rJCtxt84s(nh!Cz{*=%a`WE`Bo*W}u6iwf@-Qv?F8#-6mgU;O0p(sg({_aS$ zCdj6XD@)fiiK00YZnBarEhIXNZ9br6y^(OC6&fneW`mHO?TJTjR!CH7D&rlL>+u4LWZ%LLH5)K*UjeGQxa?W7n3{0*dB*rkvuD`VIaQDt8+P)=O zQAi9CkWCA>NXx_dI})PMlB^^oTsPbNmy(bIM?xZ6l9h#oTW6c+Dmk-}v~Nq~Dni1^ zb8p_IBqYT35ynr2qja zoJ~tf-DfwFcK3-~Lr91NmveO`=df}@$|}j4LP9FIoI5HBIdi!~=30`qgoJQ#Imasr zadRX@uO(SqNXQ76^9&^+b&iAtwj}Ea2~h#rcB_&QK1V_jTatB!gv4+;Us4h>=t#(8 zOR}DjkRQ~&>w6_3ims0!mX!oS68Z?z#O0i0X=zVLq_YX>Y+=_65<pDuUEYK3-l90^I|8XctMf@YI;t;ps^LPFp`cAcjrWY^^m zIc`Zd77}vDHM&R1g^i>QMzq0u^rG`TlNtIfrYL}*Z< z2a%jjP+a$ulw87cN7&Gk^c4~m{_C2eB!UH3P6QDxbd$x|gif%{#${xLAarmfLW&l8 zHJBrzA#8KFk_aRmi2$P|*-A)g2$P>EiLk)Fwg>XA_}FOR}wy(2#67v3hYN0+SXxcyu=8{S-XEv8(n>z>z4dEJXtmN`WF3;qSLPA4$gbY%0 z12y4DY-h4 zZY-@%WF^^MNN5P#JV?nk%;p+wa}Ob*Azb(Klw8xuHJRK~NN5Px{ca`KGIA{@2M7rb z;c~vAoGY*NN5PiuKr4{Z{+$+4i$0(`V%#3nxNzcM7oyWfXE6R3AlL#4dL8xQ?j?&?9Dd! z5)v8$vh7SIH#BlXCWi|N4FTD-&vH`tjf~ug$-RZ#SjeuI)#k=VZp`EeA^XsuT%#YA z>_eohQ6C~Jw1e$x1P$SG&Z)U?Vm3Enn(LfQZo%X}LPA5Bd{%AtHL@?0V}*o< zfNc6&$yOsm%%iIh#8$d7zNc5R}ue zk9M?j?#SglNJwZ1NWVVnZ)ATa4;B&{0@AOK+Kg;t@(>}RAt3$wXeT3gV)9TSp&=mK zrdvTq$j(OY%;aH0LPJ1yR+QYu$X%E`Tu5jL$gVz0?rP+&OdcU5G=yzls^o4)?#ASi zLPA5>=0i&EZshJv9wj6+1bx(2x1!W&4V=VVEocmcqLPKD)X-y^fF`N6a%}ybqAzaQjCC3^$mdUe) zgobcACn(urWCxSy2nh`V*>$Fp>fC@l2j4Bs7F=-l(m#uaWyQ zdA^X)5VTUyOLQ$Wfk-z(CJ=goK8$&6ZUq_XCYQkjYDhgoZG==xRb9 zWaL3iUM3_o1a+T$leW^qM7mZwn8-?UxscEh*zBBMb3epv9>O-S5b{tVyR4jt8hI#_ zR|*LY;c{N4HV-rMFea}O5*mVXcHORX^KdKY;atwEg@lH{X4_9n9$_|*V4K$nd8Ck? zb7{XHY2=YiUMu8LLUygJpKNN5O`b5A9YHk(JY z&Fh7PhJfrmM9E`}Jch{|goK8G?7Be7V~sqP$s2`)hJb9kPs!tqJdVkmgoK80?tdtG zyphKpn@z^UUUX zZ1Z6up&?w(iAtVtBQId`Q6ZrrOn#v@FEsK(CLa^>A|bnG z&{21hkry%fxRB5g*lb!_$%~D=n8_!EgodD%I)^EF36ZWhFCns$JSikJ1U9?=t>mR< z^HR3?l#rJR*>r`Hml=5(lTQnIxsYv-DtWn)moqt8NN5PR(nm^OVdND|J|iSF1U2fa zUt7k~l|;H4T}fmmc~(eh2*{?zmAuNxtC)OFNN5PR=U*cvvbx%GznXJ@UPx#NY_@Hv zHm@<8*RahOgoK80dmgIfwPy2Lw)vuv&=Ai3A|)poIf==agoK8GY`b5{>x{gP$(MzM zhJfsRN6G7ryq?KdgoK8$&2`q1_PoK!8<>1mNN5P#+(pS7jl7Y`*Mx+Ipp`mzSXXS` zM5ODZn~1C=T|z=bV6$tSk~f>po7v{;LPA48Hl42IEk@qL5uQQAt682Jd3UkdrCk~?Vbj~e+XlT(C*hH&n)kCGZa zX5?c`ekCL{gljZjZ9Z<~<4k@nBs2v5-gdf@PY~(G=MzL$l5d2BhQMa$ElNIVHlJjh z-wFv00onDUl1~}=6qDZx2@PSJ>u7sEZRFETelH|6gv+^tw&!Fk=VUJD4?;pi*rtuo zXUyg^Z1YDUp&?vO>!W9le3r?dgoK8GY&ubMf6mC~nEbDh&=8cf{Xt!GJ#XcFp3C{O zkkAmW(K74HJbJ;%7nuA-NN5Pi&drs4(a0B>{8dP32-j#YC10{~zQpDHO-N`6*XS4} zUpDe(CVv+a8UnKOF(qFy@)aik5E2@~HmBJ@YV@j+uQK_kkkAk==RhT2Gx9Yi{}K`! z!sN%AdzX=2O#Uq-Gz6{G_=2|l>qNSd{5p}9g#O1T_0SNm(MFp48)owjwpk}6G$dQj zH;sIgNxGWNHlZOP+b&j{ZyEU(ll4MELpb*blziLBx0!4Z5*mUUb-kn9A^If(%t&r~t*|u7)0Ie2>ZLgnVDf&UKZ1-^llwoL)$124^ouaM9XZqMt~Cx1tzYtQe9tRziBLPI$B$x422Hos?^^9czJ0ogQ@dYd1N z{DH~&g@lH1Ilok!KN|TXlk|`Vdj@C-kL2Ue-qYRT`N_)p6PI&AA)z6#*>;VR|23Qc zWt$5L2@T!vg;Wo|1$C~Cg}kh?jvXj$j`@$?ODjzL4XMq4OZZ)81_ z%LoY#;n{Jlk_}eQ1|sSHr;yMP&iyJSr!kw;u+5%ALPI$B$CaGc$Z47EB_uQiWYecg zPG{tFOfDxRGz4VZw0)#Kr#EtXCYKiy8iE>ip7D%~&l!kxqizNwD+xUY#jOMl;ky5z za$hAIjcjCcWg(#0vhR zO=t+n_AVvoHgawz*AWsL0($7lHYvjC4t|ufk1Z3-UU&;t+ zGO~%u^@W6nVAM^SN6Gn!bmMbAA}h%TLPA4eb7Dow`OW71Y_qqJ&=8P4S5tBUBNt$D zLm{CdASd@xazP^(WO5@Rp&=mKcTjR6BNt+FVIgpX}K@Sxwi@l z4PkP$uHP$0R+!vUNN5Oh?^;7`wpi{hocmTnLPKD)ZGw_Zna!ox=GH<&LqK+(spQf| zF3sdNLPA48cHO4rGDa@LB%c0*9zjF6k6ux7Su5wVT+Z!;gobcAe^j!k+3d+Sw-*u` z!sVQEGimu=M)qQ|pODZHCQnv!IU|>2at9%yAt0NkXv;5e&fb6<*W^*mJIY3Bg z2*{>Qwvawr+sL(<94I6-1Z3Cdqh)Vx9V6Faa*&YF5Vkp7$#sofm&w6GLPJ2d9i!xW zMy|)?5Fw!qzcvWM3x73fW43vZsAg=TR$>u12jy zR+0`Ow-lRgpDDSe+1!$CjuR3Z!gZfcdvhxzw_;TkczP?)wS}4dJ@m zjN8U+Zo@Vw2nh`V*=A#DTO+q+az7!VAzaQQHTUg|+>Xiph1_0kKEI{R_w9|`p2-7* zgod!qZpzFnPF;&=AgjtyZb~u14<45*os}FQ?=PBS$cKs*unSo|{`L*-oTu z&vqg!$!S7DL%5vnN{%#}BiZKZLPA5hoX0CU%E(bno*^VOgv)uYlB10r&E%OvLPJ2d zJ*DIrBgZg#mXOd8F6ZY;?qlRWOm+$h4dJmg{npZ+V~KQiA4_BGjbf0=L!i8;TmnKx6`caC?5JH;*T>lH4pL zG=y#5r{qMlIgxGNA|x~fWaryTo?zq&Ox`LaGz4VVA4;BR?bzQvfR(&-0u?-8UnIyX(c<2>}2wOA)z54yVl)D)|+P=c{Y;| z2nh}0a(3+~xu0X@JcrBqppeiIF6S>wo@+MGWt$HP2@Th_NN5ObcCNAfG(BjU zJ#BK4*}RBtJ}%_NLU#36@?s+|X7ULkp&_u@G(pKrjJ$-&CxwKDfNZ-$%Xz7hmooX3 zkkAkwb&o1}8Ii6=ml0V>o)!`s!mad?l9!v!%h~2+A)z5$&W0VNkFGHC3MQWs5*otg zTwKX3jl7b{XN81@aP9+?yvoR{n0!u1Xb8y84VApw$g7!rUPx#N_vT?rUPGj-`!z&X zk{5)8hHyDAQu11}c`e&~QAlVA=YGGElZ>3iVq| zE+L^IOzx(h;btRmX7Y6*p&?w(XVvB{M&82Y8$v=uKsGKt=xz7Thg*%jmB}}Sgoc3Z z{7P-!X5?*5z9l3y1Y}p^=Mpd7ZshGuzAYp)1Y}cBCI4gOf0%qnNN5Piw!TWp#&=4-?6H4A= zYAtxI-naLl7godD;ZNF;n&saI1;d1^c zBs2szn=aK6@~qi>mTmqdBs7F;bcB-68TlNO{}mD%!sTqz8a;31^GyCM%WS-Zb(}CVL18 z4av@qw}^D*e2dFjFC;XC>u$5-ZL|3{+iVaL8p65T?0Cn>cbJ?;NN5O?GwmdE^Iap~ zWs+XO%Qb?Apqx|w)}7M#teo$0Ij0lyeIYxS*ja48Z{+(-PA?=h1U9=iQt|^MKVWhO zA)z5CXVZI1erV+d=BI3P79pV_*&2OjHa}yVvkD0f;Trv|Ha|D=b0%jK z5*m`N`xi!j!Q|{hLPJo_&U3YuzO-_F$>p3wNN5O`^9?1Zn9V6{b50?lAt1Z%Qt~S! zzhaVJxXDIo-k>4{ppODZHZuuXS{K3c{n4DioXb6+PYAgL{5b(Bs7G}X?6eA$X}Uk77`i)vTH@H(Qiin#^j?y7ECy}o1e-c?qmJkve!ZmtR$-m6z zUu<(pA)z6h`)^AAZRFofR)mCxaPCj+F1aTp0WSBX2Xb!_k_@=qKUK1hNV)$>B;EfM z5*os}PrHZM>|taNCYKfx8UnIwNhRxztY>l=A)z54oBAl(U}OW6%L)k%L5`Y~pg#mlyC@f`)MJmnk`)*_@AUZXhHy z1Z3y?x|*2Z$oZM_ZEh?iGz4VVvPv#&~L{^e*g@lH1IbTt7S+lt; z+uTk_Xb6|{MSbgvCi@8q4dL9kQ*t>Ymt%4VA)z6tQRl*w zWhZKRB3+G^C$f_4C?qrlWZM|Mb-aR+D=^t#NN5P#JW0tFja-pQe2)Td$wEW8M%OF3 zl9h8MF6T}{LPJ0{J*(u(My|}{&O$;%xb9ymxr&jiFu9A6&=8QFjn7HlS2c1~Ch6-| zxSY@sF6XjJu4d$FOztKmG=$69SIO0lT%F0?g@lH1Irmm_4I|fJat|S)AzaR5m0Z)v zHJRK~NN5O`^C~6RGIA{@2M7rb;gS4=l4}#``h9I8E6G41p&?w(PnBH9Y_7vL2MGxc z;eMa?d1=pe&E~pnbFh%m5H9DEO0H)%*JGPQgoK8GZ0e)r`bMtLPdM)qN{T}WsM=f1v@n;5wXlOu(MhA?@LlA9X2DU+jwgobcA z-&ArlBR6Amw2;sckX`e=DCOMT$jzA?BP2A0ZMG@7g^^n@xsQ<05Rjb{lpD}(RwCU&ju#Rdg52A7 z*H+rva^ISB-&aUz2-oN!CATq~+px_ELPA48cAcl>wnlEt7aPKCBNa(gBZ5E2>!vi(yf`x)7f$$tq64M8~@e^zn_E9VYe&I5&n zhJc(j?HiK&jz;dt zQ_fRzFp;j01`|p5KZS&baNX}wa){X+!Zs%g2@T;Iy{Y6-BZo41f{@SqLhIh@Ipg@lHHoVc`-dmFholcxv? z4M8jQTvN#rM7mZQL1ZO4RY+(EY);-x$#%2Z&NfdI5*h-sy-mrHMvi3ibRnT3ARG5m za+Hyym^?#BXb8wj6Os#~3+=$+LunhVTeEQ^|dZbamf{$V$>F zBs2szCtjiCShG2nZJsS8Gz4VN+m-AvvV+NUgoK8GocySgASX2` zd7zO8GI^!qU6D5^I*1lxscEhkUd{k@(?2rVe$$g zp&=kAf2ic4Mjp!Kl|n*8K(_y&BP28gR@8~o^0gFO#VkmXb9Kn03}Z` z@)RcT5E2>!a?-I%o@(T&Ox`IZGz4Vp*-D;f!8kkAm2lMhz%TqDnA@*yFi zAt2jNRPsC{&tvjoA)z54CtauH`9_}4!ge+mf=fz63;DS46EyohZ+E+jMrWX~x|UToyWOg(We6cQQ&a>`>$PBL;5lP?Jg4FNgvRVA-8@;WA8 z77`i)vgapCUT@^}Ouix{Gz8@2pOn18$Qzh^RY+(E$o6Sokr8sEkvB5=nvl>CjJn3T zmAr{aH|B35lHUI*Bs2szCoQ4m&1Umvw)wh{&=8QVt0;Mkk+(4UhLF$@kW)5N@>U~n zW%5lSp&=kAZm;BRM&8EcTS7uZK=vG@K*@WIyobpTg@lHHocO$w_ZoRGlOG8Q4FTEnJtglm@;)X%77`i)a`HDy z-f!gnOnxFHGz4V(-%37UMET3E@4jC_d6&xM4B zpx;}UQ}SUVUB5p}WF`4RNN5OhpSY!xkC@Fz*yfi)LPNOj?Mgms= z-wFv0;kr*y@+l*qV)8p7p&=l99wIFW%4H>p&{I!k1F{bk*?pLBeIhGS4e0GY_`6l6aBfFUVTS#aK$SDJqeBH>`nZ$P;<2();0&?O$O1@#_8%)*-2@L_+^Drgf zH1bU*dk6^)0Xg|(CEqggEhg)Qgoc1@zevfqjeMKQ1|gv#ARBK~@*N}JVR9NFp&=kA z-LK@kM!w7Bv_e8dK(;=o4p42$cbMo`GJuiFgb&e z&=A<{`InL(8u=lUGYSa}0Xcc*7i1)VWaLLo&Lku>1Z4X{N`7qQ$4oW~2@L_+*h|Sz zjQoVjnT3RgfSk0BlAjv+DU-7Z2@L_+dTi4Xv+?s>pBecXld}p54PmlXZGLX#=S!o0E@Ia*ElU!Zzm=5*h-s z{TwB~GV&`X=MoYc0&>!wN`7tR*G$eWBs2u&oYJM_H&)JXxSaC{2@QeGiJvL?t=asR zZO$tsGz4VNUzGgL$nThJ5)v8$a`JR9%2@i|$nTk)Pe^D8$o6@a{K3c{n4DioXb8y0 z7A1c)@<%2Y5E2>!a?)x_{$%7&OfD!SGz4VpCQAO-$p13AkdV+2ZqFT*{FzAC?>`es z_dkV%hQQ{;Axi#YHh*E8iwFq~0oilBlD``HE0fJaLPJ1KK2piwjQowsMTLZhfNVcq z$={9qoyo<7goc1@yiCbIjQoSi#f5~1fSh!Tl7AZcCzDGE2@OFjwLYxmUqrfA`isa) zvZRpE5ZIjZqLP1`&A-`ZMM!7}&$thiBuNpQcs(cG{}d7$0-HU*SF(;sx&KKd-TxF4 z8Uk{1ovz<|7}IU%7T$i3-1C8xLCr{~<4 z7ZMr*vTe3^B=;GNoPo&|goK80IhR**M$3Ih&V5B8p&^|6C?#hyn=`S^m4t+baPB84 z*=S@VlPe1e4FTCXNy(XwoSDg0goK80D?P2`ELP50xSXpB2@T=gXM9)cKC9WBm2Ivj zBs7F`Us}o8jGT?h)rEwHfb7~#$=QvZoyj$Xgoc1@+DpkfjGTkXHHCzRa5;}wa!w=X zWO6Mbp&?xNN0pq5NH><|B9iWZ3JDG28ZG{w)O~KVIrsk~?JS`5D7G$IEguqsOC}*9 z2}}kHafJY3GJzOwgS)$gV8Pv;!QI_mg1gJ$?)Gqp*XMRsrB8*)np=5U|9k7Kf3Mxu z)zx*YfUqYf(2Rj(t-uqU>Yrn8Y1od z$B|vVv#U5qSrRow+S%;`+n?P$*-gkMOQMEIJ4ZNjAx|zORs7cR)MmliI_;_ts(5;a8H z`GzAG@yupS?WUOUMb9L=6GiI>eC;o@@|u zWlN%l$kr!1a#2q%DbL=BOxpXSKLJh_;Vt6CB@M7Dm5BNzAN;zF)wNz@RKbuT+| z2~RE|B%L9T*H#6Avi|OKR9Raf-H|w?}BVFYg!UD1Z2wzA6e&8 zo?J@EwJeDmB71b5Bm4NR_mQoyZAsJ+kj-B^vacum3b~FYQA1>po^@nDPxcdXT}z^d zfNYuVW82RDp6oAVvn5eOq@7DRa)2iX2)UjmQA1?w;~Y8AlLLiZ-;$^yvh|}JImnZP zgxtWAs3CH8T;a&U1zGm|;DT&08(I=IM7I98BZqkB5OHo~Nz@RK&7V4Qs3(UCxv?ct zL!_OxPi!v@^W-ogH?bsY2*}oYM-KPoa3MFfBx;DXv&oT5dva+ZH?t&ah_rJHM=s;Z zWrW<^lBgll&O;nI!jmI}+`^KmA+q%;j$GE0%L=)rB~e4r^L4%YO_@`!4VNp(vgem8 z$Of~OB~e3!+|qpoeR)qVFXYyiL=BOxzvaGQJkoD{q-=c~OQME=Y`w>kD|m7RA-A<8 zY6!^Y$&PIFWTTMVSrRow$U|K_M|pCTklR}lH3VeKxz5?-$tEFpuq0{-_PyCSa&$qK zy)?QYi@*PFNz@Q=c6a28-npVUcd{gE2*}pu9J!JwR}yk(OQME=tlQX;V>~%V$XzUn z8iIDVTfo%UdY`oi5h~fxBlY3 z7c{|deS&O#4@;tkz}bAQ`wGy?-np_k_p~Hxh;03MN3P<@RfL>qNz@Q=_I2c{o?KPP zy)20uBIMvT>~9=a^WCw2*{RIoO5+gt}f)>mP8E!*}9V>*YM;TLhfTp)DRg< zH@mH`S&-#eTC*UFzyEGY)DYSFi;i5&JJ%BDewIWHk*)vFk!yQ$Z6Wu!Bx(rAy186C z*YV^!LQb|MYKV-bwVZQZ-_CWVod;MFHAJ?)ha;Q4vss)6S`sxxwtlK3*Yo6hLLOvE z)DRg&w;;wj^qZ^yfdjSaJhTZXo0#mP8Gac8=K9k{kN1Zzx+o)RL$n zAe;Ad%kf&G@H3a+KvWV-Y9SgGD_ZTZO@7zh8 zr&$s;M7I8bBX{=X&O)AUNz@Q=ZtlokJh_XIXIK(71Z2yFj@;Fgy9#-xB~e2_HXrQB z-8{LQkS&%(4Uu*>I&ybU?k?n6mP8FfFSXv~&Z9jFvh1Zj3bMhRZAsJ+Y3FI~x9WR( z=bqv`$C9WaAgBJSeWE8P3VE(2QA4DiL!5IjPwpk;d6q;C5$8INoaD(#LY{9))DUsr z;>f){xwnuPSQ0e^Wb;Hv?&HaQguKv_s39_kyy1Fj--0ZAY2SiuFc(=8HAI}hIdVVm z+)tbrTM{({WJ@Qv_5D4$zmS($5;a6d@`%Unih8o&`efPqrIthu0ogp;QqC{G?GW5)#geEYAnSf}Vd8Z{&LxkMLk*9d_6d~`jBx(rA zmg^jOswYns@@`9_hJb87!I7tV@-!juu_S5;$ktUIdAcW07xG?9qK07KTOV`h(HR9< z?)w=9*aA>yn#=lR}wzBnJXBx(rAmU>5C;K>Vwe9V%lAu>Ma-rcT0FZ5f# zP`3WKB~e4k;de9Drj zAs}0?ci+gm)RUJA`LrcbLqIm~;+&Ux@-iWxu_S7UwDSZ&Pp;^Gb0(Z%Nb;Y3J9DyvmbT3HgF0QA4Div$_7f+LKoc z`JyFJLuBjY9C?i=uMzSkOQME=Y+b^U*Lw0=Az!v6Y6$kdc{@j5SCHktUssR~<`qk# zhKTbDM_%up*NgL2OQMEIJ0ExC4W7I~$k!~18UnKUQ%BzD$s2`y-IAywAX{pC+EI6t zCvOt6)sm!Wiyj#c*EQuNdvgIvD-s8!8 zg#6Hws3FqMKOA|lC+`*VBTJ%&NIScHZI97?p1e=Uk1dHBBJCXR$ooBczmT6;5;a8H z+3d&%Jo$i-pIQ<%MA|vYkq>(EK_Nf0Bx;DX^Gruh@#GXCKer@mh_v%|M?U1qhlKpX zlBgll&Q~4zuqPiD@=HsihDbYqa^xePd_>5vEQuN-&O_YU@u(*s74mCKqK3%X(d5X- z3bH&q9xKQO^Nl4@Lu8NMcjV*V`M5a0wIpf?$hs+xe8Q7Y2>G2QQA5PJg(IKzv$UqMdD?@#Hf?{%A?m5Ri43 z?A0)b+;xA}lg|qIlO<6@(4Q?Qx|!j*f-L*d{L*CfT$29|bd5pd- z$ObdNB~e49oqn$S&O5&o=K_{Q4Uw(;x$b|S{GX7WEQuN-d-R-Z=l7ocUdTF2qK1HM z`Nokyc=87!J6jSpM99zfwny_vPyQ%m7fYgs)T5VX+Q*VV6=b>ZKNVzySL{a4@4U!|Q3SrRowdTF$C{^rTw zgzRof)DYSCEgkv0Cw~{Rhb2)%#5r&udv5;W$v=ebX-U)&kgbP0=bxVZQ^4_R>F|{71-MmP8GyM>~z{&wmTD zJevO&WP@q2Bx;DX)Azi|-CLa|$6K9?S~9U@^YYG_crp=kF-xL`NITb__mA0RC6IbD z6>@P)qJ{{$hU@vvlbMi9SQ0e^WV5eFxhHcWm$W2mh&Z2|Yd`_O~Qz2*}nC9XXpPXA^ROB~e4_3$kTncQiZtc6O9@4zwg{ zh&U%Za(3^WU7Uj~i5ep9JkODHcybOQ2U`*~1muLRk9uTY86k6ea!w(KSQ0e^Wb?hw zIhQBr5^|^|QA0p}J<&Pm_T=0`4zna`2*}nyoO2#e&LiY-OQMF&sda zHAMF4Cr5Vj&Q9W7&XTAhvi14)w|i9Q$vPpIwkf2egC`q=T-lPSAt0O2cjTg;TvW(aEQuOYUyv>L zIdZXrERW`51=(O$wIpf?oULy=a&hlmT%4;}5;a8H`IjS?@Z=Igu5L-x5NYRv2iaq^ zq$igYat%wOhSV2Z@7>*fvAnl$XK!ienwCTj0ok&&b1vn{rG#9|lBgjdTi0`BA5ZoX za&1eZhR7cM;>fsS&sME2;EgY6#m^JG6E*R>>Sh>$JL+251>g>1GYYDj(2 z&boaZIl#AbfV6WxOQMFr*>c&TcIyMZbD%iaw$Y*u z!JZr}xr~sTTM{*-z95_D zJjC|=2*33avh^)2i5eo#l^waPcP=Z=EiH)}0as?r`wIpgtec`Oz$+fewAj>(Vu^=1F zc9uj9k*&|_+BwQQM~QQLOQMF<7tYp6d)sS-Chu$#=MI)c4S}=nOh=CP&e7uB(UPbk zvh{=9zOU%X6@}c%lBgl#^iM5U^5jZF?rcfakoscl&-8Kg(iq>)G1AUmEQuN-&X3)` zkM+*6;@s7es3Fo$e~iX?a-5L6SrRn_Wa|oUkH&j)ypX$F5;a7&zL_H@cyfY}dsq@R zL`Z+!SN7z}Lhflv)DYSFx30#m;>lHnoM=hZkouyX&0ig9N8PFgS&ooZ3$nrNWl7W! zI9oa#Wy#gNb2V{JvLtGVv~#Q@SNG)VLhfxz)DUUs;*MOylWPdMk0nt<>aq1B9l54& z=bF;aeJzO^0I&wWvt|#O{mP8E!+48a@ z*Z1W5LLO{M)DR)papVS`+(5`fEQuOYkN*7WeA`PK7Gya>HY~^nbEqXzL!_PaUSP?M zymKRQ9%f0@5ZU?wM{exNjfFhilBgjdTUU4FCZ60x$RjL?8Y0f+&a+$J)RUVEd88#b zEB?10y|m|rc7$wJkmVk2R*((mC`+P-$R54q$j!ZTb8#MRNz@RKb+mfY5p+X{K2B~e2_w*J;)dwx4lZYShPmP8GyFUaOz=UQ_6f-HM} z`+{sRCtDIVM7G}D9nBrQa|dyrVoB5xAuo2$9X+|Dkf&M_H3VeK6i4pl$(@8e&621g zLN+;aXHV`daq0;9J#x9?k>)=EQuNdXWavi+`~Kf5a-#JL=6Gi+;e|h-}m(7 zo*Kd7a+4@D6L=Azn z^(;s3=bihB^I}V)hRDAE>d5^)xxbK?SQ0fv+S%&J$)21nmf&SS)RlO<6@KsJwc+$zi5dd3 zWm`uc=gH%Qyv35JAwv2!!||RxUdUT5i5gOmJ^I0|E>0-Oa*s|Z$OdzpB~e2_*3EOa z9U&)r@u=;=lzyM4FTEuwR5(3vPH-TEQuN-?Y!QRXL<50 zAs@6PYDhi$^9(oU&-U#+TiQ9rlBglF^;aBuj(46T&W9|C8X|jinc|T`d4Z6RSrRow_UL#= zUg*gSg?!wS7g@4pXGdP-$%}-1!jh;V($4D~d9f!i7V=3;qJ~I2pK;_Rp1efJr!0vY zQjcC*=N5a$U0RUkSh}Rq#j$Jbf?|=m4542%GRH^Bx;Ck{WeEl<(*fF^94(yhRD{> zaOBmVyjsW?Er}W;WM@ZS7;e94liAt38sb>y|4yjIAUEr}WeviT=RUgyc{gnY%4 zs3EfTdGE6Oe!VBJ7xGn0qK1HM9p;=jc=84zU$Z1?h_v%9*Yh`e@kJ()N;^NZFZKqbEH1gpl7^ z5;a8jsP7|okDm18lS2N_lBgl|#n$Wga<${Bf-Lvwse)`U-&+zjM7I92BcJxpr^We$ zB~e2_w%qE-XFU0gkUv@yHALEZx+9*J1m!ILit`Ku*SLxeosoy9MD@j(pvduM7E)B~e49mu7Lj)auDrA^)``YKZjG zVvc;nlWz!VYWDq4OE$0M$TvOtrjUsxQA6Nt+1inBdGakGQ%jkG1FZ|D4=Aj>`apdcH}ES5wKfwS%oM}Fv?ABuBUOQME= zZ28%dA9?a4Av;(SH3VeyYmWTblOGE?n?{GX7W zEQuNdvSmj{e(%Zeg{-qAYDhht-7i`zleedS@a_CT+S%EXs3GE9^KDE1=$$``vx_BB zLqIkUa^z2*{7J|KEr}WevUPVy{_M%0h3slc)R20RJ=`4fi*M&I(#~#{L=Aznc@sze z>Ycxeb0JHjhJdWQ&yl}*@;4#7TM{({WXl1L{N0nk3)#bxs39O*&vWD-p8P||o|Z%n z0a-WFk$-yfPazk!Bx*=KwtkqaTz?g0IhOt^$Of~BB~e4f`LHAZ_Rhb>S#L?y5RlCu zI`SV+{v%{BOQME=Y`w&h|9bLYAsZ}-8UnKJGgseD&Ax3)#n#s39O*zjV%7 zJUNSyeJzO^0vcdGXBx*>nf^1pRk+XT{ zY~mbXNz@RKt>YZo(UTp89B4_@5Ri4-J92hU&MxF2OQME=Y(CnNb9izNAqQI$HAMF4 zTX$VNXF-;GG-p9Jm?4%#4UwZc``vcb&E?6tgdA!~)R10i=LAR2?YBO+Y<-v|QA0pB z@8HOJJUNe$!!3y#0ma(*F4 zSQ0e^Wb@~4>kD{t0U?*QBx(rAx)q(XlP5a~xtt|YL!>{Can8DeEPKALAREl`mP8Hd z1?Q8F?ChPL#W~WFs39O*uX1D;Pj(S<1xuoa$kxZWF~6YS`hv3cMoXfG^uoSh?#Qm* z*;SmQEQuNdvUM9rcJpL6A)73T8UnKJNJlQ@$%TX*ZAsJ+8K3jqYDZmn-_GvR&J`_* z8q%w9w)A&o5AWcq}LCBRYxu_*u zE^u%AEb7Tcgy;Bx(rAmM8bM&x?C|vbT_HS`sw`Wb5aS zT*{M63AvUfQA0qsbah+r}R*$*OPsPT*s2AAs}0qan638>?h>9 zmP8E!*}SN8_V;9eA)76U8Y1Mr&N;x71B6`9lBgkazMtjDfdyF}qk#q4VAi)JYDh1% z^G-(&^3Flx+`y8kAt0OgJi;EM!JZr}YPJ7IYh{fEQuNdviVm>4)x?v zAvd-pY6!@dQ{C2wd2*PLn^+Px1pD6F(f#&nctMu?KD;0s%%+w^4e5n;_IBjb-nq0m zH?t&a2*~E~j$Fo*%Luu-B~e2_wruan5uO|&(X;9 zxq>HG5OP~fqK5Rs)_Xd#(Qmy`w!WPuQA0qsjCABEPmU6DdrP8*fNb5wkxia#5^@Ji zqK1I1JJ6A%Jvmy)9W99(0=^Hz z#hBRwss_I#{@aX%KbV!S!Yj>W2}Ry2j(E_ z90_uabud}yBu|EpOV)W4 zWW5zvyOFh9f~>dVe69BDLS$Vi zL5{Iu<3OxA@Ba@#Fs>(IhD*bFb6r~=jfU!1Iq z7k?)z&dO)&ELpV&X33D-+g9|z@Z$dl%kiqVbsV*IoI!4{bx5&ND@Vc5;(x2wzGUrd zklSmWdbEZG=T%PD+#u_%gNmN0wzX@;+Gx5K|E6cHj@Dttk;n1JtisNP$hnX~Zo8%b z?HpM2Znd4=$l1*x>zzDOyOXoKLDoBYeciv}Y?9Ud^qhQ7RgXQdtyU%Lss_33mj1VO zc+na@|5qUE3Is3U8gL5{ItyfK^tXb{5B6ep1<`lNhL)Li=a!gx! z7FdL=ix_0RHLQXwl66IcthJ&C_-NIWwZ6DFqqkz-=DD>OS$h?CJ>slf1v`+nLvcqV z#)`FNwGPZi*4YfQwqG&3a1Sh4u{N3oi+|Iz{&&T~Dy!Pk;ytO#IZ%9eX}Zq;p0jbR zcy&E5S?4v#`u)e+rP|h>WbJ8?W30Rm7)DmClVhyIgC3ZVtn(S$T9uO zm7ot<`xs=s71yuTwvH$3c!R99V(rG(CCIvjLDpI^YxCM|QL+|a){C_AJg_8Lmo&&R zZDs39WL?Q1YpobpJYE~g+GvnttbDYFkabA0UesHM6)ojz*paLq4YIbaSYvQoJCU`M zLDpMw*QI(E^dl?I<`^rkHmcU%WbIw7W8$nly9_7m@S@*itiyx04katjyf`Z#tr27$ zVUYDl3;S5@*Ja7NtU=aV%Q?GhU52d77-X##X93Rxvy*jpgRHlrGF98UJXx1F$XY9U zfL9r#$U4d(>#cb9T5an{vW_&!dTYO6l`)#EqYZM5l`G5yvQ99_dTamSXst}vl?`%? zm1mcw$-1;b)><)Mxd+COb&NsQT2TpjWc8?68%>Ym-}J2iePtLv*na9WZ|xt9fYr#lnn8}S@|-iIhLDH-1Ls?Mx?kaG`%+;&Uba`N8qNzOeDvfjz-z#}WpCUa!* zZ+gz^41n{feD;i5%`@O7WWB^7x82hJvX*lOxCZygw%TYHmrZ-cD2hF5O;kaZt}tha_w9(E$@P6kPS(v0vc9e1TJSWoo@S8s*6``$WU@{+$a-t|cH1FjJ;WgEt++O>j)ASoy0t;p zTL%_rZPj`-S&ufzdh5_&ydFc=V+?YPmD_qOS&ucydMkcOQtg3l$hwU|)>|>VR;`DU z^-zNxW94~ZXR_{WkoDHVLDjy1tQQz$owZyMbA3IKtOpuoowckmY~7!%`y1pKE1z2j zk@X;hJmc20$a#gAv=#9v_kwMm5!?$O*CF`~Z zS#KQ_%mas!^)Q1RW92#PaIzk5ko8ua1J&7OJF;$PkoDFw z*dWJPxd)CX>+uFzZ^gZ*YQLUH))NhKjFrd0E@a)sAnUDzgQsL0lXYW*theGCzS;vP zll5eSth1K2fa}&7WIe+m>#V2+;d^eUko6RU9AoA6Knq!0iZ3I?S$SlgO4d^ia!gzK zdizYWo@tO{tXvH*BqQ1R#>)MA5?N0&$U18|viK}GhpguqWSzC_ z0j{wVE7nFcvG_MVYg_jQcy76@;%qXP75}E^{I4tSvOjrTolVxW4RXx>bFW@b*2@jD z-WvXv;9{~~Y>@TV@NKM1$$F_l)?35(C1xS(EXA+g+iT@#g?zM{$=Ym?+t=3VSzjRQ z3kJD;R(}4*=fE3eeZwHfSox~pO0r&QklSl5pTLx70bB2_SR2i~#lPuU|Lc6kolHJf zx0CaBgWPsY+j4TB-cQc^4YJFti#USgQe2g9< z=R*cr@8tgclAK=}l_q3YyZ6BY%-r0|EA|`YaN02SgKbO zS5>Ty=Bnb~^sH^ocKn?2u8OnC+*SOWp0ll%hSxm_{dztr{u5&j-;++snkL9?AE~z5 ziZ_I+y_%7=NKsZetJXiMU;i}7F>Ph*4`ltpAnUE+XuX20R~TfyHT-46i)4M#AnUE+ zjCwU$uP#1y6l3ilJPo*(tk)W3eOvnnBkLNnUSp7BtUL=`N7m~MvfhdsT^+ACko5+G z9Ao9R)Qx1l(ICfIxmwLe?}yEnA#1Ge{V=XqFHu`xGRPWhdG{;)-oYzmeZ?T_t>OCy zFO&6UgB)Y!G4K^xzcR>r>(JoN+D&A=$sp^k;oD3%ll5kU9AoA2dJ9=^F~~7i9K;E>&be(LDpNt zH_)FU>oW#fZ^dY>&e|PlChU+QYpiZ2(JtU3Z5|G z3k^IIu1(gpQ{)&ckF0gbx=xB5W92Kjjmf%kimbPW-xXVztm~%8dTaO%oMy5%r^qo@ z9#gB<-IT1GrpS70`0o5`g=6Y)RUsCglUNeqfMetb7)HLe@_Va*UN%#h;S( zQ-iFx;x1uz&iah3pBdyBE6-UUlJ!G_tha`5;JruI_YAV$8vZWjZ?gVf{PlnsYxs8S ztz^B`AnV&2R_-6k`lCVCTftuc1AnUE+ch=q_>stnS#;tFX^-Y5uW97T$KUb`c z=I7$y^sLi9i^SD-_`Kv7a{gkF+ivN9JHzY3Un|Zg^K0>Mdd_K|8RC6$Q~^F)f2&v< z&2PoO=~>%qDLY5fove`=a@%`9ZA)?PWBta~abz8rA?vN-?{ilq>xvn2jFqdv3S?a& zL)Kfd|JC`rk*tjwvfdhg8Ff5a$7jefR=y@$iL5JS$T3!4w~Zp}s0=yA%Kh3z)}{zE&G+9Sy$T3!43yme~*bF(w%IDS?vX04+W2`*0E>G6wGi0q5?_YAiepEQ7u3SGd z$a*Wjcvn5Q-Y4t(206ycN9$vlX%D zZ^dE02M%$@;ZHjy*2zw^!H@_-XQC(;oZ#N$@;rN)>!du4_EL!XZ=Ige+;t5>Yl>!$oiM8 z{~F{NE1v~9y+xKM$a*XKp?b#F$XYA@_;s9>R~a*rb*2P4rmcJy%uLpq6XX~x_v<}{ zV`|;H#~|yi;a?)UldN|dWW6=~CFLJv{lg&Zt>G7~AFEg!&11#C=~<_}*1$XSI0LHV z>VFkyllfoqZ+g!EzSihhTyOB5yjf@j%#tAM_dopJsv)aMkoDH^%Mw3P5By}1_15sK z0>9DP?Y9hBYsK%$`ONx@tbb+5F;=dHzmxU%3^~Ti=W9|p-1*vwgH`0Vtvp&WO3U}> zc>g=l2JE}L2j?L`b|KtubYr{lLR@&%A>UxS$ieO?X|i)@3=R@M{5o`2j<9-HP-T-Bsi(SKKqZ4F}wY-xP-pj2gYkh*Ow}x*x46j%l&F}=dZ7ZLz<=^8%b>TB>C^?5F$a*Kw z0ZWr}=>%Etz#asxg0r{OOW+WKJLqt zbNK{W@8q@WNOF!$ko8U;ODm9bg#=ma3|DD;kaLd&c}AU$Md$k~)2 zYn|b@HCH6(iV3pb$>-5Ha*j)o^-kXRvE&?^AZwji@mBkDC33EmAZwlBI(j@g$0x{I zXLzTsft(Eqvfjz(=HV4*lQ}#=ZreHSy3Ex%wjM^-!xH2eE6+_wkoAZJImXH}$t1E) zN|5!|@NUY=WL-Hy)?33Tp^K1pkpx+94Oh|Ykae8|S#J%0<+UhT7fq1$*6^L?#mKr? zf*fPz_3jd6T_Qn_vGTQaZ?g7Iko8ua1J&7TNwO}PAnUE+eY!qm?UNwut>OFE{m9xc zL5{KV8M`=H7f+D&*6=R@tV`B)6J)(L{O#C!WL+;o)?35hjy03DIYExG^2pkgta~QN zdMmCds^fKivaX*X>#gB0d?t`}LV~QfhTqiePuBhka*UPxbpTlhB*-yVUi}Xw>%as# z#>zEz8L}>uAjepF46H`h)e__xw+RYpi9zhS!*LQ(NcGkTq7#gB0srRD0zg+O!tdmmY7%Nwp zy~(o~F= zmm+Je?u)f}I&6yHvUD&fkoANVS!+c*dF^rvSx-rkW2}4@oI%zzQe?d~ysLW}Sx-xm zW2{^a4b- zN!BeBWW6zEzSF-M!AjepFCOnR; z$0f*mYq)ybj;z}y$a-t|ZN#m}x>bU#w+;#3XFH0lMcO-x=GR zta~TOdTaQ_#f`|iQG%?uhQA-&jI5g_$TM!;l&qU3$T3!)2M!?X0SU6kit&#bi)+Dt z6>Fo}FF|hGI_=v`cm@@IS7m>4?w=s*oxB#9OwP#(vfjy6;6QR7m>}z&JOds?&Vv$U zy_4s^-73x|vs;4PwzI9b2ExC-HV@r%oF_xpSlvBG-v4>YI&X#?W91%LnXD^k$Qmo| zWa5q^Tbs$+oFVJ2;a?74i>zy9$T3zvPgf)BY8i5jmCt}x$ht~~tha{mh^|W3RWsxm zD_>=;N7nT+WW5#d;8*X9tWDOnGi1FLcXF%Nb;-JJhOD>t3+`a7PS({kWW6f;T_;1Bv-b80vesI@HBlW| zTa$I`1X*vzU#G8H_aW;(39{ZA{#gB?c{`AGhXh$~4ZlyYBUyJ$kYlVo zXPrRS6B1;-6@QnzdbBnt>*fiv-ilgSwQfPyEfVAyD?ioRovgbj$a-t|zUL}rT_r)* zTf^Uvj;UB1&6otaZR@maVf=0^ygxj+;%qX56Xdp?ZLQtHzi~T^tiuvyy*0cR*o&-t zCCGYf_!no6A?q;-vfdj08tp`~o|qu(t>H6*lgN5ff*fPzb>_)rJvl*+vGU606tbR@ zAjeqw?&w6aPE3&XRyeD3%b{dFG(nEB^4v1IVr?{|6Xdq7ZJn=toq1}-*#SBK2vFHqj@GlZreKTIe;(1hIR07 zYU$q@a@)@TJ_m4Dh-a2R$@*u89Ao9tx^_PG*RxG#?HpNS#hE&MnkOX9WNpro_15qS z$@*knKSz$S@+`0}S=Y^xW2}4*tVh=Ma^x5*uUro$>!CTa&WiPQ_?&PDvhI*0>#gAv zy_3j#QjV;%Vq}HS2hJqxnK|-|TTdqI$vLv#iaSo#s(lJsPsx$>)@fF5WIZuIG1AJb zq4UXlevYhZE2`G8;;NWe7(0@6#~fK>#hi^k<(cppvL2HoYpkxVe1~vzvTmLu>#gDM ziWZ?+dyx!TV=b#z_&2%NqPDJ;BWtW>4}{+x-;k^u=Exdrd9?85y3DOR{d6Bga^|a$i8!3vy(=HGG?X9kQ;IBWtYX zdLX=h*_y0d=g2c|J%_C4b($+~Th9AoAAwVryQK10@6-JHeabvtV7b~&=nTCOy?t=p4z`y5$kEh`Kkt)0la zQ;r;CS@+A4HP&)ug}+qUm8`qw$a-t|U7_8`x?7GMW99jE z53=r&BkQfbtrD0oAk&#j4MotPuH*XrIKgOx|t zUS!=XM~<=bmC^oW-9JaxTf^T#?M~L+b7Z|0Ev=qglgK(LM~<=b>@u0GlXGOf70zl~ z_aW;(IkMgwj;wvjx^IpgW92pB0c1TON7h@zdpW0(^|TyWV=dP%;XUoc$$EH>th1IC zhL6?}WIZBBjaI3;J#Zme zFU*lO*0QbPUqC;WtjFfadMoDa>KWUM)>*wWWR2CWv-sRPh}wEkj;yhkJrMp9aAUG= zoFi+j?r8CfXcMw-k|XP_;jDcySr5*UHC8uXxvhtg^^hD{V|B9&kJqEfdQ^_Avto7$ zujG#->v1`<#_D>2XP2YNdUTGgw}$VKZa~%za%7FwU2$?-k0N{o+HOtc};i%Sx?B3_15s6=ZnaCQI4#!y7`sYge_!k$&ocycW!ZoIg6}k z<;XEsUKO7~)-!VC7%Pw0i^+O%j;yh|e≀bIE#cj;yh|S(~kAllANzImXII>pZfa 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 literal 0 HcmV?d00001 From ff1176e6bb65b4d068ea169fb52179a5be2107b7 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 09:11:28 +0800 Subject: [PATCH 044/159] fix testcases --- testcases/main/HSSF/UserModel/TestDataValidation.cs | 4 ++-- testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs | 8 ++++---- testcases/main/HSSF/UserModel/TestOLE2Embeding.cs | 2 +- testcases/main/NPOI.TestCases.csproj | 1 + testcases/main/SS/UserModel/BaseTestWorkbook.cs | 2 +- testcases/main/SS/Util/TestDateFormatConverter.cs | 2 +- testcases/ooxml/NPOI.OOXML.TestCases.csproj | 1 - testcases/ooxml/XSSF/SXSSFITestDataProvider.cs | 2 +- testcases/ooxml/XSSF/Streaming/TestSXSSFWorkbook.cs | 2 +- testcases/ooxml/XSSF/UserModel/TestUnfixedBugs.cs | 2 +- testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs | 6 +++--- testcases/ooxml/XSSF/UserModel/TestXSSFCellStyle.cs | 6 +++--- testcases/ooxml/XSSF/UserModel/TestXSSFComment.cs | 2 +- testcases/ooxml/XSSF/XSSFTestDataSamples.cs | 6 +++--- 14 files changed, 23 insertions(+), 23 deletions(-) diff --git a/testcases/main/HSSF/UserModel/TestDataValidation.cs b/testcases/main/HSSF/UserModel/TestDataValidation.cs index 62b2c8f8d..428f97078 100644 --- a/testcases/main/HSSF/UserModel/TestDataValidation.cs +++ b/testcases/main/HSSF/UserModel/TestDataValidation.cs @@ -50,7 +50,7 @@ public void AssertDataValidation(IWorkbook wb) MemoryStream baos = new MemoryStream(22000); try { - wb.Write(baos); + wb.Write(baos, false); baos.Close(); } catch (IOException e) @@ -165,7 +165,7 @@ public void TestAddToExistingSheet() MemoryStream baos = new MemoryStream(); try { - wb.Write(baos); + wb.Write(baos, false); } catch (IOException e) { diff --git a/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs b/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs index 70100b06e..95f9e7eac 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs @@ -657,7 +657,7 @@ public void Bug47920() ClassID clsid1 = fs1.Root.StorageClsid; MemoryStream out1 = new MemoryStream(4096); - wb.Write(out1); + wb.Write(out1, false); byte[] bytes = out1.ToArray(); POIFSFileSystem fs2 = new POIFSFileSystem(new MemoryStream(bytes)); ClassID clsid2 = fs2.Root.StorageClsid; @@ -1173,7 +1173,7 @@ public void Bug54500() Assert.AreEqual("ASheet!A1", name.RefersToFormula); MemoryStream stream = new MemoryStream(); - wb.Write(stream); + wb.Write(stream, false); assertSheetOrder(wb, "Sheet1", "Sheet2", "Sheet3", "ASheet"); Assert.AreEqual("ASheet!A1", name.RefersToFormula); @@ -1184,7 +1184,7 @@ public void Bug54500() Assert.AreEqual("ASheet!A1", name.RefersToFormula); MemoryStream stream2 = new MemoryStream(); - wb.Write(stream2); + wb.Write(stream2, false); assertSheetOrder(wb, "Sheet1", "Sheet3", "ASheet"); Assert.AreEqual("ASheet!A1", name.RefersToFormula); @@ -1295,7 +1295,7 @@ public void TestRewriteFileBug58480() private void WriteAndCloseWorkbook(IWorkbook workbook, FileInfo file) { ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - workbook.Write(bytesOut); + workbook.Write(bytesOut, false); workbook.Close(); byte[] byteArray = bytesOut.ToByteArray(); diff --git a/testcases/main/HSSF/UserModel/TestOLE2Embeding.cs b/testcases/main/HSSF/UserModel/TestOLE2Embeding.cs index b779df846..58f4e8c9f 100644 --- a/testcases/main/HSSF/UserModel/TestOLE2Embeding.cs +++ b/testcases/main/HSSF/UserModel/TestOLE2Embeding.cs @@ -157,7 +157,7 @@ static POIFSFileSystem GetSampleXLS() sheet.CreateRow(5).CreateCell(2).SetCellValue("yo dawg i herd you like embeddet objekts, so we Put a ole in your ole so you can save a file while you save a file"); MemoryStream bos = new MemoryStream(); - wb.Write(bos); + wb.Write(bos, false); wb.Close(); POIFSFileSystem poifs = new POIFSFileSystem(new MemoryStream(bos.ToArray())); diff --git a/testcases/main/NPOI.TestCases.csproj b/testcases/main/NPOI.TestCases.csproj index d14f69665..3b6140662 100644 --- a/testcases/main/NPOI.TestCases.csproj +++ b/testcases/main/NPOI.TestCases.csproj @@ -245,6 +245,7 @@ + diff --git a/testcases/main/SS/UserModel/BaseTestWorkbook.cs b/testcases/main/SS/UserModel/BaseTestWorkbook.cs index f2584b788..85b9366df 100644 --- a/testcases/main/SS/UserModel/BaseTestWorkbook.cs +++ b/testcases/main/SS/UserModel/BaseTestWorkbook.cs @@ -847,7 +847,7 @@ public void Test58499() OutputStream os = new NullOutputStream(); try { - workbook.Write(os); + workbook.Write(os, false); } finally { diff --git a/testcases/main/SS/Util/TestDateFormatConverter.cs b/testcases/main/SS/Util/TestDateFormatConverter.cs index 911066d4f..d5696c7a9 100644 --- a/testcases/main/SS/Util/TestDateFormatConverter.cs +++ b/testcases/main/SS/Util/TestDateFormatConverter.cs @@ -122,7 +122,7 @@ private void OutputLocaleDataFormats(DateTime date, bool dates, bool times, int FileStream outputStream = new FileStream(filename, FileMode.Create); try { - workbook.Write(outputStream); + workbook.Write(outputStream, false); } finally { diff --git a/testcases/ooxml/NPOI.OOXML.TestCases.csproj b/testcases/ooxml/NPOI.OOXML.TestCases.csproj index 6f59cb820..8d383ebb0 100644 --- a/testcases/ooxml/NPOI.OOXML.TestCases.csproj +++ b/testcases/ooxml/NPOI.OOXML.TestCases.csproj @@ -228,7 +228,6 @@ - diff --git a/testcases/ooxml/XSSF/SXSSFITestDataProvider.cs b/testcases/ooxml/XSSF/SXSSFITestDataProvider.cs index a9c387f71..dbc5876ed 100644 --- a/testcases/ooxml/XSSF/SXSSFITestDataProvider.cs +++ b/testcases/ooxml/XSSF/SXSSFITestDataProvider.cs @@ -66,7 +66,7 @@ public IWorkbook WriteOutAndReadBack(IWorkbook wb) try { MemoryStream baos = new MemoryStream(8192); - wb.Write(baos); + wb.Write(baos, false); Stream is1 = new MemoryStream(baos.ToArray()); result = new XSSFWorkbook(is1); } diff --git a/testcases/ooxml/XSSF/Streaming/TestSXSSFWorkbook.cs b/testcases/ooxml/XSSF/Streaming/TestSXSSFWorkbook.cs index 9ac06a4a8..d0c37a37b 100644 --- a/testcases/ooxml/XSSF/Streaming/TestSXSSFWorkbook.cs +++ b/testcases/ooxml/XSSF/Streaming/TestSXSSFWorkbook.cs @@ -450,7 +450,7 @@ private static void saveTwice(IWorkbook wb) try { NullOutputStream out1 = new NullOutputStream(); - wb.Write(out1); + wb.Write(out1, false); out1.Close(); } catch (Exception e) diff --git a/testcases/ooxml/XSSF/UserModel/TestUnfixedBugs.cs b/testcases/ooxml/XSSF/UserModel/TestUnfixedBugs.cs index 1ebb57ba5..79e2c31db 100644 --- a/testcases/ooxml/XSSF/UserModel/TestUnfixedBugs.cs +++ b/testcases/ooxml/XSSF/UserModel/TestUnfixedBugs.cs @@ -247,7 +247,7 @@ public void TestBug55752() Stream stream = new FileStream("55752.xlsx", FileMode.Create, FileAccess.ReadWrite); try { - wb.Write(stream); + wb.Write(stream, false); } finally { diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs index 3d0506098..918fc02c6 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs @@ -1928,7 +1928,7 @@ private void saveAndReloadReport(IWorkbook wb, FileInfo outFile) } FileStream fileOutStream = new FileStream(outFile.FullName, FileMode.Open, FileAccess.ReadWrite); - wb.Write(fileOutStream); + wb.Write(fileOutStream, false); fileOutStream.Close(); //System.out.Println("File \""+outFile.Name+"\" has been saved successfully"); @@ -3195,7 +3195,7 @@ private void CreateXls() ICellStyle style = workbook.CreateCellStyle(); style.Rotation = ((short)-90); cell1.CellStyle = (style); - workbook.Write(fileOut); + workbook.Write(fileOut,false); fileOut.Close(); workbook.Close(); } @@ -3210,7 +3210,7 @@ private void CreateXlsx() ICellStyle style = workbook.CreateCellStyle(); style.Rotation = ((short)-90); cell1.CellStyle = (style); - workbook.Write(fileOut); + workbook.Write(fileOut, false); fileOut.Close(); workbook.Close(); } diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFCellStyle.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFCellStyle.cs index ade463252..82aa454a0 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFCellStyle.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFCellStyle.cs @@ -459,7 +459,7 @@ public void TestGetSetTopBorderColor() byte[] rgb = cellStyle.TopBorderXSSFColor.RGB; Assert.AreEqual(Color.Cyan.ToArgb(), Color.FromArgb(rgb[0], rgb[1], rgb[2]).ToArgb()); //another border was added to the styles table - Assert.AreEqual(num+1, stylesTable.GetBorders().Count); + Assert.AreEqual(num, stylesTable.GetBorders().Count); //passing null unsets the color cellStyle.SetTopBorderColor(null); @@ -500,7 +500,7 @@ public void TestGetSetLeftBorderColor() byte[] rgb = cellStyle.LeftBorderXSSFColor.RGB; Assert.AreEqual(Color.Cyan.ToArgb(), Color.FromArgb(rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF).ToArgb()); //another border was Added to the styles table - Assert.AreEqual(num+1, stylesTable.GetBorders().Count); + Assert.AreEqual(num, stylesTable.GetBorders().Count); //passing null unsets the color cellStyle.SetLeftBorderColor(null); @@ -541,7 +541,7 @@ public void TestGetSetRightBorderColor() byte[] rgb = cellStyle.RightBorderXSSFColor.RGB; Assert.AreEqual(Color.Cyan.ToArgb(), Color.FromArgb(rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF).ToArgb()); //another border was Added to the styles table - Assert.AreEqual(num+1, stylesTable.GetBorders().Count); + Assert.AreEqual(num, stylesTable.GetBorders().Count); //passing null unsets the color cellStyle.SetRightBorderColor(null); diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFComment.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFComment.cs index 5c437e1cf..1f87d9a6a 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFComment.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFComment.cs @@ -285,7 +285,7 @@ public void TestBug58175a() FileStream out1 = new FileStream("C:\\temp\\58175.xlsx", FileMode.CreateNew, FileAccess.ReadWrite); try { - wb.Write(out1); + wb.Write(out1, false); } finally { diff --git a/testcases/ooxml/XSSF/XSSFTestDataSamples.cs b/testcases/ooxml/XSSF/XSSFTestDataSamples.cs index 8acb10a9a..7c4fd81d8 100644 --- a/testcases/ooxml/XSSF/XSSFTestDataSamples.cs +++ b/testcases/ooxml/XSSF/XSSFTestDataSamples.cs @@ -69,7 +69,7 @@ public static IWorkbook WriteOutAndReadBack(IWorkbook wb) { Stopwatch sw = new Stopwatch(); sw.Start(); - wb.Write(baos); + wb.Write(baos, false); sw.Stop(); Debug.WriteLine("XSSFWorkbook write time: " + sw.ElapsedMilliseconds + "ms"); using (Stream is1 = new MemoryStream(baos.ToArray())) @@ -177,7 +177,7 @@ public static XSSFWorkbook WriteOutAndReadBack(XSSFWorkbook wb, String testName) FileStream out1 = file.Create(); try { - wb.Write(out1); + wb.Write(out1, false); } finally { @@ -196,7 +196,7 @@ public static XSSFWorkbook WriteOutAndReadBack(XSSFWorkbook wb, String testName) public static ByteArrayOutputStream WriteOut(R wb) where R : IWorkbook { ByteArrayOutputStream out1 = new ByteArrayOutputStream(8192); - wb.Write(out1); + wb.Write(out1, false); return out1; } From f1f86780ac3ef00cacb1231dc5f56b6151c0ca23 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 09:29:56 +0800 Subject: [PATCH 045/159] fix test cases --- .../main/SS/Formula/Eval/TestFormulaBugs.cs | 2 +- .../main/SS/UserModel/BaseTestWorkbook.cs | 42 +++++++------------ 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/testcases/main/SS/Formula/Eval/TestFormulaBugs.cs b/testcases/main/SS/Formula/Eval/TestFormulaBugs.cs index 40d32c9e1..31262899a 100644 --- a/testcases/main/SS/Formula/Eval/TestFormulaBugs.cs +++ b/testcases/main/SS/Formula/Eval/TestFormulaBugs.cs @@ -96,7 +96,7 @@ public void Test27405() try { FileStream fileOut = new FileStream("27405output.xls", FileMode.Create); - wb.Write(fileOut); + wb.Write(fileOut, false); fileOut.Close(); } catch (IOException e) diff --git a/testcases/main/SS/UserModel/BaseTestWorkbook.cs b/testcases/main/SS/UserModel/BaseTestWorkbook.cs index 85b9366df..5838dbd67 100644 --- a/testcases/main/SS/UserModel/BaseTestWorkbook.cs +++ b/testcases/main/SS/UserModel/BaseTestWorkbook.cs @@ -262,41 +262,27 @@ public void TestCreateSheetWithLongNames() IWorkbook wb1 = _testDataProvider.CreateWorkbook(); String sheetName1 = "My very long sheet name which is longer than 31 chars"; - String tRuncatedSheetName1 = sheetName1.Substring(0, 31); - ISheet sh1 = wb1.CreateSheet(sheetName1); - Assert.AreEqual(tRuncatedSheetName1, sh1.SheetName); - Assert.AreSame(sh1, wb1.GetSheet(tRuncatedSheetName1)); - // now via wb.SetSheetName - wb1.SetSheetName(0, sheetName1); - Assert.AreEqual(tRuncatedSheetName1, sh1.SheetName); - Assert.AreSame(sh1, wb1.GetSheet(tRuncatedSheetName1)); - - String sheetName2 = "My very long sheet name which is longer than 31 chars " + - "and sheetName2.Substring(0, 31) == sheetName1.Substring(0, 31)"; try { - ISheet sh2 = wb1.CreateSheet(sheetName2); + ISheet sh1 = wb1.CreateSheet(sheetName1); Assert.Fail("expected exception"); } - catch (ArgumentException e) + catch (ArgumentException ex) { - // expected during successful Test - Assert.AreEqual("The workbook already contains a sheet named 'My very long sheet name which is longer than 31 chars and sheetName2.Substring(0, 31) == sheetName1.Substring(0, 31)'", e.Message); + Assert.IsTrue(ex.Message.StartsWith("sheetName 'My very long sheet name which is longer than 31 chars' is invalid")); + } + try + { + wb1.CreateSheet("test"); + // now via wb.SetSheetName + wb1.SetSheetName(0, sheetName1); + Assert.Fail("expected exception"); + } + catch (ArgumentException ex) + { + Assert.IsTrue(ex.Message.StartsWith("sheetName 'My very long sheet name which is longer than 31 chars' is invalid")); } - - String sheetName3 = "POI allows creating sheets with names longer than 31 characters"; - String tRuncatedSheetName3 = sheetName3.Substring(0, 31); - ISheet sh3 = wb1.CreateSheet(sheetName3); - Assert.AreEqual(tRuncatedSheetName3, sh3.SheetName); - Assert.AreSame(sh3, wb1.GetSheet(tRuncatedSheetName3)); - - //serialize and read again - IWorkbook wb2 = _testDataProvider.WriteOutAndReadBack(wb1); wb1.Close(); - Assert.AreEqual(2, wb2.NumberOfSheets); - Assert.AreEqual(0, wb2.GetSheetIndex(tRuncatedSheetName1)); - Assert.AreEqual(1, wb2.GetSheetIndex(tRuncatedSheetName3)); - wb2.Close(); } [Test] From 33fd5f2f0e6dd57c3e91e1ddde4e12bd8a72579b Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 09:43:59 +0800 Subject: [PATCH 046/159] fix TestLongSheetNames --- testcases/main/HSSF/UserModel/TestHSSFSheet.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/testcases/main/HSSF/UserModel/TestHSSFSheet.cs b/testcases/main/HSSF/UserModel/TestHSSFSheet.cs index 856723e3c..916e7b135 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFSheet.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFSheet.cs @@ -1066,18 +1066,15 @@ public void TestLongSheetNames() HSSFWorkbook wb = new HSSFWorkbook(); String SAME_PREFIX = "A123456789B123456789C123456789"; // 30 chars - wb.CreateSheet(SAME_PREFIX + "Dxxxx"); try { wb.CreateSheet(SAME_PREFIX + "Dyyyy"); // identical up to the 32nd char - throw new AssertionException("Expected exception not thrown"); + Assert.Fail("Expected exception not thrown"); } catch (ArgumentException e) { - Assert.AreEqual("The workbook already contains a sheet named 'A123456789B123456789C123456789Dyyyy'", e.Message); + Assert.IsTrue(e.Message.StartsWith("sheetName 'A123456789B123456789C123456789Dyyyy' is invalid")); } - wb.CreateSheet(SAME_PREFIX + "Exxxx"); // OK - differs in the 31st char - wb.Close(); } /** From ff05d4ac60dc8dfaf827af560ed421102bdfe5d1 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 10:08:38 +0800 Subject: [PATCH 047/159] change ObjectExtensions namespace to NPOI.Util #700 --- main/Util/ObjectExtensions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main/Util/ObjectExtensions.cs b/main/Util/ObjectExtensions.cs index 3dc79e039..b732237de 100644 --- a/main/Util/ObjectExtensions.cs +++ b/main/Util/ObjectExtensions.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; using System.Reflection; -using System.ArrayExtensions; +using System; +using NPOI.Util.ArrayExtensions; -namespace System +namespace NPOI.Util { public static class ObjectExtensions { From ef52c15df6cd99f3753045966d1e48ca9d37bad9 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 10:09:27 +0800 Subject: [PATCH 048/159] Add RecyclableMemoryStream 1.4.1 to .NET FX version in order to support .NET FX 4.5 --- main/NPOI.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main/NPOI.csproj b/main/NPOI.csproj index 4e93a6da9..809114adf 100644 --- a/main/NPOI.csproj +++ b/main/NPOI.csproj @@ -1358,6 +1358,9 @@ + + 1.4.1 + 1.8.9 From 1ff666acfe162221792ef66c380e4f41d8472177 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 10:26:31 +0800 Subject: [PATCH 049/159] upgrade version to 2.5.6 --- OpenXmlFormats/Properties/AssemblyInfo.cs | 4 ++-- main/Properties/AssemblyInfo.cs | 4 ++-- ooxml/Properties/AssemblyInfo.cs | 4 ++-- openxml4Net/Properties/AssemblyInfo.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/OpenXmlFormats/Properties/AssemblyInfo.cs b/OpenXmlFormats/Properties/AssemblyInfo.cs index 790ca743f..0fdba96df 100644 --- a/OpenXmlFormats/Properties/AssemblyInfo.cs +++ b/OpenXmlFormats/Properties/AssemblyInfo.cs @@ -33,8 +33,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.5.5.0")] -[assembly: AssemblyFileVersion("2.5.5.0")] +[assembly: AssemblyVersion("2.5.6.0")] +[assembly: AssemblyFileVersion("2.5.6.0")] [assembly: AssemblyInformationalVersion("2.0.0.0")] #if NETSTANDARD2_1 || NETSTANDARD2_0 || NET40 [assembly: AllowPartiallyTrustedCallers] diff --git a/main/Properties/AssemblyInfo.cs b/main/Properties/AssemblyInfo.cs index 203e9bc0c..f4ea20ca3 100644 --- a/main/Properties/AssemblyInfo.cs +++ b/main/Properties/AssemblyInfo.cs @@ -33,8 +33,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.5.5.0")] -[assembly: AssemblyFileVersion("2.5.5.0")] +[assembly: AssemblyVersion("2.5.6.0")] +[assembly: AssemblyFileVersion("2.5.6.0")] [assembly: AssemblyInformationalVersion("2.0.0.0")] [assembly: InternalsVisibleTo("NPOI.TestCases, PublicKey=002400000480000094000000060200000024000052534131000400000100010095ccd95af3b39d8bc20544d3f47fd24b53ebc5ccb693eaed116290629f8cd882c827ebd511ad59449224f0718d3f9d03b64945a6c8b6644266001b8c8426185330e3d96da70ae16d4acc21b8d4d480f1385c7e924273179375aa88f81380a72fb115712a313379d16aed4aa36208ee3b4a5dd785b06a07b2d868e3227f4495b5", AllInternalsVisible = true)] diff --git a/ooxml/Properties/AssemblyInfo.cs b/ooxml/Properties/AssemblyInfo.cs index 555bd2c07..80cb2551b 100644 --- a/ooxml/Properties/AssemblyInfo.cs +++ b/ooxml/Properties/AssemblyInfo.cs @@ -33,8 +33,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.5.5.0")] -[assembly: AssemblyFileVersion("2.5.5.0")] +[assembly: AssemblyVersion("2.5.6.0")] +[assembly: AssemblyFileVersion("2.5.6.0")] [assembly: AssemblyInformationalVersion("2.0.0.0")] //[assembly: InternalsVisibleTo("ooxml.Testcases")] diff --git a/openxml4Net/Properties/AssemblyInfo.cs b/openxml4Net/Properties/AssemblyInfo.cs index 429c9a8a4..393b36675 100644 --- a/openxml4Net/Properties/AssemblyInfo.cs +++ b/openxml4Net/Properties/AssemblyInfo.cs @@ -33,8 +33,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.5.5.0")] -[assembly: AssemblyFileVersion("2.5.5.0")] +[assembly: AssemblyVersion("2.5.6.0")] +[assembly: AssemblyFileVersion("2.5.6.0")] [assembly: AssemblyInformationalVersion("2.0.0.0")] #if NETSTANDARD2_1 || NETSTANDARD2_0 || NET40 [assembly: AllowPartiallyTrustedCallers] From 02dcc6b3140c99f9596f784217d006e6e6e20618 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 11:02:05 +0800 Subject: [PATCH 050/159] fix pic drawing serialization in groupshape fix #724 --- OpenXmlFormats/Drawing/ShapeProperties.cs | 8 ++--- OpenXmlFormats/Drawing/spreadsheetShape.cs | 34 +++++++++++++++++----- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/OpenXmlFormats/Drawing/ShapeProperties.cs b/OpenXmlFormats/Drawing/ShapeProperties.cs index a5387ed27..cddef953d 100644 --- a/OpenXmlFormats/Drawing/ShapeProperties.cs +++ b/OpenXmlFormats/Drawing/ShapeProperties.cs @@ -1192,13 +1192,13 @@ public static CT_NonVisualGroupDrawingShapeProps Parse(XmlNode node, XmlNamespac internal void Write(StreamWriter sw, string nodeName) { - sw.Write(string.Format(""); if (this.grpSpLocks != null) this.grpSpLocks.Write(sw, "grpSpLocks"); if (this.extLst != null) this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + sw.Write(string.Format("", nodeName)); } public CT_NonVisualGroupDrawingShapeProps() @@ -2198,7 +2198,7 @@ public static CT_GroupShapeProperties Parse(XmlNode node, XmlNamespaceManager na internal void Write(StreamWriter sw, string nodeName) { - sw.Write(string.Format(""); @@ -2224,7 +2224,7 @@ internal void Write(StreamWriter sw, string nodeName) this.scene3d.Write(sw, "scene3d"); if (this.extLst != null) this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + sw.Write(string.Format("", nodeName)); } public CT_GroupShapeProperties() diff --git a/OpenXmlFormats/Drawing/spreadsheetShape.cs b/OpenXmlFormats/Drawing/spreadsheetShape.cs index 7636aa2a8..97aa66c86 100644 --- a/OpenXmlFormats/Drawing/spreadsheetShape.cs +++ b/OpenXmlFormats/Drawing/spreadsheetShape.cs @@ -596,9 +596,14 @@ public class CT_GroupShape CT_GroupShapeProperties grpSpPrField; CT_GroupShapeNonVisual nvGrpSpPrField; CT_Connector connectorField = null; - CT_Picture pictureField = null; + List pictures = null; CT_Shape shapeField = null; + public CT_GroupShape() + { + this.pictures = new List(); + } + public void Set(CT_GroupShape groupShape) { this.grpSpPrField = groupShape.grpSpPr; @@ -627,8 +632,9 @@ public CT_Shape AddNewSp() } public CT_Picture AddNewPic() { - pictureField = new CT_Picture(); - return pictureField; + var pic=new CT_Picture(); + pictures.Add(pic); + return pic; } public CT_GroupShapeNonVisual nvGrpSpPr @@ -653,6 +659,11 @@ public static CT_GroupShape Parse(XmlNode node, XmlNamespaceManager namespaceMan ctObj.nvGrpSpPr = CT_GroupShapeNonVisual.Parse(childNode, namespaceManager); else if (childNode.LocalName == "grpSpPr") ctObj.grpSpPr = CT_GroupShapeProperties.Parse(childNode, namespaceManager); + else if (childNode.LocalName == "pic") + { + var pic = CT_Picture.Parse(childNode, namespaceManager); + ctObj.pictures.Add(pic); + } } return ctObj; } @@ -664,9 +675,16 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format(""); if (this.nvGrpSpPr != null) - this.nvGrpSpPr.Write(sw, "nvGrpSpPr"); + this.nvGrpSpPr.Write(sw, "xdr:nvGrpSpPr"); if (this.grpSpPr != null) - this.grpSpPr.Write(sw, "grpSpPr"); + this.grpSpPr.Write(sw, "xdr:grpSpPr"); + if (this.pictures.Count > 0) + { + foreach (var pic in this.pictures) + { + pic.Write(sw, "pic"); + } + } sw.Write(string.Format("", nodeName)); } @@ -697,13 +715,13 @@ public static CT_GroupShapeNonVisual Parse(XmlNode node, XmlNamespaceManager nam internal void Write(StreamWriter sw, string nodeName) { - sw.Write(string.Format(""); if (this.cNvPr != null) this.cNvPr.Write(sw, "cNvPr"); if (this.cNvGrpSpPr != null) - this.cNvGrpSpPr.Write(sw, "cNvGrpSpPr"); - sw.Write(string.Format("", nodeName)); + this.cNvGrpSpPr.Write(sw, "xdr:cNvGrpSpPr"); + sw.Write(string.Format("", nodeName)); } public CT_NonVisualGroupDrawingShapeProps AddNewCNvGrpSpPr() From 6092217115242a04c9a846eb176b23903d0f286d Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 11:32:52 +0800 Subject: [PATCH 051/159] fix shape lost #746 --- OpenXmlFormats/Drawing/ShapeGeometry.cs | 11 ++++-- OpenXmlFormats/Drawing/ShapeProperties.cs | 41 +++++++++++++------- OpenXmlFormats/Drawing/SpreadsheetDrawing.cs | 3 +- OpenXmlFormats/Drawing/spreadsheetShape.cs | 18 ++++++++- 4 files changed, 52 insertions(+), 21 deletions(-) diff --git a/OpenXmlFormats/Drawing/ShapeGeometry.cs b/OpenXmlFormats/Drawing/ShapeGeometry.cs index eae05e7fa..29241fbba 100644 --- a/OpenXmlFormats/Drawing/ShapeGeometry.cs +++ b/OpenXmlFormats/Drawing/ShapeGeometry.cs @@ -928,15 +928,20 @@ internal static CT_GeomGuideList Parse(XmlNode node, XmlNamespaceManager namespa internal void Write(StreamWriter sw, string nodeName) { - sw.Write("", nodeName); - if (this.gdField != null) + sw.Write(""); + } + else { + sw.Write(">"); foreach (CT_GeomGuide gg in gdField) { gg.Write(sw, "gd"); } + sw.Write("", nodeName); } - sw.Write("", nodeName); } } diff --git a/OpenXmlFormats/Drawing/ShapeProperties.cs b/OpenXmlFormats/Drawing/ShapeProperties.cs index cddef953d..bceb18202 100644 --- a/OpenXmlFormats/Drawing/ShapeProperties.cs +++ b/OpenXmlFormats/Drawing/ShapeProperties.cs @@ -1377,10 +1377,17 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "noChangeArrowheads", this.noChangeArrowheads, false); XmlHelper.WriteAttribute(sw, "noChangeShapeType", this.noChangeShapeType, false); XmlHelper.WriteAttribute(sw, "noTextEdit", this.noTextEdit, false); - sw.Write(">"); - if(this.extLst!=null) + + if (this.extLst == null) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + sw.Write(string.Format("", nodeName)); + } } public CT_ShapeLocking() { @@ -1626,17 +1633,23 @@ public static CT_GroupLocking Parse(XmlNode node, XmlNamespaceManager namespaceM internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); - if (this.extLst != null) - this.extLst.Write(sw, "extLst"); - sw.Write(string.Format("", nodeName)); + if(this.noGrp) + XmlHelper.WriteAttribute(sw, "noGrp", this.noGrp); + if(this.noUngrp) + XmlHelper.WriteAttribute(sw, "noUngrp", this.noUngrp); + if(this.noSelect) + XmlHelper.WriteAttribute(sw, "noSelect", this.noSelect); + if(this.noRot) + XmlHelper.WriteAttribute(sw, "noRot", this.noRot); + if(this.noChangeAspect) + XmlHelper.WriteAttribute(sw, "noChangeAspect", this.noChangeAspect); + if(this.noMove) + XmlHelper.WriteAttribute(sw, "noMove", this.noMove); + if(this.noResize) + XmlHelper.WriteAttribute(sw, "noResize", this.noResize); + sw.Write("/>"); + /*if (this.extLst != null) + this.extLst.Write(sw, "extLst");*/ } [XmlElement(Order = 0)] diff --git a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs index d6b6ac89b..b9f6cc908 100644 --- a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs +++ b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs @@ -1270,8 +1270,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); } bool _fLocksWithSheet; bool _fPrintsWithSheet; diff --git a/OpenXmlFormats/Drawing/spreadsheetShape.cs b/OpenXmlFormats/Drawing/spreadsheetShape.cs index 97aa66c86..232e54b68 100644 --- a/OpenXmlFormats/Drawing/spreadsheetShape.cs +++ b/OpenXmlFormats/Drawing/spreadsheetShape.cs @@ -597,11 +597,12 @@ public class CT_GroupShape CT_GroupShapeNonVisual nvGrpSpPrField; CT_Connector connectorField = null; List pictures = null; - CT_Shape shapeField = null; + List shapes = null; public CT_GroupShape() { this.pictures = new List(); + this.shapes = new List(); } public void Set(CT_GroupShape groupShape) @@ -627,7 +628,8 @@ public CT_Connector AddNewCxnSp() } public CT_Shape AddNewSp() { - shapeField = new CT_Shape(); + var shapeField = new CT_Shape(); + shapes.Add(shapeField); return shapeField; } public CT_Picture AddNewPic() @@ -664,6 +666,11 @@ public static CT_GroupShape Parse(XmlNode node, XmlNamespaceManager namespaceMan var pic = CT_Picture.Parse(childNode, namespaceManager); ctObj.pictures.Add(pic); } + else if (childNode.LocalName == "sp") + { + var shape = CT_Shape.Parse(childNode, namespaceManager); + ctObj.shapes.Add(shape); + } } return ctObj; } @@ -678,6 +685,13 @@ internal void Write(StreamWriter sw, string nodeName) this.nvGrpSpPr.Write(sw, "xdr:nvGrpSpPr"); if (this.grpSpPr != null) this.grpSpPr.Write(sw, "xdr:grpSpPr"); + if (this.shapes.Count > 0) + { + foreach (var shape in this.shapes) + { + shape.Write(sw, "sp"); + } + } if (this.pictures.Count > 0) { foreach (var pic in this.pictures) From c2f8f809c6cc6f22b3bcf5b220b2a29b2eb03471 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 13 Feb 2022 14:20:58 +0800 Subject: [PATCH 052/159] remove preserveSpaces call --- ooxml/XSSF/UserModel/XSSFRichTextString.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ooxml/XSSF/UserModel/XSSFRichTextString.cs b/ooxml/XSSF/UserModel/XSSFRichTextString.cs index 919d5402d..fa999adce 100644 --- a/ooxml/XSSF/UserModel/XSSFRichTextString.cs +++ b/ooxml/XSSF/UserModel/XSSFRichTextString.cs @@ -79,7 +79,7 @@ public XSSFRichTextString(String str) { st = new CT_Rst(); st.t = str; - PreserveSpaces(st.t); + //PreserveSpaces(st.t); } @@ -279,12 +279,12 @@ public void Append(String text, XSSFFont font) //convert string into a text Run: string CT_RElt lt = st.AddNewR(); lt.t = st.t; - PreserveSpaces(lt.t); + //PreserveSpaces(lt.t); st.unsetT(); } CT_RElt lt2 = st.AddNewR(); lt2.t= (text); - PreserveSpaces(lt2.t); + //PreserveSpaces(lt2.t); if (font != null) { @@ -441,7 +441,7 @@ public String String { ClearFormatting(); st.t = value; - PreserveSpaces(st.t); + //PreserveSpaces(st.t); } } @@ -692,7 +692,7 @@ CT_Rst BuildCTRst(String text, SortedDictionary formats) CT_RElt run = st.AddNewR(); String fragment = text.Substring(runStartIdx, runEndIdx - runStartIdx); run.t = (fragment); - PreserveSpaces(run.t); + //PreserveSpaces(run.t); CT_RPrElt fmt = kv.Value; if (fmt != null) { From 66f88cae88337d1f3d641ea4f8c24f9b97383b95 Mon Sep 17 00:00:00 2001 From: Toni Qu Date: Mon, 14 Feb 2022 19:31:40 +0800 Subject: [PATCH 053/159] migrate a few code changes from poi 4.0 --- OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs | 5 +- main/SS/Formula/EvaluationWorkbook.cs | 2 + main/SS/Formula/FormulaType.cs | 30 +++- main/SS/Formula/OperationEvaluationContext.cs | 16 ++ main/SS/Formula/WorkbookEvaluator.cs | 100 ++++++++++++- .../SS/UserModel/DifferentialStyleProvider.cs | 15 ++ main/SS/UserModel/ExcelNumberFormat.cs | 55 +++++++ main/SS/UserModel/TableStyle.cs | 14 ++ main/SS/UserModel/TableStyleInfo.cs | 33 +++++ main/SS/UserModel/TableStyleType.cs | 52 +++++++ main/SS/Util/CellRangeAddressBase.cs | 4 + .../XSSF/UserModel/DefaultIndexedColorMap.cs | 22 +++ .../XSSF/UserModel/Helpers/XSSFXmlColumnPr.cs | 56 ++++--- ooxml/XSSF/UserModel/IndexedColorMap.cs | 11 ++ ooxml/XSSF/UserModel/XSSFTable.cs | 137 ++++++++++++++---- ooxml/XSSF/UserModel/XSSFTableColumn.cs | 58 ++++++++ ooxml/XSSF/UserModel/XSSFTableStyleInfo.cs | 81 +++++++++++ 17 files changed, 628 insertions(+), 63 deletions(-) create mode 100644 main/SS/UserModel/DifferentialStyleProvider.cs create mode 100644 main/SS/UserModel/ExcelNumberFormat.cs create mode 100644 main/SS/UserModel/TableStyle.cs create mode 100644 main/SS/UserModel/TableStyleInfo.cs create mode 100644 main/SS/UserModel/TableStyleType.cs create mode 100644 ooxml/XSSF/UserModel/DefaultIndexedColorMap.cs create mode 100644 ooxml/XSSF/UserModel/IndexedColorMap.cs create mode 100644 ooxml/XSSF/UserModel/XSSFTableColumn.cs create mode 100644 ooxml/XSSF/UserModel/XSSFTableStyleInfo.cs diff --git a/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs b/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs index 16fbe947d..617a57aec 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs @@ -674,7 +674,10 @@ internal void Write(StreamWriter sw, string nodeName) } sw.Write(string.Format("", nodeName)); } - + public void RemoveTableColumn(int columnIndex) + { + this.tableColumn.RemoveAt(columnIndex); + } [XmlElement] public List tableColumn { diff --git a/main/SS/Formula/EvaluationWorkbook.cs b/main/SS/Formula/EvaluationWorkbook.cs index 157938aa3..08bb4e833 100644 --- a/main/SS/Formula/EvaluationWorkbook.cs +++ b/main/SS/Formula/EvaluationWorkbook.cs @@ -128,6 +128,8 @@ public interface IEvaluationWorkbook * @see WorkbookEvaluator#clearAllCachedResultValues() */ void ClearAllCachedResultValues(); + + SpreadsheetVersion GetSpreadsheetVersion(); } public class ExternalName diff --git a/main/SS/Formula/FormulaType.cs b/main/SS/Formula/FormulaType.cs index b6f99491d..eccd2bc7f 100644 --- a/main/SS/Formula/FormulaType.cs +++ b/main/SS/Formula/FormulaType.cs @@ -15,25 +15,41 @@ limitations under the License. ==================================================================== */ +using System; + namespace NPOI.SS.Formula { + internal class SingleValueAttribute : Attribute + { + private bool _isSingleValue=false; + public SingleValueAttribute(bool isSingleValue) + { + this._isSingleValue = isSingleValue; + } + public bool IsSingleValue + { + get { return _isSingleValue; } + } + } - /** - * Enumeration of various formula types.
- * - * For POI internal use only - * - * @author Josh Micich - */ + /// + /// Enumeration of various formula types. For internal use only + /// public enum FormulaType:int { + [SingleValue(true)] Cell = 0, + [SingleValue(true)] Shared = 1, + [SingleValue(false)] Array = 2, + [SingleValue(true)] CondFormat = 3, + [SingleValue(false)] NamedRange = 4, // this constant is currently very specific. The exact differences from general data // validation formulas or conditional format formulas is not known yet + [SingleValue(false)] DataValidationList = 5, } } \ No newline at end of file diff --git a/main/SS/Formula/OperationEvaluationContext.cs b/main/SS/Formula/OperationEvaluationContext.cs index 1d6f7b665..06131d73e 100644 --- a/main/SS/Formula/OperationEvaluationContext.cs +++ b/main/SS/Formula/OperationEvaluationContext.cs @@ -25,6 +25,7 @@ public class OperationEvaluationContext private int _rowIndex; private int _columnIndex; private EvaluationTracker _tracker; + private bool _isSingleValue; private WorkbookEvaluator _bookEvaluator; public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, IEvaluationWorkbook workbook, int sheetIndex, int srcRowNum, @@ -37,12 +38,27 @@ public class OperationEvaluationContext _columnIndex = srcColNum; _tracker = tracker; } + public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, IEvaluationWorkbook workbook, int sheetIndex, int srcRowNum, + int srcColNum, EvaluationTracker tracker, bool isSingleValue) + { + _bookEvaluator = bookEvaluator; + _workbook = workbook; + _sheetIndex = sheetIndex; + _rowIndex = srcRowNum; + _columnIndex = srcColNum; + _tracker = tracker; + _isSingleValue = isSingleValue; + } public IEvaluationWorkbook GetWorkbook() { return _workbook; } + public bool IsSingleValue() + { + return _isSingleValue; + } public int RowIndex { get diff --git a/main/SS/Formula/WorkbookEvaluator.cs b/main/SS/Formula/WorkbookEvaluator.cs index 56f262d8f..f0eb7e97e 100644 --- a/main/SS/Formula/WorkbookEvaluator.cs +++ b/main/SS/Formula/WorkbookEvaluator.cs @@ -32,6 +32,7 @@ namespace NPOI.SS.Formula using NPOI.SS.Formula.PTG; using NPOI.Util; using NPOI.SS.Formula.Function; + using EnumsNET; /** * Evaluates formula cells.

@@ -258,12 +259,103 @@ public ValueEval Evaluate(IEvaluationCell srcCell) int sheetIndex = GetSheetIndex(srcCell.Sheet); return EvaluateAny(srcCell, sheetIndex, srcCell.RowIndex, srcCell.ColumnIndex, new EvaluationTracker(_cache)); } + /** + * Evaluate a formula outside a cell value, e.g. conditional format rules or data validation expressions + * + * @param formula to evaluate + * @param ref defines the optional sheet and row/column base for the formula, if it is relative + * @return value + */ + public ValueEval Evaluate(String formula, CellReference reference) + { + String sheetName = reference == null ? null : reference.SheetName; + int sheetIndex; + if (sheetName == null) + { + sheetIndex = -1; // workbook scope only + } + else + { + sheetIndex = Workbook.GetSheetIndex(sheetName); + } + int rowIndex = reference == null ? -1 : reference.Row; + int colIndex = reference == null ? -1 : reference.Col; + OperationEvaluationContext ec = new OperationEvaluationContext( + this, + Workbook, + sheetIndex, + rowIndex, + colIndex, + new EvaluationTracker(_cache) + ); + Ptg[] ptgs = FormulaParser.Parse(formula, (IFormulaParsingWorkbook)Workbook, FormulaType.Cell, sheetIndex, rowIndex); + return EvaluateNameFormula(ptgs, ec); + } + public ValueEval Evaluate(String formula, CellReference target, CellRangeAddressBase region) + { + return Evaluate(formula, target, region, FormulaType.Cell); + } + private ValueEval Evaluate(String formula, CellReference target, CellRangeAddressBase region, FormulaType formulaType) + { + String sheetName = target == null ? null : target.SheetName; + if (sheetName == null) throw new ArgumentException("Sheet name is required"); + int sheetIndex = Workbook.GetSheetIndex(sheetName); + Ptg[] ptgs = FormulaParser.Parse(formula, (IFormulaParsingWorkbook)Workbook, formulaType, sheetIndex, target.Row); - /** - * @return never null, never {@link BlankEval} - */ - private ValueEval EvaluateAny(IEvaluationCell srcCell, int sheetIndex, + AdjustRegionRelativeReference(ptgs, target, region); + + OperationEvaluationContext ec = new OperationEvaluationContext(this, Workbook, sheetIndex, target.Row, target.Col, new EvaluationTracker(_cache),formulaType.GetAttributes().Get().IsSingleValue); + return EvaluateNameFormula(ptgs, ec); + } + protected bool AdjustRegionRelativeReference(Ptg[] ptgs, CellReference target, CellRangeAddressBase region) + { + if (!region.IsInRange(target)) + { + throw new ArgumentException(target + " is not within " + region); + } + + //return adjustRegionRelativeReference(ptgs, target.getRow() - region.getFirstRow(), target.getCol() - region.getFirstColumn()); + + int deltaRow = target.Row; + int deltaColumn = target.Col; + + bool shifted = false; + foreach(Ptg ptg in ptgs) + { + // base class for cell reference "things" + if (ptg is RefPtgBase) { + RefPtgBase reference = (RefPtgBase)ptg; + // re-calculate cell references + SpreadsheetVersion version = _workbook.GetSpreadsheetVersion(); + if (reference.IsRowRelative) + { + int rowIndex = reference.Row + deltaRow; + if (rowIndex > version.MaxRows) + { + throw new IndexOutOfRangeException(version.Name + " files can only have " + version.MaxRows + " rows, but row " + rowIndex + " was requested."); + } + reference.Row = rowIndex; + shifted = true; + } + if (reference.IsColRelative) + { + int colIndex = reference.Column + deltaColumn; + if (colIndex > version.MaxColumns) + { + throw new IndexOutOfRangeException(version.Name + " files can only have " + version.MaxColumns + " columns, but column " + colIndex + " was requested."); + } + reference.Column = colIndex; + shifted = true; + } + } + } + return shifted; + } + /** + * @return never null, never {@link BlankEval} + */ + private ValueEval EvaluateAny(IEvaluationCell srcCell, int sheetIndex, int rowIndex, int columnIndex, EvaluationTracker tracker) { bool shouldCellDependencyBeRecorded = _stabilityClassifier == null ? true diff --git a/main/SS/UserModel/DifferentialStyleProvider.cs b/main/SS/UserModel/DifferentialStyleProvider.cs new file mode 100644 index 000000000..a05fc63c9 --- /dev/null +++ b/main/SS/UserModel/DifferentialStyleProvider.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.UserModel +{ + public interface DifferentialStyleProvider + { + IBorderFormatting BorderFormatting { get; } + IFontFormatting FontFormatting { get; } + IPatternFormatting PatternFormatting { get; } + ExcelNumberFormat NumberFormat { get; } + int StripeSize { get; } + } +} diff --git a/main/SS/UserModel/ExcelNumberFormat.cs b/main/SS/UserModel/ExcelNumberFormat.cs new file mode 100644 index 000000000..497081930 --- /dev/null +++ b/main/SS/UserModel/ExcelNumberFormat.cs @@ -0,0 +1,55 @@ +using NPOI.SS.Formula; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.UserModel +{ + public class ExcelNumberFormat + { + private int idx; + private String format; + public ExcelNumberFormat(int idx, String format) + { + this.idx = idx; + this.format = format; + } + public static ExcelNumberFormat From(ICellStyle style) + { + if (style == null) return null; + return new ExcelNumberFormat(style.DataFormat, style.GetDataFormatString()); + } + + public static ExcelNumberFormat From(ICell cell, ConditionalFormattingEvaluator cfEvaluator) + { + if (cell == null) return null; + + ExcelNumberFormat nf = null; + + if (cfEvaluator != null) + { + // first one wins (priority order, per Excel help) + List rules = cfEvaluator.GetConditionalFormattingForCell(cell); + foreach (EvaluationConditionalFormatRule rule in rules) + { + nf = rule.NumberFormat; + if (nf != null) break; + } + } + if (nf == null) + { + ICellStyle style = cell.CellStyle; + nf = ExcelNumberFormat.From(style); + } + return nf; + } + public int Idx + { + get { return idx; } + } + public String Format + { + get { return format; } + } + } +} diff --git a/main/SS/UserModel/TableStyle.cs b/main/SS/UserModel/TableStyle.cs new file mode 100644 index 000000000..fbd8ab751 --- /dev/null +++ b/main/SS/UserModel/TableStyle.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.UserModel +{ + public interface ITableStyle + { + string Name { get; } + int Index { get; } + bool IsBuiltin { get; } + DifferentialStyleProvider GetStyle(TableStyleType type); + } +} diff --git a/main/SS/UserModel/TableStyleInfo.cs b/main/SS/UserModel/TableStyleInfo.cs new file mode 100644 index 000000000..33b9f41bc --- /dev/null +++ b/main/SS/UserModel/TableStyleInfo.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.UserModel +{ + public interface ITableStyleInfo + { + bool IsShowColumnStripes { get; set; } + ///

+ /// return true if alternating row styles should be applied + /// + bool IsShowRowStripes { get; set; } + /// + /// return true if the distinct first column style should be applied + /// + bool IsShowFirstColumn { get; set; } + + /// + /// return true if the distinct last column style should be applied + /// + bool IsShowLastColumn { get; set; } + /// + /// return the name of the style (may reference a built-in style) + /// + String Name { get; set; } + + /// + /// style definition + /// + ITableStyle Style { get; } + } +} diff --git a/main/SS/UserModel/TableStyleType.cs b/main/SS/UserModel/TableStyleType.cs new file mode 100644 index 000000000..acd1e22b0 --- /dev/null +++ b/main/SS/UserModel/TableStyleType.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.UserModel +{ + public enum TableStyleType + { + wholeTable, + pageFieldLabels,// pivot only + pageFieldValues,// pivot only + firstColumnStripe, + secondColumnStripe, + firstRowStripe, + secondRowStripe, + lastColumn, + firstColumn, + headerRow, + totalRow, + firstHeaderCell, + lastHeaderCell, + firstTotalCell, + lastTotalCell, + /* these are for pivot tables only */ + /***/ + firstSubtotalColumn, + /***/ + secondSubtotalColumn, + /***/ + thirdSubtotalColumn, + /***/ + blankRow, + /***/ + firstSubtotalRow, + /***/ + secondSubtotalRow, + /***/ + thirdSubtotalRow, + /***/ + firstColumnSubheading, + /***/ + secondColumnSubheading, + /***/ + thirdColumnSubheading, + /***/ + firstRowSubheading, + /***/ + secondRowSubheading, + /***/ + thirdRowSubheading + } +} diff --git a/main/SS/Util/CellRangeAddressBase.cs b/main/SS/Util/CellRangeAddressBase.cs index 71a91daa7..79ccd4587 100644 --- a/main/SS/Util/CellRangeAddressBase.cs +++ b/main/SS/Util/CellRangeAddressBase.cs @@ -96,6 +96,10 @@ public bool IsInRange(int rowInd, int colInd) return _firstRow <= rowInd && rowInd <= _lastRow && //containsRow _firstCol <= colInd && colInd <= _lastCol; //containsColumn } + public bool IsInRange(CellReference reference) + { + return IsInRange(reference.Row, reference.Col); + } public bool IsFullColumnRange { get diff --git a/ooxml/XSSF/UserModel/DefaultIndexedColorMap.cs b/ooxml/XSSF/UserModel/DefaultIndexedColorMap.cs new file mode 100644 index 000000000..6fca78557 --- /dev/null +++ b/ooxml/XSSF/UserModel/DefaultIndexedColorMap.cs @@ -0,0 +1,22 @@ +using NPOI.HSSF.Util; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.OOXML.XSSF.UserModel +{ + public class DefaultIndexedColorMap:IIndexedColorMap + { + public byte[] GetRGB(int index) + { + return GetDefaultRGB(index); + } + public static byte[] GetDefaultRGB(int index) + { + HSSFColor hssfColor = HSSFColor.GetIndexHash()[index]; + if (hssfColor == null) return null; + byte[] rgbShort = hssfColor.GetTriplet(); + return rgbShort; + } + } +} diff --git a/ooxml/XSSF/UserModel/Helpers/XSSFXmlColumnPr.cs b/ooxml/XSSF/UserModel/Helpers/XSSFXmlColumnPr.cs index 3756dfe2e..cc104af09 100644 --- a/ooxml/XSSF/UserModel/Helpers/XSSFXmlColumnPr.cs +++ b/ooxml/XSSF/UserModel/Helpers/XSSFXmlColumnPr.cs @@ -36,33 +36,46 @@ public class XSSFXmlColumnPr { private XSSFTable table; - private CT_TableColumn ctTableColumn; + private XSSFTableColumn tableColumn; private CT_XmlColumnPr ctXmlColumnPr; + internal XSSFXmlColumnPr(XSSFTableColumn tableColumn, CT_XmlColumnPr ctXmlColumnPr) + { + this.table = tableColumn.GetTable(); + this.tableColumn = tableColumn; + this.ctXmlColumnPr = ctXmlColumnPr; + } + [Obsolete] public XSSFXmlColumnPr(XSSFTable table, CT_TableColumn ctTableColum, CT_XmlColumnPr CT_XmlColumnPr) { this.table = table; - this.ctTableColumn = ctTableColum; + this.tableColumn = table.GetColumns()[table.FindColumnIndex(ctTableColum.name)]; this.ctXmlColumnPr = CT_XmlColumnPr; } - public long GetMapId() + public long MapId { - return ctXmlColumnPr.mapId; + get { + return ctXmlColumnPr.mapId; + } } - public String GetXPath() + public String XPath { - return ctXmlColumnPr.xpath; + get { + return ctXmlColumnPr.xpath; + } } - /** - * (see Open Office XML Part 4: chapter 3.5.1.3) - * @return An integer representing the unique identifier of this column. - */ - public long GetId() + /// + /// (see Open Office XML Part 4: chapter 3.5.1.3) An integer representing the unique identifier of this column. + /// + public long Id { - return ctTableColumn.id; + get + { + return tableColumn.Id; + } } @@ -71,17 +84,20 @@ public long GetId() * * @return the local XPath */ - public String GetLocalXPath() + public String LocalXPath { - StringBuilder localXPath = new StringBuilder(); - int numberOfCommonXPathAxis = table.GetCommonXpath().Split(new char[] { '/' }).Length - 1; - - String[] xPathTokens = ctXmlColumnPr.xpath.Split(new char[] { '/' }); - for (int i = numberOfCommonXPathAxis; i < xPathTokens.Length; i++) + get { - localXPath.Append("/" + xPathTokens[i]); + StringBuilder localXPath = new StringBuilder(); + int numberOfCommonXPathAxis = table.GetCommonXpath().Split(new char[] { '/' }).Length - 1; + + String[] xPathTokens = ctXmlColumnPr.xpath.Split(new char[] { '/' }); + for (int i = numberOfCommonXPathAxis; i < xPathTokens.Length; i++) + { + localXPath.Append("/" + xPathTokens[i]); + } + return localXPath.ToString(); } - return localXPath.ToString(); } public ST_XmlDataType GetXmlDataType() diff --git a/ooxml/XSSF/UserModel/IndexedColorMap.cs b/ooxml/XSSF/UserModel/IndexedColorMap.cs new file mode 100644 index 000000000..65ede7b36 --- /dev/null +++ b/ooxml/XSSF/UserModel/IndexedColorMap.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.OOXML.XSSF.UserModel +{ + public interface IIndexedColorMap + { + byte[] GetRGB(int index); + } +} diff --git a/ooxml/XSSF/UserModel/XSSFTable.cs b/ooxml/XSSF/UserModel/XSSFTable.cs index 59f3d22bb..0d34b741d 100644 --- a/ooxml/XSSF/UserModel/XSSFTable.cs +++ b/ooxml/XSSF/UserModel/XSSFTable.cs @@ -47,8 +47,8 @@ namespace NPOI.XSSF.UserModel public class XSSFTable : POIXMLDocumentPart, ITable { private CT_Table ctTable; - private List xmlColumnPr; - private CT_TableColumn[] ctColumns; + private List xmlColumnPrs; + private List tableColumns; private Dictionary columnMap; private CellReference startCellReference; private CellReference endCellReference; @@ -129,7 +129,7 @@ public bool MapsTo(long id) foreach (XSSFXmlColumnPr pointer in pointers) { - if (pointer.GetMapId() == id) + if (pointer.MapId == id) { maps = true; break; @@ -138,19 +138,6 @@ public bool MapsTo(long id) return maps; } - - private CT_TableColumn[] TableColumns - { - get - { - if (ctColumns == null) - { - ctColumns = ctTable.tableColumns.tableColumn.ToArray(); - } - return ctColumns; - } - - } /** * * Calculates the xpath of the root element for the table. This will be the common part @@ -166,11 +153,11 @@ public String GetCommonXpath() Array commonTokens = null; - foreach (CT_TableColumn column in TableColumns) + foreach (XSSFTableColumn column in GetColumns()) { - if (column.xmlColumnPr != null) + if (column.GetXmlColumnPr() != null) { - String xpath = column.xmlColumnPr.xpath; + String xpath = column.GetXmlColumnPr().XPath; String[] tokens = xpath.Split(new char[] { '/' }); if (commonTokens==null) { @@ -212,22 +199,23 @@ public String GetCommonXpath() * Note this list is static - once read, it does not notice later changes to the underlying column structures * @return List of XSSFXmlColumnPr */ + [Obsolete] public List GetXmlColumnPrs() { - if (xmlColumnPr == null) + if (xmlColumnPrs == null) { - xmlColumnPr = new List(); + xmlColumnPrs = new List(); foreach (CT_TableColumn column in ctTable.tableColumns.tableColumn) { if (column.xmlColumnPr != null) { XSSFXmlColumnPr columnPr = new XSSFXmlColumnPr(this, column, column.xmlColumnPr); - xmlColumnPr.Add(columnPr); + xmlColumnPrs.Add(columnPr); } } } - return xmlColumnPr; + return xmlColumnPrs; } /** @@ -271,8 +259,42 @@ public long NumberOfMappedColumns return ctTable.tableColumns.count; } } - - + public int ColumnCount + { + get + { + CT_TableColumns tableColumns = ctTable.tableColumns; + if (tableColumns == null) + { + return 0; + } + // Casting to int should be safe here - tables larger than the + // sheet (which holds the actual data of the table) can't exists. + return (int)tableColumns.count; + } + } + /// + /// 0 for no totals rows, 1 for totals row shown. + /// Values > 1 are not currently used by Excel up through 2016, and the OOXML spec + /// doesn't define how they would be implemented. + /// + public int TotalsRowCount + { + get { + return (int)ctTable.totalsRowCount; + } + } + /// + /// 0 for no header rows, 1 for table headers shown. + /// Values > 1 might be used by Excel for pivot tables? + /// + public int HeaderRowCount + { + get + { + return (int)ctTable.headerRowCount; + } + } /** * @return The reference for the cell in the top-left part of the table * (see Open Office XML Part 4: chapter 3.5.1.2, attribute ref) @@ -402,9 +424,11 @@ public void UpdateHeaders() } cellnum++; } - ctColumns = null; - columnMap = null; } + tableColumns = null; + columnMap = null; + xmlColumnPrs = null; + commonXPath = null; } /** * Gets the relative column index of a column in this table having the header name column. @@ -423,12 +447,13 @@ public int FindColumnIndex(String columnHeader) if (columnHeader == null) return -1; if (columnMap == null) { - columnMap = new Dictionary(TableColumns.Length * 3 / 2); + int count = ColumnCount; + columnMap = new Dictionary(count * 3 / 2); int i = 0; - foreach (CT_TableColumn column in TableColumns) + foreach (XSSFTableColumn column in GetColumns()) { - columnMap.Add(column.name.ToUpper(CultureInfo.CurrentCulture), i); + columnMap.Add(column.Name.ToUpper(CultureInfo.CurrentCulture), i); i++; } } @@ -440,7 +465,38 @@ public int FindColumnIndex(String columnHeader) idx = columnMap[testKey]; return idx; } - + /// + /// Note this list is static - once read, it does not notice later changes to the underlying column structures + /// + /// + public List GetColumns() + { + if (tableColumns == null) + { + var columns = new List(); + CT_TableColumns ctTableColumns = ctTable.tableColumns; + if (ctTableColumns != null) + { + foreach (CT_TableColumn column in ctTableColumns.tableColumn) + { + XSSFTableColumn tableColumn = new XSSFTableColumn(this, column); + columns.Add(tableColumn); + } + } + tableColumns = columns; + } + return tableColumns; + } + public void RemoveColumn(XSSFTableColumn column) + { + int columnIndex = GetColumns().IndexOf(column); + if (columnIndex >= 0) + { + ctTable.tableColumns.RemoveTableColumn(columnIndex); + UpdateReferences(); + UpdateHeaders(); + } + } public String SheetName { get @@ -449,6 +505,10 @@ public String SheetName } } + /// + /// This is misleading. The Spec indicates this is true if the totals row + /// hasever been shown, not whether or not it is currently displayed. + /// public bool IsHasTotalsRow { get @@ -488,5 +548,20 @@ public int EndRowIndex return EndCellReference.Row; } } + public bool Contains(CellReference cell) + { + if (cell == null) return false; + // check if cell is on the same sheet as the table + if (! SheetName.Equals(cell.SheetName)) return false; + // check if the cell is inside the table + if (cell.Row >= StartRowIndex + && cell.Row <= EndRowIndex + && cell.Col >= StartColIndex + && cell.Col <= EndColIndex) + { + return true; + } + return false; + } } } \ No newline at end of file diff --git a/ooxml/XSSF/UserModel/XSSFTableColumn.cs b/ooxml/XSSF/UserModel/XSSFTableColumn.cs new file mode 100644 index 000000000..09a29b159 --- /dev/null +++ b/ooxml/XSSF/UserModel/XSSFTableColumn.cs @@ -0,0 +1,58 @@ +using NPOI.OpenXmlFormats.Spreadsheet; +using NPOI.XSSF.UserModel.Helpers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.XSSF.UserModel +{ + public class XSSFTableColumn + { + private XSSFTable table; + private CT_TableColumn ctTableColumn; + private XSSFXmlColumnPr xmlColumnPr; + internal XSSFTableColumn(XSSFTable table, CT_TableColumn ctTableColumn) + { + this.table = table; + this.ctTableColumn = ctTableColumn; + } + /// + /// Get the table which contains this column + /// + /// the table containing this column + public XSSFTable GetTable() + { + return table; + } + public long Id { + get { + return ctTableColumn.id; + } + set + { + ctTableColumn.id = (uint)value; + } + } + public string Name + { + get { return ctTableColumn.name; } + set { ctTableColumn.name = value; } + } + public XSSFXmlColumnPr GetXmlColumnPr() + { + if (xmlColumnPr == null) + { + CT_XmlColumnPr ctXmlColumnPr = ctTableColumn.xmlColumnPr; + if (ctXmlColumnPr != null) + { + xmlColumnPr = new XSSFXmlColumnPr(this, ctXmlColumnPr); + } + } + return xmlColumnPr; + } + public int ColumnIndex + { + get { return table.FindColumnIndex(this.Name); } + } + } +} diff --git a/ooxml/XSSF/UserModel/XSSFTableStyleInfo.cs b/ooxml/XSSF/UserModel/XSSFTableStyleInfo.cs new file mode 100644 index 000000000..68cf6a06e --- /dev/null +++ b/ooxml/XSSF/UserModel/XSSFTableStyleInfo.cs @@ -0,0 +1,81 @@ +using NPOI.OpenXmlFormats.Spreadsheet; +using NPOI.SS.UserModel; +using NPOI.XSSF.Model; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.OOXML.XSSF.UserModel +{ + public class XSSFTableStyleInfo : ITableStyleInfo + { + private CT_TableStyleInfo styleInfo; + private StylesTable stylesTable; + private ITableStyle style; + private bool columnStripes; + private bool rowStripes; + private bool firstColumn; + private bool lastColumn; + + public XSSFTableStyleInfo(StylesTable stylesTable, CT_TableStyleInfo tableStyleInfo) + { + this.columnStripes = tableStyleInfo.showColumnStripes; + this.rowStripes = tableStyleInfo.showRowStripes; + this.firstColumn = tableStyleInfo.showFirstColumn; + this.lastColumn = tableStyleInfo.showLastColumn; + this.style = stylesTable.GetTableStyle(tableStyleInfo.name); + this.stylesTable = stylesTable; + this.styleInfo = tableStyleInfo; + } + public bool IsShowColumnStripes + { + get { return columnStripes; } + set { + this.columnStripes = value; + styleInfo.showColumnStripes=value; + } + } + + public bool IsShowRowStripes + { + get { return rowStripes; } + set { + this.rowStripes = value; + styleInfo.showRowStripes = value; + } + } + + public bool IsShowFirstColumn + { + get { return firstColumn; } + set + { + this.firstColumn = value; + styleInfo.showFirstColumn = value; + } + } + + public bool IsShowLastColumn + { + get { return lastColumn; } + set { + this.lastColumn = value; + styleInfo.showLastColumn = value; + } + } + + public string Name + { + get { return style.Name; } + set { + styleInfo.name = value; + style = stylesTable.GetTableStyle(value); + } + } + + public ITableStyle Style + { + get { return style; } + } + } +} From 0b58272ae3142a94f46549f5ddcf1c83c8c1e29e Mon Sep 17 00:00:00 2001 From: Toni Qu Date: Mon, 14 Feb 2022 19:37:08 +0800 Subject: [PATCH 054/159] implement GetSpreadsheetVersion() --- main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs b/main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs index 1deda4777..7d6e0c010 100644 --- a/main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs +++ b/main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs @@ -172,6 +172,11 @@ public void ClearAllCachedResultValues() { _masterBook.ClearAllCachedResultValues(); } + + public SpreadsheetVersion getSpreadsheetVersion() + { + return _masterBook.GetSpreadsheetVersion(); + } } } \ No newline at end of file From fb367f7ddea0006e616283cf2fc998d6d3f5f7f1 Mon Sep 17 00:00:00 2001 From: Toni Qu Date: Mon, 14 Feb 2022 19:38:16 +0800 Subject: [PATCH 055/159] migrate a few code changes from poi --- main/NPOI.Core.csproj | 1 + main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/main/NPOI.Core.csproj b/main/NPOI.Core.csproj index 5f7f0381e..09de52fc6 100644 --- a/main/NPOI.Core.csproj +++ b/main/NPOI.Core.csproj @@ -23,6 +23,7 @@
+ diff --git a/main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs b/main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs index 7d6e0c010..7a32e7cad 100644 --- a/main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs +++ b/main/SS/Formula/Eval/Forked/ForkedEvaluationWorkbook.cs @@ -173,7 +173,7 @@ public void ClearAllCachedResultValues() _masterBook.ClearAllCachedResultValues(); } - public SpreadsheetVersion getSpreadsheetVersion() + public SpreadsheetVersion GetSpreadsheetVersion() { return _masterBook.GetSpreadsheetVersion(); } From 846c819d0892aef4140f7f001b2530618000e24f Mon Sep 17 00:00:00 2001 From: Toni Qu Date: Mon, 14 Feb 2022 19:44:51 +0800 Subject: [PATCH 056/159] add StopIfTrue property to ConditionalFormattingRule interface --- main/HSSF/UserModel/HSSFConditionalFormattingRule.cs | 7 +++++++ main/SS/UserModel/ConditionalFormattingRule.cs | 2 ++ ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/main/HSSF/UserModel/HSSFConditionalFormattingRule.cs b/main/HSSF/UserModel/HSSFConditionalFormattingRule.cs index bbad8d0e1..e84f51d79 100644 --- a/main/HSSF/UserModel/HSSFConditionalFormattingRule.cs +++ b/main/HSSF/UserModel/HSSFConditionalFormattingRule.cs @@ -358,5 +358,12 @@ protected internal static String ToFormulaString(Ptg[] parsedExpression, HSSFWor } return HSSFFormulaParser.ToFormulaString(workbook, parsedExpression); } + public bool StopIfTrue + { + get + { + return true; + } + } } } \ No newline at end of file diff --git a/main/SS/UserModel/ConditionalFormattingRule.cs b/main/SS/UserModel/ConditionalFormattingRule.cs index 89eb8511b..7eaf6d464 100644 --- a/main/SS/UserModel/ConditionalFormattingRule.cs +++ b/main/SS/UserModel/ConditionalFormattingRule.cs @@ -129,6 +129,8 @@ public interface IConditionalFormattingRule * @return the second formula */ String Formula2 { get; } + + bool StopIfTrue { get; } } } \ No newline at end of file diff --git a/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs b/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs index 19bc9fad5..10b6157af 100644 --- a/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs +++ b/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs @@ -449,6 +449,13 @@ public String Formula2 } } + public bool StopIfTrue + { + get + { + return _cfRule.stopIfTrue; + } + } } } From 64d08cc646a8d51cdbfca7da36ad7d46c6d46fde Mon Sep 17 00:00:00 2001 From: Toni Qu Date: Mon, 14 Feb 2022 19:46:08 +0800 Subject: [PATCH 057/159] temporary checkin - compilation may break --- .../Formula/ConditionalFormattingEvaluator.cs | 123 ++++++++ .../EvaluationConditionalFormatRule.cs | 285 ++++++++++++++++++ ooxml/XSSF/Model/StylesTable.cs | 32 +- ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs | 76 +++++ ooxml/XSSF/UserModel/XSSFTableStyle.cs | 89 ++++++ 5 files changed, 603 insertions(+), 2 deletions(-) create mode 100644 main/SS/Formula/ConditionalFormattingEvaluator.cs create mode 100644 main/SS/Formula/EvaluationConditionalFormatRule.cs create mode 100644 ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs create mode 100644 ooxml/XSSF/UserModel/XSSFTableStyle.cs diff --git a/main/SS/Formula/ConditionalFormattingEvaluator.cs b/main/SS/Formula/ConditionalFormattingEvaluator.cs new file mode 100644 index 000000000..61f0cdc07 --- /dev/null +++ b/main/SS/Formula/ConditionalFormattingEvaluator.cs @@ -0,0 +1,123 @@ +using NPOI.SS.UserModel; +using NPOI.SS.Util; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula +{ + public class ConditionalFormattingEvaluator + { + private WorkbookEvaluator workbookEvaluator; + private IWorkbook workbook; + + private Dictionary> formats = new Dictionary>(); + private Dictionary> values = new Dictionary>(); + + public ConditionalFormattingEvaluator(IWorkbook wb, IWorkbookEvaluatorProvider provider) + { + this.workbook = wb; + this.workbookEvaluator = provider.GetWorkbookEvaluator(); + } + protected WorkbookEvaluator WorkbookEvaluator + { + get + { + return workbookEvaluator; + } + } + public List GetConditionalFormattingForCell(CellReference cellRef) + { + List rules = values[cellRef]; + + if (rules == null) + { + // compute and cache them + rules = new List(); + + ISheet sheet; + if (cellRef.SheetName != null) + { + sheet = workbook.GetSheet(cellRef.SheetName); + } + else + { + sheet = workbook.GetSheetAt(workbook.ActiveSheetIndex); + } + + /* + * Per Excel help: + * https://support.office.com/en-us/article/Manage-conditional-formatting-rule-precedence-e09711a3-48df-4bcb-b82c-9d8b8b22463d#__toc269129417 + * stopIfTrue is true for all rules from HSSF files, and an explicit value for XSSF files. + * thus the explicit ordering of the rule lists in #getFormattingRulesForSheet(Sheet) + */ + bool stopIfTrue = false; + foreach (EvaluationConditionalFormatRule rule in GetRules(sheet)) + { + + if (stopIfTrue) + { + continue; // a previous rule matched and wants no more evaluations + } + + if (rule.Matches(cellRef)) + { + rules.Add(rule); + stopIfTrue = rule.Rule.StopIfTrue; + } + } + Collections.sort(rules); + values.Add(cellRef, rules); + } + + return rules; + } + public List GetConditionalFormattingForCell(ICell cell) + { + return GetConditionalFormattingForCell(GetRef(cell)); + } + public static CellReference GetRef(ICell cell) + { + return new CellReference(cell.Sheet.SheetName, cell.RowIndex, cell.ColumnIndex, false, false); + } + + protected List GetRules(ISheet sheet) + { + String sheetName = sheet.SheetName; + List rules = formats[sheetName]; + if (rules == null) + { + if (formats.ContainsKey(sheetName)) + { + return new List(); + } + ISheetConditionalFormatting scf = sheet.SheetConditionalFormatting; + int count = scf.NumConditionalFormattings; + rules = new List(count); + formats.Add(sheetName, rules); + for (int i = 0; i < count; i++) + { + IConditionalFormatting f = scf.GetConditionalFormattingAt(i); + //optimization, as this may be expensive for lots of ranges + CellRangeAddress[] regions = f.GetFormattingRanges(); + for (int r = 0; r < f.NumberOfRules; r++) + { + IConditionalFormattingRule rule = f.GetRule(r); + rules.Add(new EvaluationConditionalFormatRule(workbookEvaluator, sheet, f, i, rule, r, regions)); + } + } + // need them in formatting and priority order so logic works right + Collections.sort(rules); + } + return rules; + } + public void ClearAllCachedFormats() + { + formats.Clear(); + } + public void ClearAllCachedValues() + { + values.Clear(); + } + } +} diff --git a/main/SS/Formula/EvaluationConditionalFormatRule.cs b/main/SS/Formula/EvaluationConditionalFormatRule.cs new file mode 100644 index 000000000..4904fe3fa --- /dev/null +++ b/main/SS/Formula/EvaluationConditionalFormatRule.cs @@ -0,0 +1,285 @@ +using NPOI.SS.Formula.Eval; +using NPOI.SS.UserModel; +using NPOI.SS.Util; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula +{ + public enum OperatorEnum + { + NO_COMPARISON, + BETWEEN, + NOT_BETWEEN, + EQUAL, + NOT_EQUAL, + GREATER_THAN, + LESS_THAN, + GREATER_OR_EQUAL, + LESS_OR_EQUAL + } + public class EvaluationConditionalFormatRule: IComparable + { + /** + * Note: this class has a natural ordering that is inconsistent with equals. + */ + protected class ValueAndFormat : IComparable + { + private double? value; + private String str; + private String format; + private DecimalFormat decimalTextFormat; + + public ValueAndFormat(Double value, String format, DecimalFormat df) + { + this.value = value; + this.format = format; + str = null; + decimalTextFormat = df; + } + + public ValueAndFormat(String value, String format) + { + this.value = null; + this.format = format; + str = value; + decimalTextFormat = null; + } + + public bool IsNumber + { + get + { + return value != null; + } + } + + public double? Value + { + get + { + return value; + } + } + + public String String + { + get + { + return str; + } + } + + public override String ToString() + { + if (IsNumber) + { + return decimalTextFormat.Format(Value.Value); + } + else + { + return this.String; + } + } + + + public override bool Equals(Object obj) + { + if (!(obj is ValueAndFormat)) + { + return false; + } + ValueAndFormat o = (ValueAndFormat)obj; + return (value == o.value || value.Equals(o.value)) + && (format == o.format || format.Equals(o.format)) + && (str == o.str || str.Equals(o.str)); + } + + /** + * Note: this class has a natural ordering that is inconsistent with equals. + * @param o + * @return value comparison + */ + public int CompareTo(ValueAndFormat o) + { + if (value == null && o.value != null) + { + return 1; + } + if (o.value == null && value != null) + { + return -1; + } + int cmp = value == null ? 0 : value.Value.CompareTo(o.value); + if (cmp != 0) + { + return cmp; + } + + if (str == null && o.str != null) + { + return 1; + } + if (o.str == null && str != null) + { + return -1; + } + + return str == null ? 0 : str.CompareTo(o.str); + } + + + public override int GetHashCode() + { + return (str == null ? 0 : str.GetHashCode()) * 37 * 37 + 37 * (value == null ? 0 : value.GetHashCode()) + (format == null ? 0 : format.GetHashCode()); + } + + public static bool operator <(ValueAndFormat left, ValueAndFormat right) + { + return left.CompareTo(right) < 0; + } + + public static bool operator <=(ValueAndFormat left, ValueAndFormat right) + { + return left.CompareTo(right) <= 0; + } + + public static bool operator >(ValueAndFormat left, ValueAndFormat right) + { + return left.CompareTo(right) > 0; + } + + public static bool operator >=(ValueAndFormat left, ValueAndFormat right) + { + return left.CompareTo(right) >= 0; + } + } + + private WorkbookEvaluator workbookEvaluator; + private ISheet sheet; + private IConditionalFormatting formatting; + private IConditionalFormattingRule rule; + + /* cached values */ + private CellRangeAddress[] regions; + /** + * Depending on the rule type, it may want to know about certain values in the region when evaluating {@link #matches(CellReference)}, + * such as top 10, unique, duplicate, average, etc. This collection stores those if needed so they are not repeatedly calculated + */ + private Dictionary> meaningfulRegionValues = new Dictionary>(); + + private int priority; + private int formattingIndex; + private int ruleIndex; + private String formula1; + private String formula2; + private String text; + // cached for performance, used with cell text comparisons, which are case insensitive and need to be Locale aware (contains, starts with, etc.) + private String lowerText; + + private OperatorEnum @operator; + private ConditionType type; + // cached for performance, to avoid reading the XMLBean every time a conditionally formatted cell is rendered + private ExcelNumberFormat numberFormat; + // cached for performance, used to format numeric cells for string comparisons. See Bug #61764 for explanation + private DecimalFormat decimalTextFormat; + + public ISheet Sheet { get { return sheet; } } + public IConditionalFormatting Formatting { get { return formatting; } } + public int FormattingIndex { get { return formattingIndex; } } + public IConditionalFormattingRule Rule { get { return rule; } } + public int RuleIndex { get { return ruleIndex; } } + public CellRangeAddress[] Regions { get { return regions; } } + public int Priority { get { return priority; } } + public string Formula1 { get { return formula1; } } + public string Formula2 { get { return formula2; } } + public string Text { get { return text; } } + public ConditionType Type { get { return type; } } + public OperatorEnum Operator { get { return @operator; } } + public ExcelNumberFormat NumberFormat + { + get + { + return numberFormat; + } + } + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + if (obj.GetType()!=this.GetType()) + { + return false; + } + EvaluationConditionalFormatRule r = (EvaluationConditionalFormatRule)obj; + return Sheet.SheetName.Equals(r.Sheet.SheetName, StringComparison.InvariantCultureIgnoreCase) + && FormattingIndex == r.FormattingIndex + && RuleIndex == r.RuleIndex; + } + public int CompareTo(EvaluationConditionalFormatRule o) + { + int cmp = Sheet.SheetName.CompareTo(o.Sheet.SheetName); + if (cmp != 0) + { + return cmp; + } + + int x = Priority; + int y = o.Priority; + // logic from Integer.compare() + cmp = x-y; + if (cmp != 0) + { + return cmp; + } + + cmp = FormattingIndex - o.FormattingIndex; + if (cmp != 0) + { + return cmp; + } + return RuleIndex-o.RuleIndex; + } + private ValueEval UnwrapEval(ValueEval eval) + { + ValueEval comp = eval; + + while (comp is RefEval) { + RefEval reference = (RefEval)comp; + comp = reference.GetInnerValueEval(reference.FirstSheetIndex); + } + return comp; + } + private bool CheckFormula(CellReference reference, CellRangeAddress region) + { + ValueEval comp = UnwrapEval(workbookEvaluator.Evaluate(rule.Formula1, reference, region)); + + // Copied for now from DataValidationEvaluator.ValidationEnum.FORMULA#isValidValue() + if (comp is BlankEval) { + return true; + } + if (comp is ErrorEval) { + return false; + } + if (comp is BoolEval) { + return ((BoolEval)comp).BooleanValue; + } + // empirically tested in Excel - 0=false, any other number = true/valid + // see test file DataValidationEvaluations.xlsx + if (comp is NumberEval) { + return ((NumberEval)comp).NumberValue != 0; + } + return false; // anything else is false, such as text + } + public override int GetHashCode() + { + int hash = sheet.SheetName.GetHashCode(); + hash = 31 * hash + formattingIndex; + hash = 31 * hash + ruleIndex; + return hash; + } + } +} diff --git a/ooxml/XSSF/Model/StylesTable.cs b/ooxml/XSSF/Model/StylesTable.cs index 678dc610d..7469b1bc7 100644 --- a/ooxml/XSSF/Model/StylesTable.cs +++ b/ooxml/XSSF/Model/StylesTable.cs @@ -27,6 +27,7 @@ using NPOI.XSSF.UserModel.Extensions; using System.Collections.ObjectModel; using NPOI.SS; +using NPOI.OOXML.XSSF.UserModel; namespace NPOI.XSSF.Model { @@ -48,7 +49,8 @@ public class StylesTable : POIXMLDocumentPart private List xfs = new List(); private List dxfs = new List(); - + private Dictionary tableStyles = new Dictionary(); + private IIndexedColorMap indexedColors = new DefaultIndexedColorMap(); /** * The first style id available for use as a custom style */ @@ -149,7 +151,22 @@ public void SetTheme(ThemesTable theme) border.SetThemesTable(theme); } } - + public ITableStyle GetTableStyle(String name) + { + if (name == null) return null; + try + { + return XSSFBuiltinTableStyle.ValueOf(name).getStyle(); + } + catch (ArgumentException e) + { + return GetExplicitTableStyle(name); + } + } + public ITableStyle GetExplicitTableStyle(String name) + { + return tableStyles[name]; + } /** * If there isn't currently a {@link ThemesTable} for the * current Workbook, then creates one and sets it up. @@ -229,6 +246,17 @@ protected void ReadFrom(XmlDocument xmldoc) if (styleDxfs != null) dxfs.AddRange(styleDxfs.dxf); + + CT_TableStyles ctTableStyles = styleSheet.tableStyles; + if (ctTableStyles != null) + { + int idx = 0; + foreach (CT_TableStyle style in ctTableStyles.tableStyle) + { + tableStyles.Add(style.name, new XSSFTableStyle(idx, styleDxfs, style, indexedColors)); + idx++; + } + } } catch (XmlException e) { diff --git a/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs new file mode 100644 index 000000000..a8c9bed16 --- /dev/null +++ b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs @@ -0,0 +1,76 @@ +using NPOI.SS.UserModel; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.OOXML.XSSF.UserModel +{ + public class XSSFBuiltinTableStyle + { + private static Dictionary styleMap = new Dictionary(); + public ITableStyle GetStyle() + { + Init(); + return styleMap[this]; + } + public static bool IsBuiltinStyle(ITableStyle style) + { + if (style == null) return false; + try + { + XSSFBuiltinTableStyle.valueOf(style.Name); + return true; + } + catch (ArgumentException) + { + return false; + } + } + protected class XSSFBuiltinTypeStyleStyle : ITableStyle + { + + private XSSFBuiltinTableStyle builtIn; + private ITableStyle style; + + /** + * @param builtIn + * @param style + */ + protected XSSFBuiltinTypeStyleStyle(XSSFBuiltinTableStyle builtIn, ITableStyle style) + { + this.builtIn = builtIn; + this.style = style; + } + + public String Name + { + get + { + return style.Name; + } + } + + public int Index + { + get + { + return builtIn.ordinal(); + } + } + + public bool IsBuiltin + { + get + { + return true; + } + } + + public DifferentialStyleProvider GetStyle(TableStyleType type) + { + return style.GetStyle(type); + } + + } +} +} diff --git a/ooxml/XSSF/UserModel/XSSFTableStyle.cs b/ooxml/XSSF/UserModel/XSSFTableStyle.cs new file mode 100644 index 000000000..1bc2e0bdc --- /dev/null +++ b/ooxml/XSSF/UserModel/XSSFTableStyle.cs @@ -0,0 +1,89 @@ +using NPOI.OpenXmlFormats.Spreadsheet; +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.OOXML.XSSF.UserModel +{ + public class XSSFTableStyle : ITableStyle + { + private String name; + private int index; + private Dictionary elementMap = new Dictionary(); + + public XSSFTableStyle(int index, CT_Dxfs dxfs, CT_TableStyle tableStyle, IIndexedColorMap colorMap) + { + this.name = tableStyle.name; + this.index = index; + + List dxfList = new List(); + + // CT* classes don't handle "mc:AlternateContent" elements, so get the Dxf instances manually + XmlCursor cur = dxfs.newCursor(); + // sometimes there are namespaces sometimes not. + String xquery = "declare namespace x='" + XSSFRelation.NS_SPREADSHEETML + "' .//x:dxf | .//dxf"; + cur.selectPath(xquery); + while (cur.toNextSelection()) + { + XmlObject obj = cur.getObject(); + String parentName = obj.getDomNode().getParentNode().getNodeName(); + // ignore alternate content choices, we won't know anything about their namespaces + if (parentName.Equals("mc:Fallback") || parentName.Equals("x:dxfs") || parentName.contentEquals("dxfs")) + { + CTDxf dxf; + try + { + if (obj is CTDxf) { + dxf = (CTDxf)obj; + } else + { + dxf = CT_Dxf.Factory.parse(obj.newXMLStreamReader(), new XmlOptions().setDocumentType(CTDxf.type)); + } + if (dxf != null) dxfList.Add(dxf); + } + catch (XmlException e) + { + logger.log(POILogger.WARN, "Error parsing XSSFTableStyle", e); + } + } + } + + foreach (CT_TableStyleElement element in tableStyle.tableStyleElement) + { + TableStyleType type = TableStyleType.valueOf(element.getType().toString()); + DifferentialStyleProvider dstyle = null; + if (element.isSetDxfId()) + { + int idx = (int)element.getDxfId(); + CT_Dxf dxf; + dxf = dxfList.get(idx); + int stripeSize = 0; + if (element.isSetSize()) stripeSize = (int)element.getSize(); + if (dxf != null) dstyle = new XSSFDxfStyleProvider(dxf, stripeSize, colorMap); + } + elementMap.put(type, dstyle); + } + } + public string Name + { + get { return name; } + } + + public int Index + { + get { return index; } + } + + public bool IsBuiltin + { + get { return false; } + } + + public DifferentialStyleProvider GetStyle(TableStyleType type) + { + return elementMap[type]; + } + } +} From 838b9748539495ec0c9c73fd06faf3c270481380 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 14 Feb 2022 21:19:55 +0800 Subject: [PATCH 058/159] fix #711 --- ooxml/XSSF/UserModel/XSSFCellStyle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ooxml/XSSF/UserModel/XSSFCellStyle.cs b/ooxml/XSSF/UserModel/XSSFCellStyle.cs index afc1b9341..14a2012bb 100644 --- a/ooxml/XSSF/UserModel/XSSFCellStyle.cs +++ b/ooxml/XSSF/UserModel/XSSFCellStyle.cs @@ -1434,7 +1434,7 @@ public BorderDiagonal BorderDiagonal } set { - CT_Border ct = GetCTBorder(copy: true); + CT_Border ct = GetCTBorder(); if (value == BorderDiagonal.Both) { ct.diagonalDown = true; From fa734f16d4b3f1eeb4e91f6e4d812071ff580d2b Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 15 Feb 2022 00:29:01 +0800 Subject: [PATCH 059/159] Migrate a few changes from poi 4.0 --- OpenXmlFormats/Spreadsheet/Styles/CT_Dxfs.cs | 4 ++ main/HSSF/Record/CFRule12Record.cs | 11 ++++ .../HSSFConditionalFormattingRule.cs | 50 +++++++++++++--- .../Formula/ConditionalFormattingEvaluator.cs | 6 +- main/SS/Formula/DataValidationEvaluator.cs | 27 +++++++++ main/SS/UserModel/ConditionFilterData.cs | 39 ++++++++++++ main/SS/UserModel/ConditionFilterType.cs | 24 ++++++++ .../SS/UserModel/ConditionalFormattingRule.cs | 21 +++++++ ooxml/XSSF/Extractor/XSSFExportToXml.cs | 2 +- .../XSSF/UserModel/XSSFConditionFilterData.cs | 27 +++++++++ .../XSSFConditionalFormattingRule.cs | 60 +++++++++++++++++++ ooxml/XSSF/UserModel/XSSFDxfStyleProvider.cs | 56 +++++++++++++++++ ooxml/XSSF/UserModel/XSSFTableStyle.cs | 46 +++----------- 13 files changed, 324 insertions(+), 49 deletions(-) create mode 100644 main/SS/Formula/DataValidationEvaluator.cs create mode 100644 main/SS/UserModel/ConditionFilterData.cs create mode 100644 main/SS/UserModel/ConditionFilterType.cs create mode 100644 ooxml/XSSF/UserModel/XSSFConditionFilterData.cs create mode 100644 ooxml/XSSF/UserModel/XSSFDxfStyleProvider.cs diff --git a/OpenXmlFormats/Spreadsheet/Styles/CT_Dxfs.cs b/OpenXmlFormats/Spreadsheet/Styles/CT_Dxfs.cs index 42985d1c5..d1786dd35 100644 --- a/OpenXmlFormats/Spreadsheet/Styles/CT_Dxfs.cs +++ b/OpenXmlFormats/Spreadsheet/Styles/CT_Dxfs.cs @@ -199,6 +199,10 @@ public CT_Border AddNewBorder() this.borderField = border; return border; } + public bool IsSetNumFmt() + { + return numFmtField != null; + } public bool IsSetFont() { return fontField != null; diff --git a/main/HSSF/Record/CFRule12Record.cs b/main/HSSF/Record/CFRule12Record.cs index 17ba7651f..c11e2e769 100644 --- a/main/HSSF/Record/CFRule12Record.cs +++ b/main/HSSF/Record/CFRule12Record.cs @@ -484,6 +484,17 @@ protected override int DataSize public CellRangeAddress GetAssociatedRange() { return futureHeader.AssociatedRange; } + + public int Priority + { + get + { + return priority; + } + set { + this.priority = value; + } + } } } \ No newline at end of file diff --git a/main/HSSF/UserModel/HSSFConditionalFormattingRule.cs b/main/HSSF/UserModel/HSSFConditionalFormattingRule.cs index e84f51d79..62e8ad4c4 100644 --- a/main/HSSF/UserModel/HSSFConditionalFormattingRule.cs +++ b/main/HSSF/UserModel/HSSFConditionalFormattingRule.cs @@ -79,13 +79,13 @@ private HSSFFontFormatting GetFontFormatting(bool Create) FontFormatting fontFormatting = cfRuleRecord.FontFormatting; if (fontFormatting != null) { - cfRuleRecord.FontFormatting=(fontFormatting); + cfRuleRecord.FontFormatting = (fontFormatting); return new HSSFFontFormatting(cfRuleRecord, workbook); } else if (Create) { fontFormatting = new FontFormatting(); - cfRuleRecord.FontFormatting=(fontFormatting); + cfRuleRecord.FontFormatting = (fontFormatting); return new HSSFFontFormatting(cfRuleRecord, workbook); } else @@ -116,13 +116,13 @@ private HSSFBorderFormatting GetBorderFormatting(bool Create) BorderFormatting borderFormatting = cfRuleRecord.BorderFormatting; if (borderFormatting != null) { - cfRuleRecord.BorderFormatting=(borderFormatting); + cfRuleRecord.BorderFormatting = (borderFormatting); return new HSSFBorderFormatting(cfRuleRecord, workbook); } else if (Create) { borderFormatting = new BorderFormatting(); - cfRuleRecord.BorderFormatting=(borderFormatting); + cfRuleRecord.BorderFormatting = (borderFormatting); return new HSSFBorderFormatting(cfRuleRecord, workbook); } else @@ -152,13 +152,13 @@ private HSSFPatternFormatting GetPatternFormatting(bool Create) PatternFormatting patternFormatting = cfRuleRecord.PatternFormatting; if (patternFormatting != null) { - cfRuleRecord.PatternFormatting=(patternFormatting); + cfRuleRecord.PatternFormatting = (patternFormatting); return new HSSFPatternFormatting(cfRuleRecord, workbook); } else if (Create) { patternFormatting = new PatternFormatting(); - cfRuleRecord.PatternFormatting=(patternFormatting); + cfRuleRecord.PatternFormatting = (patternFormatting); return new HSSFPatternFormatting(cfRuleRecord, workbook); } else @@ -284,7 +284,7 @@ public IColorScaleFormatting ColorScaleFormatting { return GetColorScaleFormatting(false); } - + } /** * create a new color scale / gradient formatting object if it does not exist, @@ -365,5 +365,41 @@ public bool StopIfTrue return true; } } + public string Text + { + get { return null; } + } + public int Priority + { + get { + CFRule12Record rule12 = GetCFRule12Record(false); + if (rule12 == null) return 0; + return rule12.Priority; + } + } + public ExcelNumberFormat NumberFormat + { + get + { + return null; + } + } + public ConditionFilterType? ConditionFilterType + { + get + { + if (ConditionType == ConditionType.Filter) + return null; + else + return SS.UserModel.ConditionFilterType.FILTER; + } + } + public IConditionFilterData FilterConfiguration + { + get + { + return null; + } + } } } \ No newline at end of file diff --git a/main/SS/Formula/ConditionalFormattingEvaluator.cs b/main/SS/Formula/ConditionalFormattingEvaluator.cs index 61f0cdc07..cc2a12cb4 100644 --- a/main/SS/Formula/ConditionalFormattingEvaluator.cs +++ b/main/SS/Formula/ConditionalFormattingEvaluator.cs @@ -12,7 +12,7 @@ public class ConditionalFormattingEvaluator private IWorkbook workbook; private Dictionary> formats = new Dictionary>(); - private Dictionary> values = new Dictionary>(); + private SortedDictionary> values = new SortedDictionary>(); public ConditionalFormattingEvaluator(IWorkbook wb, IWorkbookEvaluatorProvider provider) { @@ -66,7 +66,7 @@ public List GetConditionalFormattingForCell(Cel stopIfTrue = rule.Rule.StopIfTrue; } } - Collections.sort(rules); + rules.Sort(); values.Add(cellRef, rules); } @@ -107,7 +107,7 @@ protected List GetRules(ISheet sheet) } } // need them in formatting and priority order so logic works right - Collections.sort(rules); + rules.Sort(); } return rules; } diff --git a/main/SS/Formula/DataValidationEvaluator.cs b/main/SS/Formula/DataValidationEvaluator.cs new file mode 100644 index 000000000..8ca3e96b5 --- /dev/null +++ b/main/SS/Formula/DataValidationEvaluator.cs @@ -0,0 +1,27 @@ +using NPOI.SS.UserModel; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula +{ + public class DataValidationEvaluator + { + /** + * Note that this assumes the cell cached value is up to date and in sync with data edits + * + * @param cell The {@link Cell} to check. + * @param type The {@link CellType} to check for. + * @return true if the cell or cached cell formula result type match the given type + */ + public static bool IsType(ICell cell, CellType type) + { + CellType cellType = cell.CellType; + return cellType == type + || (cellType == CellType.Formula + && cell.CachedFormulaResultType == type + ); + } + + } +} diff --git a/main/SS/UserModel/ConditionFilterData.cs b/main/SS/UserModel/ConditionFilterData.cs new file mode 100644 index 000000000..0ad78f1f5 --- /dev/null +++ b/main/SS/UserModel/ConditionFilterData.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.UserModel +{ + public interface IConditionFilterData + { + /** + * @return true if the flag is missing or set to true + */ + bool AboveAverage{ get; } + + /** + * @return true if the flag is set + */ + bool Bottom{ get; } + + /** + * @return true if the flag is set + */ + bool EqualAverage{ get; } + + /** + * @return true if the flag is set + */ + bool Percent{ get; } + + /** + * @return value, or 0 if not used/defined + */ + long Rank{ get; } + + /** + * @return value, or 0 if not used/defined + */ + int StdDev{ get; } + } +} diff --git a/main/SS/UserModel/ConditionFilterType.cs b/main/SS/UserModel/ConditionFilterType.cs new file mode 100644 index 000000000..2c8f8ae8a --- /dev/null +++ b/main/SS/UserModel/ConditionFilterType.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.UserModel +{ + public enum ConditionFilterType + { + FILTER, + TOP_10, + UNIQUE_VALUES, + DUPLICATE_VALUES, + CONTAINS_TEXT, + NOT_CONTAINS_TEXT, + BEGINS_WITH, + ENDS_WITH, + CONTAINS_BLANKS, + NOT_CONTAINS_BLANKS, + CONTAINS_ERRORS, + NOT_CONTAINS_ERRORS, + TIME_PERIOD, + ABOVE_AVERAGE + } +} diff --git a/main/SS/UserModel/ConditionalFormattingRule.cs b/main/SS/UserModel/ConditionalFormattingRule.cs index 7eaf6d464..f6f44b2ef 100644 --- a/main/SS/UserModel/ConditionalFormattingRule.cs +++ b/main/SS/UserModel/ConditionalFormattingRule.cs @@ -130,7 +130,28 @@ public interface IConditionalFormattingRule */ String Formula2 { get; } + /// + /// XSSF rules store textual condition values as an attribute and also as a formula that needs shifting. Using the attribute is simpler/faster. + /// HSSF rules don't have this and return null. We can fall back on the formula for those (AFAIK). + /// @return condition text if it exists, or null + /// + String Text { get; } + + /// + /// The priority of the rule, if defined, otherwise 0. + /// For XSSF, this should always be set.For HSSF, only newer style rules + /// have this set, older ones will return 0. + /// + /// + int Priority {get; } + bool StopIfTrue { get; } + + ExcelNumberFormat NumberFormat { get; } + + ConditionFilterType? ConditionFilterType { get; } + + IConditionFilterData FilterConfiguration { get; } } } \ No newline at end of file diff --git a/ooxml/XSSF/Extractor/XSSFExportToXml.cs b/ooxml/XSSF/Extractor/XSSFExportToXml.cs index b7566321d..4dbfb1a45 100644 --- a/ooxml/XSSF/Extractor/XSSFExportToXml.cs +++ b/ooxml/XSSF/Extractor/XSSFExportToXml.cs @@ -190,7 +190,7 @@ public void ExportToXML(Stream os, String encoding, bool validate) if (cell != null) { XSSFXmlColumnPr pointer = tableColumns[j - startColumnIndex]; - String localXPath = pointer.GetLocalXPath(); + String localXPath = pointer.LocalXPath; XmlNode currentNode = GetNodeByXPath(localXPath, tableRootNode, doc, false); ST_XmlDataType dataType = pointer.GetXmlDataType(); diff --git a/ooxml/XSSF/UserModel/XSSFConditionFilterData.cs b/ooxml/XSSF/UserModel/XSSFConditionFilterData.cs new file mode 100644 index 000000000..082375eb2 --- /dev/null +++ b/ooxml/XSSF/UserModel/XSSFConditionFilterData.cs @@ -0,0 +1,27 @@ +using NPOI.OpenXmlFormats.Spreadsheet; +using NPOI.SS.UserModel; +using System; + +namespace NPOI.OOXML.XSSF.UserModel +{ + public class XSSFConditionFilterData:IConditionFilterData + { + private CT_CfRule _cfRule; + public XSSFConditionFilterData(CT_CfRule cfRule) + { + _cfRule = cfRule; + } + + public bool AboveAverage => _cfRule.aboveAverage; + + public bool Bottom => _cfRule.bottom; + + public bool EqualAverage => _cfRule.equalAverage; + + public bool Percent => _cfRule.percent; + + public long Rank => _cfRule.rank; + + public int StdDev => _cfRule.stdDev; + } +} diff --git a/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs b/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs index 10b6157af..117f949b0 100644 --- a/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs +++ b/ooxml/XSSF/UserModel/XSSFConditionalFormattingRule.cs @@ -18,6 +18,7 @@ */ using EnumsNET; +using NPOI.OOXML.XSSF.UserModel; using NPOI.OpenXmlFormats.Spreadsheet; using NPOI.SS.UserModel; using NPOI.XSSF.Model; @@ -39,6 +40,7 @@ public class XSSFConditionalFormattingRule : IConditionalFormattingRule private XSSFSheet _sh; private static Dictionary typeLookup = new Dictionary(); + private static Dictionary filterTypeLookup = new Dictionary(); static XSSFConditionalFormattingRule() { typeLookup.Add(ST_CfType.cellIs, ConditionTypeClass.CellValueIs); @@ -61,6 +63,20 @@ static XSSFConditionalFormattingRule() typeLookup.Add(ST_CfType.notContainsErrors, ConditionTypeClass.Filter); typeLookup.Add(ST_CfType.timePeriod, ConditionTypeClass.Filter); typeLookup.Add(ST_CfType.aboveAverage, ConditionTypeClass.Filter); + + filterTypeLookup.Add(ST_CfType.top10, SS.UserModel.ConditionFilterType.TOP_10); + filterTypeLookup.Add(ST_CfType.uniqueValues, SS.UserModel.ConditionFilterType.UNIQUE_VALUES); + filterTypeLookup.Add(ST_CfType.duplicateValues, SS.UserModel.ConditionFilterType.DUPLICATE_VALUES); + filterTypeLookup.Add(ST_CfType.containsText, SS.UserModel.ConditionFilterType.CONTAINS_TEXT); + filterTypeLookup.Add(ST_CfType.notContainsText, SS.UserModel.ConditionFilterType.NOT_CONTAINS_TEXT); + filterTypeLookup.Add(ST_CfType.beginsWith, SS.UserModel.ConditionFilterType.BEGINS_WITH); + filterTypeLookup.Add(ST_CfType.endsWith, SS.UserModel.ConditionFilterType.ENDS_WITH); + filterTypeLookup.Add(ST_CfType.containsBlanks, SS.UserModel.ConditionFilterType.CONTAINS_BLANKS); + filterTypeLookup.Add(ST_CfType.notContainsBlanks, SS.UserModel.ConditionFilterType.NOT_CONTAINS_BLANKS); + filterTypeLookup.Add(ST_CfType.containsErrors, SS.UserModel.ConditionFilterType.CONTAINS_ERRORS); + filterTypeLookup.Add(ST_CfType.notContainsErrors, SS.UserModel.ConditionFilterType.NOT_CONTAINS_ERRORS); + filterTypeLookup.Add(ST_CfType.timePeriod, SS.UserModel.ConditionFilterType.TIME_PERIOD); + filterTypeLookup.Add(ST_CfType.aboveAverage, SS.UserModel.ConditionFilterType.ABOVE_AVERAGE); } /*package*/ public XSSFConditionalFormattingRule(XSSFSheet sh) @@ -456,6 +472,50 @@ public bool StopIfTrue return _cfRule.stopIfTrue; } } + public String Text + { + get + { + return _cfRule.text; + } + } + public int Priority + { + get + { + int priority = _cfRule.priority; + // priorities start at 1, if it is less, it is undefined, use definition order in caller + return priority >= 1 ? priority : 0; + } + } + public ExcelNumberFormat NumberFormat + { + get + { + CT_Dxf dxf = GetDxf(false); + if (dxf == null || !dxf.IsSetNumFmt()) return null; + + CT_NumFmt numFmt = dxf.numFmt; + return new ExcelNumberFormat((int)numFmt.numFmtId, numFmt.formatCode); + } + } + + public ConditionFilterType? ConditionFilterType + { + get + { + if (!filterTypeLookup.ContainsKey(_cfRule.type)) + return null; + return filterTypeLookup[_cfRule.type]; + } + } + public IConditionFilterData FilterConfiguration + { + get + { + return new XSSFConditionFilterData(_cfRule); + } + } } } diff --git a/ooxml/XSSF/UserModel/XSSFDxfStyleProvider.cs b/ooxml/XSSF/UserModel/XSSFDxfStyleProvider.cs new file mode 100644 index 000000000..9cabb22c9 --- /dev/null +++ b/ooxml/XSSF/UserModel/XSSFDxfStyleProvider.cs @@ -0,0 +1,56 @@ +using NPOI.OpenXmlFormats.Spreadsheet; +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.OOXML.XSSF.UserModel +{ + public class XSSFDxfStyleProvider : DifferentialStyleProvider + { + private IIndexedColorMap colorMap; + private IBorderFormatting border; + private IFontFormatting font; + private ExcelNumberFormat number; + private IPatternFormatting fill; + private int stripeSize; + + public XSSFDxfStyleProvider(CT_Dxf dxf, int stripeSize, IIndexedColorMap colorMap) + { + this.stripeSize = stripeSize; + this.colorMap = colorMap; + if (dxf == null) + { + border = null; + font = null; + number = null; + fill = null; + } + else + { + border = dxf.IsSetBorder() ? new XSSFBorderFormatting(dxf.border) : null; + font = dxf.IsSetFont() ? new XSSFFontFormatting(dxf.font) : null; + if (dxf.IsSetNumFmt()) + { + CT_NumFmt numFmt = dxf.numFmt; + number = new ExcelNumberFormat((int)numFmt.numFmtId, numFmt.formatCode); + } + else + { + number = null; + } + fill = dxf.IsSetFill() ? new XSSFPatternFormatting(dxf.fill) : null; + } + } + public IBorderFormatting BorderFormatting => border; + + public IFontFormatting FontFormatting => font; + + public IPatternFormatting PatternFormatting => fill; + + public ExcelNumberFormat NumberFormat => number; + + public int StripeSize => stripeSize; + } +} diff --git a/ooxml/XSSF/UserModel/XSSFTableStyle.cs b/ooxml/XSSF/UserModel/XSSFTableStyle.cs index 1bc2e0bdc..7fb00a66e 100644 --- a/ooxml/XSSF/UserModel/XSSFTableStyle.cs +++ b/ooxml/XSSF/UserModel/XSSFTableStyle.cs @@ -3,7 +3,7 @@ using NPOI.XSSF.UserModel; using System; using System.Collections.Generic; -using System.Text; +using EnumsNET; namespace NPOI.OOXML.XSSF.UserModel { @@ -18,52 +18,22 @@ public XSSFTableStyle(int index, CT_Dxfs dxfs, CT_TableStyle tableStyle, IIndexe this.name = tableStyle.name; this.index = index; - List dxfList = new List(); - - // CT* classes don't handle "mc:AlternateContent" elements, so get the Dxf instances manually - XmlCursor cur = dxfs.newCursor(); - // sometimes there are namespaces sometimes not. - String xquery = "declare namespace x='" + XSSFRelation.NS_SPREADSHEETML + "' .//x:dxf | .//dxf"; - cur.selectPath(xquery); - while (cur.toNextSelection()) - { - XmlObject obj = cur.getObject(); - String parentName = obj.getDomNode().getParentNode().getNodeName(); - // ignore alternate content choices, we won't know anything about their namespaces - if (parentName.Equals("mc:Fallback") || parentName.Equals("x:dxfs") || parentName.contentEquals("dxfs")) - { - CTDxf dxf; - try - { - if (obj is CTDxf) { - dxf = (CTDxf)obj; - } else - { - dxf = CT_Dxf.Factory.parse(obj.newXMLStreamReader(), new XmlOptions().setDocumentType(CTDxf.type)); - } - if (dxf != null) dxfList.Add(dxf); - } - catch (XmlException e) - { - logger.log(POILogger.WARN, "Error parsing XSSFTableStyle", e); - } - } - } + List dxfList = dxfs.dxf; foreach (CT_TableStyleElement element in tableStyle.tableStyleElement) { - TableStyleType type = TableStyleType.valueOf(element.getType().toString()); + TableStyleType type = Enums.Parse(element.type.GetName()); DifferentialStyleProvider dstyle = null; - if (element.isSetDxfId()) + if (element.dxfIdSpecified) { - int idx = (int)element.getDxfId(); + int idx = (int)element.dxfId; CT_Dxf dxf; - dxf = dxfList.get(idx); + dxf = dxfList[idx]; int stripeSize = 0; - if (element.isSetSize()) stripeSize = (int)element.getSize(); + if (element.size!=0) stripeSize = (int)element.size; if (dxf != null) dstyle = new XSSFDxfStyleProvider(dxf, stripeSize, colorMap); } - elementMap.put(type, dstyle); + elementMap.Add(type, dstyle); } } public string Name From 3a3250f3bf66ed774aa2821da291c168459c64c2 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 15 Feb 2022 08:34:26 +0800 Subject: [PATCH 060/159] checkin --- .../EvaluationConditionalFormatRule.cs | 209 +++++++++++++++++- ooxml/XSSF/Model/StylesTable.cs | 5 + 2 files changed, 203 insertions(+), 11 deletions(-) diff --git a/main/SS/Formula/EvaluationConditionalFormatRule.cs b/main/SS/Formula/EvaluationConditionalFormatRule.cs index 4904fe3fa..98f43bad4 100644 --- a/main/SS/Formula/EvaluationConditionalFormatRule.cs +++ b/main/SS/Formula/EvaluationConditionalFormatRule.cs @@ -1,4 +1,5 @@ -using NPOI.SS.Formula.Eval; +using EnumsNET; +using NPOI.SS.Formula.Eval; using NPOI.SS.UserModel; using NPOI.SS.Util; using System; @@ -7,17 +8,17 @@ namespace NPOI.SS.Formula { - public enum OperatorEnum + public enum OperatorEnum:byte { - NO_COMPARISON, - BETWEEN, - NOT_BETWEEN, - EQUAL, - NOT_EQUAL, - GREATER_THAN, - LESS_THAN, - GREATER_OR_EQUAL, - LESS_OR_EQUAL + NO_COMPARISON=0, + BETWEEN=1, + NOT_BETWEEN=2, + EQUAL=3, + NOT_EQUAL=4, + GREATER_THAN=5, + LESS_THAN=6, + GREATER_OR_EQUAL=7, + LESS_OR_EQUAL=8 } public class EvaluationConditionalFormatRule: IComparable { @@ -155,6 +156,16 @@ public override int GetHashCode() return left.CompareTo(right) >= 0; } } + protected interface ValueFunction + { + + /** + * + * @param values + * @return the desired values for the rules implemented by the current instance + */ + List Evaluate(List values); + } private WorkbookEvaluator workbookEvaluator; private ISheet sheet; @@ -184,7 +195,29 @@ public override int GetHashCode() private ExcelNumberFormat numberFormat; // cached for performance, used to format numeric cells for string comparisons. See Bug #61764 for explanation private DecimalFormat decimalTextFormat; + public EvaluationConditionalFormatRule(WorkbookEvaluator workbookEvaluator, ISheet sheet, IConditionalFormatting formatting, int formattingIndex, IConditionalFormattingRule rule, int ruleIndex, CellRangeAddress[] regions) + { + this.workbookEvaluator = workbookEvaluator; + this.sheet = sheet; + this.formatting = formatting; + this.rule = rule; + this.formattingIndex = formattingIndex; + this.ruleIndex = ruleIndex; + + this.priority = rule.Priority; + + this.regions = regions; + formula1 = rule.Formula1; + formula2 = rule.Formula2; + + text = rule.Text; + lowerText = text == null ? null : text.ToLowerInvariant(); + numberFormat = rule.NumberFormat; + + @operator = (OperatorEnum)rule.ComparisonOperation; + type = rule.ConditionType; + } public ISheet Sheet { get { return sheet; } } public IConditionalFormatting Formatting { get { return formatting; } } public int FormattingIndex { get { return formattingIndex; } } @@ -253,6 +286,49 @@ private ValueEval UnwrapEval(ValueEval eval) } return comp; } + private bool CheckValue(ICell cell, CellRangeAddress region) + { + if (cell == null || DataValidationEvaluator.IsType(cell, CellType.Blank) + || DataValidationEvaluator.IsType(cell, CellType.Error) + || (DataValidationEvaluator.IsType(cell, CellType.String) + && string.IsNullOrEmpty(cell.StringCellValue) + ) + ) + { + return false; + } + + ValueEval eval = UnwrapEval(workbookEvaluator.Evaluate(rule.Formula1, ConditionalFormattingEvaluator.GetRef(cell), region)); + + String f2 = rule.Formula2; + ValueEval eval2 = BlankEval.instance; + if (f2 != null && f2.Length > 0) + { + eval2 = UnwrapEval(workbookEvaluator.Evaluate(f2, ConditionalFormattingEvaluator.GetRef(cell), region)); + } + + // we assume the cell has been evaluated, and the current formula value stored + if (DataValidationEvaluator.IsType(cell, CellType.Boolean) + && (eval == BlankEval.instance || eval is BoolEval) + && (eval2 == BlankEval.instance || eval2 is BoolEval) + ) { + return @operator.IsValid(cell.BooleanCellValue, eval == BlankEval.instance ? null : ((BoolEval)eval).BooleanValue, eval2 == BlankEval.instance ? null : ((BoolEval)eval2).BooleanValue); + } + if (DataValidationEvaluator.IsType(cell, CellType.Numeric) + && (eval == BlankEval.instance || eval is NumberEval ) + && (eval2 == BlankEval.instance || eval2 is NumberEval) + ) { + return @operator.IsValid(cell.NumericCellValue, eval == BlankEval.instance ? null : ((NumberEval)eval).NumberValue, eval2 == BlankEval.instance ? null : ((NumberEval)eval2).NumberValue); + } + if (DataValidationEvaluator.IsType(cell, CellType.String) + && (eval == BlankEval.instance || eval is StringEval ) + && (eval2 == BlankEval.instance || eval2 is StringEval) + ) { + return @operator.IsValid(cell.StringCellValue, eval == BlankEval.instance ? null : ((StringEval)eval).StringValue, eval2 == BlankEval.instance ? null : ((StringEval)eval2).StringValue); + } + + return @operator.IsValidForIncompatibleTypes(); + } private bool CheckFormula(CellReference reference, CellRangeAddress region) { ValueEval comp = UnwrapEval(workbookEvaluator.Evaluate(rule.Formula1, reference, region)); @@ -274,6 +350,117 @@ private bool CheckFormula(CellReference reference, CellRangeAddress region) } return false; // anything else is false, such as text } + internal bool Matches(CellReference reference) + { + // first check that it is in one of the regions defined for this format + CellRangeAddress region = null; + foreach (CellRangeAddress r in regions) + { + if (r.IsInRange(reference)) + { + region = r; + break; + } + } + + if (region == null) + { + // cell not in range of this rule + return false; + } + + ConditionType ruleType = this.Rule.ConditionType; + + // these rules apply to all cells in a region. Specific condition criteria + // may specify no special formatting for that value partition, but that's display logic + if (ruleType.Equals(ConditionType.ColorScale) + || ruleType.Equals(ConditionType.DataBar) + || ruleType.Equals(ConditionType.IconSet)) + { + return true; + } + + ICell cell = null; + IRow row = sheet.GetRow(reference.Row); + if (row != null) + { + cell = row.GetCell(reference.Col); + } + + if (ruleType.Equals(ConditionType.CellValueIs)) + { + // undefined cells never match a VALUE_IS condition + if (cell == null) return false; + return CheckValue(cell, region); + } + if (ruleType.Equals(ConditionType.Formula)) + { + return CheckFormula(reference, region); + } + //if (ruleType.Equals(ConditionType.Filter)) + //{ + // return CheckFilter(cell, reference, region); + //} + + // TODO: anything else, we don't handle yet, such as top 10 + return false; + } + private List GetMeaningfulValues(CellRangeAddress region, bool withText, ValueFunction func) + { + if (meaningfulRegionValues.ContainsKey(region)) + { + return meaningfulRegionValues[region]; + } + List values = meaningfulRegionValues[region]; + + List allValues = new List((region.LastColumn - region.FirstColumn + 1) * (region.LastRow - region.FirstRow + 1)); + + for (int r = region.FirstRow; r <= region.LastRow; r++) + { + IRow row = sheet.GetRow(r); + if (row == null) + { + continue; + } + for (int c = region.FirstColumn; c <= region.LastColumn; c++) + { + ICell cell = row.GetCell(c); + ValueAndFormat cv = GetCellValue(cell); + if (withText || cv.IsNumber) + { + allValues.Add(cv); + } + } + } + + values = func.Evaluate(allValues); + meaningfulRegionValues.Add(region, values); + + return values; + } + private ValueAndFormat GetCellValue(ICell cell) + { + if (cell != null) + { + String format = cell.CellStyle.GetDataFormatString(); + CellType type = cell.CellType; + if (type == CellType.Formula) + { + type = cell.CachedFormulaResultType; + } + switch (type) + { + case CellType.Numeric: + return new ValueAndFormat(cell.NumericCellValue, format, decimalTextFormat); + case CellType.String: + case CellType.Boolean: + return new ValueAndFormat(cell.StringCellValue, format); + default: + break; + } + } + return new ValueAndFormat("", ""); + } public override int GetHashCode() { int hash = sheet.SheetName.GetHashCode(); diff --git a/ooxml/XSSF/Model/StylesTable.cs b/ooxml/XSSF/Model/StylesTable.cs index 7469b1bc7..09567eccb 100644 --- a/ooxml/XSSF/Model/StylesTable.cs +++ b/ooxml/XSSF/Model/StylesTable.cs @@ -933,6 +933,11 @@ public XSSFFont FindFont(bool bold, short color, short fontHeight, String name, } return null; } + + public IIndexedColorMap GetIndexedColors() + { + return indexedColors; + } } } From c10bbd29a0bfdfc651a0ff03e116fb028cb2ab0b Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 16 Feb 2022 08:43:47 +0800 Subject: [PATCH 061/159] Update EvaluationConditionalFormatRule.cs --- .../EvaluationConditionalFormatRule.cs | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/main/SS/Formula/EvaluationConditionalFormatRule.cs b/main/SS/Formula/EvaluationConditionalFormatRule.cs index 98f43bad4..2846f0d2d 100644 --- a/main/SS/Formula/EvaluationConditionalFormatRule.cs +++ b/main/SS/Formula/EvaluationConditionalFormatRule.cs @@ -20,6 +20,54 @@ public enum OperatorEnum:byte GREATER_OR_EQUAL=7, LESS_OR_EQUAL=8 } + public class OperatorEnumHelper + { + public static bool IsValid(OperatorEnum @operator, object cellValue, object v1, object v2) { + switch(@operator) + { + case OperatorEnum.NO_COMPARISON: + return false; + case OperatorEnum.BETWEEN: + if (cellValue is Double) + { + // use zero for null + double n1 = v1==null? 0:(double)v1; + double n2 = v2 == null ? 0 : (double)v2; + return (double)cellValue - n1 >= 0 && (double)cellValue - n2 <= 0; + } + else if (cellValue is String) + { + String n1 = v1 == null ? "" : (String)v1; + String n2 = v2 == null ? "" : (String)v2; + return string.Compare((String)cellValue, n1, true) >= 0 && string.Compare((String)cellValue, n2, true) <= 0; + } + else if (cellValue is Boolean) + return false; + return false; // just in case - not a typical possibility + case OperatorEnum.EQUAL: + break; + case OperatorEnum.NOT_EQUAL: + break; + case OperatorEnum.GREATER_THAN: + break; + case OperatorEnum.LESS_THAN: + break; + case OperatorEnum.GREATER_OR_EQUAL: + break; + case OperatorEnum.LESS_OR_EQUAL: + break; + } + + + } + public static bool IsValidForIncompatibleTypes(OperatorEnum @operator) + { + if (@operator == OperatorEnum.NOT_BETWEEN || @operator == OperatorEnum.NOT_EQUAL) + return true; + else + return false; + } + } public class EvaluationConditionalFormatRule: IComparable { /** @@ -306,28 +354,27 @@ private bool CheckValue(ICell cell, CellRangeAddress region) { eval2 = UnwrapEval(workbookEvaluator.Evaluate(f2, ConditionalFormattingEvaluator.GetRef(cell), region)); } - // we assume the cell has been evaluated, and the current formula value stored if (DataValidationEvaluator.IsType(cell, CellType.Boolean) && (eval == BlankEval.instance || eval is BoolEval) && (eval2 == BlankEval.instance || eval2 is BoolEval) ) { - return @operator.IsValid(cell.BooleanCellValue, eval == BlankEval.instance ? null : ((BoolEval)eval).BooleanValue, eval2 == BlankEval.instance ? null : ((BoolEval)eval2).BooleanValue); + return OperatorEnumHelper.IsValid(@operator, cell.BooleanCellValue, eval == BlankEval.instance ? (bool?)null : ((BoolEval)eval).BooleanValue, eval2 == BlankEval.instance ? (bool?)null : ((BoolEval)eval2).BooleanValue); } if (DataValidationEvaluator.IsType(cell, CellType.Numeric) && (eval == BlankEval.instance || eval is NumberEval ) && (eval2 == BlankEval.instance || eval2 is NumberEval) ) { - return @operator.IsValid(cell.NumericCellValue, eval == BlankEval.instance ? null : ((NumberEval)eval).NumberValue, eval2 == BlankEval.instance ? null : ((NumberEval)eval2).NumberValue); + return OperatorEnumHelper.IsValid(@operator, cell.NumericCellValue, eval==BlankEval.instance ? (double?)null : ((NumberEval)eval).NumberValue, eval2 == BlankEval.instance ? (double?)null : ((NumberEval)eval2).NumberValue); } if (DataValidationEvaluator.IsType(cell, CellType.String) && (eval == BlankEval.instance || eval is StringEval ) && (eval2 == BlankEval.instance || eval2 is StringEval) ) { - return @operator.IsValid(cell.StringCellValue, eval == BlankEval.instance ? null : ((StringEval)eval).StringValue, eval2 == BlankEval.instance ? null : ((StringEval)eval2).StringValue); + return OperatorEnumHelper.IsValid(@operator, cell.StringCellValue, eval == BlankEval.instance ? null : ((StringEval)eval).StringValue, eval2 == BlankEval.instance ? null : ((StringEval)eval2).StringValue); } - return @operator.IsValidForIncompatibleTypes(); + return OperatorEnumHelper.IsValidForIncompatibleTypes(@operator); } private bool CheckFormula(CellReference reference, CellRangeAddress region) { From 831587452f92f55b54e914f6bcdcab0360162aed Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 17 Feb 2022 07:10:41 +0800 Subject: [PATCH 062/159] Update EvaluationConditionalFormatRule.cs --- .../EvaluationConditionalFormatRule.cs | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/main/SS/Formula/EvaluationConditionalFormatRule.cs b/main/SS/Formula/EvaluationConditionalFormatRule.cs index 2846f0d2d..f60792e5a 100644 --- a/main/SS/Formula/EvaluationConditionalFormatRule.cs +++ b/main/SS/Formula/EvaluationConditionalFormatRule.cs @@ -23,6 +23,8 @@ public enum OperatorEnum:byte public class OperatorEnumHelper { public static bool IsValid(OperatorEnum @operator, object cellValue, object v1, object v2) { + if (cellValue == null) + return false; switch(@operator) { case OperatorEnum.NO_COMPARISON: @@ -43,13 +45,58 @@ public class OperatorEnumHelper } else if (cellValue is Boolean) return false; - return false; // just in case - not a typical possibility + return false; case OperatorEnum.EQUAL: - break; + if (v1 == null) + return false; + if (cellValue is Double) + { + return (double)cellValue >= 0; + } + else if (cellValue is String) + { + return string.Compare((String)cellValue, (String)v1, true) >= 0; + } + else if (cellValue is Boolean) + { + bool n1 = (bool)v1; + return (bool)cellValue==n1; + } + return false; case OperatorEnum.NOT_EQUAL: - break; + if (v1 == null) + return true; + if (cellValue is String) + { + String n1 = (String)v1; + return string.Compare((String)cellValue, n1, true) >= 0; + } + else if (cellValue is Boolean) + { + bool n1 = (bool)v1; + return (bool)cellValue != n1; + } + return false; case OperatorEnum.GREATER_THAN: - break; + if (cellValue is Double) + { + // use zero for null + double n1 = v1 == null ? 0 : (double)v1; + return (double)cellValue>= n1; + } + else if (cellValue is String) + { + String n1 = v1 == null ? "" : (String)v1; + return string.Compare((String)cellValue, n1, true) >= 0; + } + else if (cellValue is Boolean) + { + if (v1 == null) + return true; + bool n1 = (bool)v1; + return ((bool)cellValue).CompareTo(n1)>0; + } + return false; case OperatorEnum.LESS_THAN: break; case OperatorEnum.GREATER_OR_EQUAL: From d030e12906d3d82bace3889f1a0b14327963168c Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 20 Feb 2022 10:05:28 +0800 Subject: [PATCH 063/159] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 783608f46..cdef5bb68 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ Get Started with NPOI Replace DotnetCore.NPOI with NPOI ASAP =========== +NPOI NEVER joins China NCC (.NET Core Community) group. They are cheating. The readme.md in Dotnetcore.npoi repo is full of lies. What they want to do is just to destory NPOI since they cannot make use of reputation of this component any more. That's why I'm always saying they are evil. The whole NCC group is evil. +NPOI从未加入过中国NCC开源组织,他们在欺骗公众!Dotnetcore.npoi的readme.e完全是诽谤,一堆谎言。他们想做的无非就是想毁掉NPOI,因为他们不能再用NPOI来行骗了。这也是我为什么一直说他们很邪恶,整个NCC组织就是一个邪教。 + [Stop using Dotnetcore/NPOI nuget package. It’s obsolete!](https://tonyqus.medium.com/stop-using-dotnetcore-npoi-nuget-package-its-too-obsolete-6d0aeedb3319) [The real history of Dotnetcore.NPOI](https://tonyqus.medium.com/the-real-history-of-dotnetcore-npoi-999bb5e140c7) From fa6e492e83d02ad968292cb235322e4dc8dcd60d Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 20 Feb 2022 10:05:47 +0800 Subject: [PATCH 064/159] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cdef5bb68..f397b64f1 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Get Started with NPOI Replace DotnetCore.NPOI with NPOI ASAP =========== NPOI NEVER joins China NCC (.NET Core Community) group. They are cheating. The readme.md in Dotnetcore.npoi repo is full of lies. What they want to do is just to destory NPOI since they cannot make use of reputation of this component any more. That's why I'm always saying they are evil. The whole NCC group is evil. + NPOI从未加入过中国NCC开源组织,他们在欺骗公众!Dotnetcore.npoi的readme.e完全是诽谤,一堆谎言。他们想做的无非就是想毁掉NPOI,因为他们不能再用NPOI来行骗了。这也是我为什么一直说他们很邪恶,整个NCC组织就是一个邪教。 [Stop using Dotnetcore/NPOI nuget package. It’s obsolete!](https://tonyqus.medium.com/stop-using-dotnetcore-npoi-nuget-package-its-too-obsolete-6d0aeedb3319) From a31cfde589eea443b6b081d50203ff2942039af4 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 20 Feb 2022 14:33:09 +0800 Subject: [PATCH 065/159] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f397b64f1..33c75d568 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Replace DotnetCore.NPOI with NPOI ASAP =========== NPOI NEVER joins China NCC (.NET Core Community) group. They are cheating. The readme.md in Dotnetcore.npoi repo is full of lies. What they want to do is just to destory NPOI since they cannot make use of reputation of this component any more. That's why I'm always saying they are evil. The whole NCC group is evil. -NPOI从未加入过中国NCC开源组织,他们在欺骗公众!Dotnetcore.npoi的readme.e完全是诽谤,一堆谎言。他们想做的无非就是想毁掉NPOI,因为他们不能再用NPOI来行骗了。这也是我为什么一直说他们很邪恶,整个NCC组织就是一个邪教。 +NPOI从未加入过中国NCC开源组织,他们在欺骗公众!Dotnetcore.npoi的readme.md完全是诽谤,一堆谎言。他们想做的无非就是想毁掉NPOI,因为他们不能再用NPOI来行骗了。这也是我为什么一直说他们很邪恶,整个NCC组织就是一个邪教。 [Stop using Dotnetcore/NPOI nuget package. It’s obsolete!](https://tonyqus.medium.com/stop-using-dotnetcore-npoi-nuget-package-its-too-obsolete-6d0aeedb3319) From ec2fd3565624d9ac35767412ef2c74b82a6f6f37 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 20 Feb 2022 14:57:21 +0800 Subject: [PATCH 066/159] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 33c75d568..9cae821f7 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,12 @@ This project is the .NET version of POI Java project. With NPOI, you can read/wr [Who is using NPOI?](https://github.com/nissl-lab/npoi/issues/705) +Telegram User Group +================ +Join us on telegram: https://t.me/npoidevs + +NOTE: QQ or wechat is not recommended. + Get Started with NPOI ============ [Getting Started with NPOI](https://github.com/nissl-lab/npoi/wiki/Getting-Started-with-NPOI) @@ -32,12 +38,6 @@ NPOI从未加入过中国NCC开源组织,他们在欺骗公众!Dotnetcore.np [The real history of Dotnetcore.NPOI](https://tonyqus.medium.com/the-real-history-of-dotnetcore-npoi-999bb5e140c7) -Telegram User Group -================ -Join us on telegram: https://t.me/npoidevs - -NOTE: QQ or wechat is not recommended. - Advantage of NPOI ================= a. It's totally free to use From 45c96d31e46d63a67bedf7d44a64e4df97997580 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 20 Feb 2022 16:31:59 +0800 Subject: [PATCH 067/159] implement XSSFBuiltinTableStyleEnum and XSSFBuiltinTableStyle.Init --- .../EvaluationConditionalFormatRule.cs | 295 +- ooxml/NPOI.OOXML.Core.csproj | 8 + ooxml/XSSF/Model/StylesTable.cs | 9 +- ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs | 364 +- ooxml/XSSF/UserModel/presetTableStyles.xml | 18070 ++++++++++++++++ 5 files changed, 18698 insertions(+), 48 deletions(-) create mode 100644 ooxml/XSSF/UserModel/presetTableStyles.xml diff --git a/main/SS/Formula/EvaluationConditionalFormatRule.cs b/main/SS/Formula/EvaluationConditionalFormatRule.cs index f60792e5a..cdc1b74b8 100644 --- a/main/SS/Formula/EvaluationConditionalFormatRule.cs +++ b/main/SS/Formula/EvaluationConditionalFormatRule.cs @@ -1,5 +1,6 @@ using EnumsNET; using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; using NPOI.SS.UserModel; using NPOI.SS.Util; using System; @@ -55,7 +56,7 @@ public class OperatorEnumHelper } else if (cellValue is String) { - return string.Compare((String)cellValue, (String)v1, true) >= 0; + return string.Compare((String)cellValue, (String)v1, true) == 0; } else if (cellValue is Boolean) { @@ -69,7 +70,7 @@ public class OperatorEnumHelper if (cellValue is String) { String n1 = (String)v1; - return string.Compare((String)cellValue, n1, true) >= 0; + return string.Compare((String)cellValue, n1, true) != 0; } else if (cellValue is Boolean) { @@ -82,12 +83,12 @@ public class OperatorEnumHelper { // use zero for null double n1 = v1 == null ? 0 : (double)v1; - return (double)cellValue>= n1; + return (double)cellValue> n1; } else if (cellValue is String) { String n1 = v1 == null ? "" : (String)v1; - return string.Compare((String)cellValue, n1, true) >= 0; + return string.Compare((String)cellValue, n1, true) > 0; } else if (cellValue is Boolean) { @@ -98,13 +99,70 @@ public class OperatorEnumHelper } return false; case OperatorEnum.LESS_THAN: - break; + if (cellValue is Double) + { + // use zero for null + double n1 = v1 == null ? 0 : (double)v1; + return (double)cellValue= n1; + } + else if (cellValue is String) + { + if (v1 == null) + return true; + return string.Compare((String)cellValue, (String)v1, true) >= 0; + } + else if (cellValue is Boolean) + { + if (v1 == null) + return false; + bool n1 = (bool)v1; + return ((bool)cellValue).CompareTo(n1) >= 0; + } + return false; case OperatorEnum.LESS_OR_EQUAL: - break; + if (cellValue is Double) + { + // use zero for null + double n1 = v1 == null ? 0 : (double)v1; + return (double)cellValue <= n1; + } + else if (cellValue is String) + { + if (v1 == null) + return false; + return string.Compare((String)cellValue, (String)v1, true) <= 0; + } + else if (cellValue is Boolean) + { + if (v1 == null) + return false; + bool n1 = (bool)v1; + return ((bool)cellValue).CompareTo(n1) <= 0; + } + return false; } - + return false; } public static bool IsValidForIncompatibleTypes(OperatorEnum @operator) @@ -251,16 +309,6 @@ public override int GetHashCode() return left.CompareTo(right) >= 0; } } - protected interface ValueFunction - { - - /** - * - * @param values - * @return the desired values for the rules implemented by the current instance - */ - List Evaluate(List values); - } private WorkbookEvaluator workbookEvaluator; private ISheet sheet; @@ -491,22 +539,214 @@ internal bool Matches(CellReference reference) { return CheckFormula(reference, region); } - //if (ruleType.Equals(ConditionType.Filter)) - //{ - // return CheckFilter(cell, reference, region); - //} - + if (ruleType.Equals(ConditionType.Filter)) + { + return CheckFilter(cell, reference, region); + } // TODO: anything else, we don't handle yet, such as top 10 return false; } - private List GetMeaningfulValues(CellRangeAddress region, bool withText, ValueFunction func) + private bool CheckFilter(ICell cell, CellReference reference, CellRangeAddress region) + { + ConditionFilterType? filterType = rule.ConditionFilterType; + if (filterType == null) + { + return false; + } + ValueAndFormat cv = GetCellValue(cell); + switch (filterType) + { + case ConditionFilterType.FILTER: + return false; // we don't evaluate HSSF filters yet + case ConditionFilterType.TOP_10: + // from testing, Excel only operates on numbers and dates (which are stored as numbers) in the range. + // numbers stored as text are ignored, but numbers formatted as text are treated as numbers. + + return GetMeaningfulValues(region, true, (allValues)=> { + IConditionFilterData fc = rule.FilterConfiguration; + + if (!fc.Bottom) + { + allValues.Sort(); + allValues.Reverse(); + } + else + { + allValues.Sort(); + } + + int limit = (int)fc.Rank; + if (fc.Percent) + { + limit = allValues.Count * limit / 100; + } + if (allValues.Count <= limit) + { + return allValues; + } + return allValues.GetRange(0, limit); + }).Contains(cv); + case ConditionFilterType.UNIQUE_VALUES: + // Per Excel help, "duplicate" means matching value AND format + // https://support.office.com/en-us/article/Filter-for-unique-values-or-remove-duplicate-values-ccf664b0-81d6-449b-bbe1-8daaec1e83c2 + return GetMeaningfulValues(region, true, (allValues) => { + allValues.Sort(); + List unique = new List(); + + for (int i = 0; i < allValues.Count; i++) + { + ValueAndFormat v = allValues[i]; + // skip this if the current value matches the next one, or is the last one and matches the previous one + if ((i < allValues.Count - 1 && v.Equals(allValues[i + 1])) || (i > 0 && i == allValues.Count - 1 && v.Equals(allValues[i - 1]))) + { + // current value matches next value, skip both + i++; + continue; + } + unique.Add(v); + } + + return unique; + }).Contains(cv); + case ConditionFilterType.DUPLICATE_VALUES: + // Per Excel help, "duplicate" means matching value AND format + // https://support.office.com/en-us/article/Filter-for-unique-values-or-remove-duplicate-values-ccf664b0-81d6-449b-bbe1-8daaec1e83c2 + return GetMeaningfulValues(region, true, (allValues) => { + allValues.Sort(); + List dup = new List(); + + for (int i = 0; i < allValues.Count; i++) + { + ValueAndFormat v = allValues[i]; + // skip this if the current value matches the next one, or is the last one and matches the previous one + if ((i < allValues.Count - 1 && v.Equals(allValues[i + 1])) || (i > 0 && i == allValues.Count - 1 && v.Equals(allValues[i - 1]))) + { + // current value matches next value, add one + dup.Add(v); + i++; + } + } + return dup; + }).Contains(cv); + case ConditionFilterType.ABOVE_AVERAGE: + // from testing, Excel only operates on numbers and dates (which are stored as numbers) in the range. + // numbers stored as text are ignored, but numbers formatted as text are treated as numbers. + + IConditionFilterData conf = rule.FilterConfiguration; + + // actually ordered, so iteration order is predictable + List values = GetMeaningfulValues(region, false, (allValues) => { + double total = 0; + ValueEval[] pop = new ValueEval[allValues.Count]; + for (int i = 0; i < allValues.Count; i++) + { + ValueAndFormat v = allValues[i]; + total += (double)v.Value; + pop[i] = new NumberEval((double)v.Value); + } + + List avgSet = new List(1); + avgSet.Add(new ValueAndFormat(allValues.Count == 0 ? 0 : total / allValues.Count, null, decimalTextFormat)); + + double stdDev2 = allValues.Count <= 1 ? 0 : ((NumberEval)AggregateFunction.STDEV.Evaluate(pop, 0, 0)).NumberValue; + avgSet.Add(new ValueAndFormat(stdDev2, null, decimalTextFormat)); + return avgSet; + }); + double? val = cv.IsNumber ? cv.Value : null; + if (val == null) + { + return false; + } + + double avg = (double)values[0].Value; + double stdDev = (double)values[1].Value; + + /* + * use StdDev, aboveAverage, equalAverage to find: + * comparison value + * operator type + */ + + double comp = conf.StdDev > 0 ? (avg + (conf.AboveAverage ? 1 : -1) * stdDev * conf.StdDev) : avg; + + OperatorEnum op; + if (conf.AboveAverage) + { + if (conf.EqualAverage) + { + op = OperatorEnum.GREATER_OR_EQUAL; + } + else + { + op = OperatorEnum.GREATER_THAN; + } + } + else + { + if (conf.EqualAverage) + { + op = OperatorEnum.LESS_OR_EQUAL; + } + else + { + op = OperatorEnum.LESS_THAN; + } + } + return OperatorEnumHelper.IsValid(op, val, comp, null); + case ConditionFilterType.CONTAINS_TEXT: + // implemented both by a cfRule "text" attribute and a formula. Use the text. + return text == null ? false : cv.ToString().ToLowerInvariant().Contains(lowerText); + case ConditionFilterType.NOT_CONTAINS_TEXT: + // implemented both by a cfRule "text" attribute and a formula. Use the text. + return text == null ? true : !cv.ToString().ToLowerInvariant().Contains(lowerText); + case ConditionFilterType.BEGINS_WITH: + // implemented both by a cfRule "text" attribute and a formula. Use the text. + return cv.ToString().ToLowerInvariant().StartsWith(lowerText); + case ConditionFilterType.ENDS_WITH: + // implemented both by a cfRule "text" attribute and a formula. Use the text. + return cv.ToString().ToLowerInvariant().EndsWith(lowerText); + case ConditionFilterType.CONTAINS_BLANKS: + try + { + String v = cv.String; + // see TextFunction.TRIM for implementation + return v == null || v.Trim().Length == 0; + } + catch (Exception e) + { + // not a valid string value, and not a blank cell (that's checked earlier) + return false; + } + case ConditionFilterType.NOT_CONTAINS_BLANKS: + try + { + String v = cv.String; + // see TextFunction.TRIM for implementation + return v != null && v.Trim().Length > 0; + } + catch (Exception e) + { + // not a valid string value, but not blank + return true; + } + case ConditionFilterType.CONTAINS_ERRORS: + return cell != null && DataValidationEvaluator.IsType(cell, CellType.Error); + case ConditionFilterType.NOT_CONTAINS_ERRORS: + return cell == null || !DataValidationEvaluator.IsType(cell, CellType.Error); + case ConditionFilterType.TIME_PERIOD: + // implemented both by a cfRule "text" attribute and a formula. Use the formula. + return CheckFormula(reference, region); + default: + return false; + } + } + private List GetMeaningfulValues(CellRangeAddress region, bool withText, Func, List> func) { if (meaningfulRegionValues.ContainsKey(region)) { return meaningfulRegionValues[region]; } - List values = meaningfulRegionValues[region]; - + List values = new List(); List allValues = new List((region.LastColumn - region.FirstColumn + 1) * (region.LastRow - region.FirstRow + 1)); for (int r = region.FirstRow; r <= region.LastRow; r++) @@ -526,8 +766,7 @@ private List GetMeaningfulValues(CellRangeAddress region, bool w } } } - - values = func.Evaluate(allValues); + values = func(allValues); meaningfulRegionValues.Add(region, values); return values; diff --git a/ooxml/NPOI.OOXML.Core.csproj b/ooxml/NPOI.OOXML.Core.csproj index 806d95d45..4f3ea4af1 100644 --- a/ooxml/NPOI.OOXML.Core.csproj +++ b/ooxml/NPOI.OOXML.Core.csproj @@ -30,6 +30,14 @@ + + + + + + + + diff --git a/ooxml/XSSF/Model/StylesTable.cs b/ooxml/XSSF/Model/StylesTable.cs index 09567eccb..88b151c9a 100644 --- a/ooxml/XSSF/Model/StylesTable.cs +++ b/ooxml/XSSF/Model/StylesTable.cs @@ -49,7 +49,7 @@ public class StylesTable : POIXMLDocumentPart private List xfs = new List(); private List dxfs = new List(); - private Dictionary tableStyles = new Dictionary(); + private Dictionary tableStyles = new Dictionary(); private IIndexedColorMap indexedColors = new DefaultIndexedColorMap(); /** * The first style id available for use as a custom style @@ -156,9 +156,10 @@ public ITableStyle GetTableStyle(String name) if (name == null) return null; try { - return XSSFBuiltinTableStyle.ValueOf(name).getStyle(); + return XSSFBuiltinTableStyle.GetStyle( + (XSSFBuiltinTableStyleEnum)Enum.Parse(typeof(XSSFBuiltinTableStyleEnum), name)); } - catch (ArgumentException e) + catch { return GetExplicitTableStyle(name); } @@ -185,7 +186,7 @@ public void EnsureThemesTable() * @throws IOException if an error occurs while Reading. */ - protected void ReadFrom(XmlDocument xmldoc) + internal void ReadFrom(XmlDocument xmldoc) { try { diff --git a/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs index a8c9bed16..61f6bae61 100644 --- a/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs +++ b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs @@ -1,42 +1,374 @@ -using NPOI.SS.UserModel; +using EnumsNET; +using NPOI.SS.UserModel; +using NPOI.XSSF.Model; using System; using System.Collections.Generic; +using System.Linq; using System.Text; +using System.Xml; namespace NPOI.OOXML.XSSF.UserModel { + public enum XSSFBuiltinTableStyleEnum:int + { + TableStyleDark1, + + TableStyleDark2, + + TableStyleDark3, + + TableStyleDark4, + + TableStyleDark5, + + TableStyleDark6, + + TableStyleDark7, + + TableStyleDark8, + + TableStyleDark9, + + TableStyleDark10, + + TableStyleDark11, + + TableStyleLight1, + + TableStyleLight2, + + TableStyleLight3, + + TableStyleLight4, + + TableStyleLight5, + + TableStyleLight6, + + TableStyleLight7, + + TableStyleLight8, + + TableStyleLight9, + + TableStyleLight10, + + TableStyleLight11, + + TableStyleLight12, + + TableStyleLight13, + + TableStyleLight14, + + TableStyleLight15, + + TableStyleLight16, + + TableStyleLight17, + + TableStyleLight18, + + TableStyleLight19, + + TableStyleLight20, + + TableStyleLight21, + + TableStyleMedium1, + + TableStyleMedium2, + + TableStyleMedium3, + + TableStyleMedium4, + + TableStyleMedium5, + + TableStyleMedium6, + + TableStyleMedium7, + + TableStyleMedium8, + + TableStyleMedium9, + + TableStyleMedium10, + + TableStyleMedium11, + + TableStyleMedium12, + + TableStyleMedium13, + + TableStyleMedium14, + + TableStyleMedium15, + + TableStyleMedium16, + + TableStyleMedium17, + + TableStyleMedium18, + + TableStyleMedium19, + + TableStyleMedium20, + + TableStyleMedium21, + + TableStyleMedium22, + + TableStyleMedium23, + + TableStyleMedium24, + + TableStyleMedium25, + + TableStyleMedium26, + + TableStyleMedium27, + + TableStyleMedium28, + + PivotStyleMedium1, + + PivotStyleMedium2, + + PivotStyleMedium3, + + PivotStyleMedium4, + + PivotStyleMedium5, + + PivotStyleMedium6, + + PivotStyleMedium7, + + PivotStyleMedium8, + + PivotStyleMedium9, + + PivotStyleMedium10, + + PivotStyleMedium11, + + PivotStyleMedium12, + + PivotStyleMedium13, + + PivotStyleMedium14, + + PivotStyleMedium15, + + PivotStyleMedium16, + + PivotStyleMedium17, + + PivotStyleMedium18, + + PivotStyleMedium19, + + PivotStyleMedium20, + + PivotStyleMedium21, + + PivotStyleMedium22, + + PivotStyleMedium23, + + PivotStyleMedium24, + + PivotStyleMedium25, + + PivotStyleMedium26, + + PivotStyleMedium27, + + PivotStyleMedium28, + + PivotStyleLight1, + + PivotStyleLight2, + + PivotStyleLight3, + + PivotStyleLight4, + + PivotStyleLight5, + + PivotStyleLight6, + + PivotStyleLight7, + + PivotStyleLight8, + + PivotStyleLight9, + + PivotStyleLight10, + + PivotStyleLight11, + + PivotStyleLight12, + + PivotStyleLight13, + + PivotStyleLight14, + + PivotStyleLight15, + + PivotStyleLight16, + + PivotStyleLight17, + + PivotStyleLight18, + + PivotStyleLight19, + + PivotStyleLight20, + + PivotStyleLight21, + + PivotStyleLight22, + + PivotStyleLight23, + + PivotStyleLight24, + + PivotStyleLight25, + + PivotStyleLight26, + + PivotStyleLight27, + + PivotStyleLight28, + + PivotStyleDark1, + + PivotStyleDark2, + + PivotStyleDark3, + + PivotStyleDark4, + + PivotStyleDark5, + + PivotStyleDark6, + + PivotStyleDark7, + + PivotStyleDark8, + + PivotStyleDark9, + + PivotStyleDark10, + + PivotStyleDark11, + + PivotStyleDark12, + + PivotStyleDark13, + + PivotStyleDark14, + + PivotStyleDark15, + + PivotStyleDark16, + + PivotStyleDark17, + + PivotStyleDark18, + + PivotStyleDark19, + + PivotStyleDark20, + + PivotStyleDark21, + + PivotStyleDark22, + + PivotStyleDark23, + + PivotStyleDark24, + + PivotStyleDark25, + + PivotStyleDark26, + + PivotStyleDark27, + + PivotStyleDark28 + } public class XSSFBuiltinTableStyle { - private static Dictionary styleMap = new Dictionary(); - public ITableStyle GetStyle() + const string presetTableStylesResourceName= "NPOI.XSSF.UserModel.presetTableStyles.xml"; + private static Dictionary styleMap = new Dictionary(); + public static ITableStyle GetStyle(XSSFBuiltinTableStyleEnum style) { Init(); - return styleMap[this]; + return styleMap[style]; } public static bool IsBuiltinStyle(ITableStyle style) { - if (style == null) return false; - try - { - XSSFBuiltinTableStyle.valueOf(style.Name); - return true; - } - catch (ArgumentException) - { + if (style == null) return false; + return Enums.GetNames().Any(x=>x==style.Name); + } + private static void Init() + { + if (styleMap.Count > 0) + return; + using (var xmlstream = typeof (XSSFBuiltinTableStyle).Assembly.GetManifestResourceStream(presetTableStylesResourceName)) + { + var xmlReader = new XmlTextReader(xmlstream); + var xmlDocument = new XmlDocument(); + xmlDocument.Load(xmlReader); + var node = xmlDocument.SelectSingleNode("presetTableStyles"); + foreach (XmlNode child in node.ChildNodes) + { + String styleName = child.Name; + if (Enum.TryParse(styleName, out XSSFBuiltinTableStyleEnum builtIn)) + { + continue; + } + var dxfsNode = child.SelectSingleNode("dxfs"); + var tableStyleNode = child.SelectSingleNode("tableStyles"); + StylesTable styles = new StylesTable(); + var styleXmlDocument = new XmlDocument(); + styleXmlDocument.LoadXml(StyleXML(dxfsNode, tableStyleNode)); + styles.ReadFrom(styleXmlDocument); + styleMap.Add(builtIn,new XSSFBuiltinTypeStyleStyle(builtIn, styles.GetExplicitTableStyle(styleName))); + } } } - protected class XSSFBuiltinTypeStyleStyle : ITableStyle + private static string StyleXML(XmlNode dxfsNode, XmlNode tableStyleNode) { + // built-ins doc uses 1-based dxf indexing, Excel uses 0 based. + // add a dummy node to adjust properly. + dxfsNode.InsertBefore(dxfsNode.OwnerDocument.CreateElement("dxf"), dxfsNode.FirstChild); + + StringBuilder sb = new StringBuilder(); + sb.Append("\n") + .Append("\n"); + sb.Append(dxfsNode.OuterXml); + sb.Append(tableStyleNode.OuterXml); + sb.Append(""); + return sb.ToString(); + } + protected class XSSFBuiltinTypeStyleStyle : ITableStyle { - private XSSFBuiltinTableStyle builtIn; + private XSSFBuiltinTableStyleEnum builtIn; private ITableStyle style; /** * @param builtIn * @param style */ - protected XSSFBuiltinTypeStyleStyle(XSSFBuiltinTableStyle builtIn, ITableStyle style) + internal XSSFBuiltinTypeStyleStyle(XSSFBuiltinTableStyleEnum builtIn, ITableStyle style) { this.builtIn = builtIn; this.style = style; @@ -54,7 +386,7 @@ public int Index { get { - return builtIn.ordinal(); + return (int)builtIn; } } diff --git a/ooxml/XSSF/UserModel/presetTableStyles.xml b/ooxml/XSSF/UserModel/presetTableStyles.xml new file mode 100644 index 000000000..f83f2e33c --- /dev/null +++ b/ooxml/XSSF/UserModel/presetTableStyles.xml @@ -0,0 +1,18070 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 88c218323736abb54c0034f8bba8bc68078609e9 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 21 Feb 2022 09:00:30 +0800 Subject: [PATCH 068/159] add CreateColumn --- OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs | 19 +++- main/SS/Util/AreaReference.cs | 54 +++++++++- ooxml/XSSF/UserModel/XSSFTable.cs | 105 ++++++++++++++++++- 3 files changed, 173 insertions(+), 5 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs b/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs index 617a57aec..a0ec37a64 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs @@ -198,7 +198,10 @@ internal void Write(StreamWriter sw) this.extLst.Write(sw, "extLst"); sw.Write(""); } - + public bool IsSetAutoFilter + { + get { return this.autoFilterField != null; } + } [XmlElement] public CT_AutoFilter autoFilter { @@ -626,6 +629,12 @@ internal CT_TableStyleInfo AddNewTableStyleInfo() this.tableStyleInfoField = new CT_TableStyleInfo(); return this.tableStyleInfoField; } + + public CT_TableColumns AddNewTableColumns() + { + this.tableColumnsField = new CT_TableColumns(); + return this.tableColumnsField; + } } [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main")] @@ -658,8 +667,6 @@ public static CT_TableColumns Parse(XmlNode node, XmlNamespaceManager namespaceM return ctObj; } - - internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); @@ -674,6 +681,12 @@ internal void Write(StreamWriter sw, string nodeName) } sw.Write(string.Format("", nodeName)); } + public CT_TableColumn InsertNewTableColumn(int columnIndex) + { + var newTableColumn = new CT_TableColumn(); + this.tableColumn.Insert(columnIndex,newTableColumn); + return newTableColumn; + } public void RemoveTableColumn(int columnIndex) { this.tableColumn.RemoveAt(columnIndex); diff --git a/main/SS/Util/AreaReference.cs b/main/SS/Util/AreaReference.cs index 08d68265e..e45b2ac82 100644 --- a/main/SS/Util/AreaReference.cs +++ b/main/SS/Util/AreaReference.cs @@ -105,7 +105,59 @@ public AreaReference(String reference, SpreadsheetVersion version) _isSingleCell = part0.Equals(part1); } } - + public AreaReference(CellReference topLeft, CellReference botRight, SpreadsheetVersion version) + { + _version = (null != version) ? version : DEFAULT_SPREADSHEET_VERSION; + bool swapRows = topLeft.Row > botRight.Row; + bool swapCols = topLeft.Col > botRight.Col; + if (swapRows || swapCols) + { + int firstRow; + int lastRow; + int firstColumn; + int lastColumn; + bool firstRowAbs; + bool lastRowAbs; + bool firstColAbs; + bool lastColAbs; + if (swapRows) + { + firstRow = botRight.Row; + firstRowAbs = botRight.IsRowAbsolute; + lastRow = topLeft.Row; + lastRowAbs = topLeft.IsRowAbsolute; + } + else + { + firstRow = topLeft.Row; + firstRowAbs = topLeft.IsRowAbsolute; + lastRow = botRight.Row; + lastRowAbs = botRight.IsRowAbsolute; + } + if (swapCols) + { + firstColumn = botRight.Col; + firstColAbs = botRight.IsColAbsolute; + lastColumn = topLeft.Col; + lastColAbs = topLeft.IsColAbsolute; + } + else + { + firstColumn = topLeft.Col; + firstColAbs = topLeft.IsColAbsolute; + lastColumn = botRight.Col; + lastColAbs = botRight.IsColAbsolute; + } + _firstCell = new CellReference(firstRow, firstColumn, firstRowAbs, firstColAbs); + _lastCell = new CellReference(lastRow, lastColumn, lastRowAbs, lastColAbs); + } + else + { + _firstCell = topLeft; + _lastCell = botRight; + } + _isSingleCell = false; + } private static bool IsPlainColumn(String refPart) { for (int i = refPart.Length - 1; i >= 0; i--) diff --git a/ooxml/XSSF/UserModel/XSSFTable.cs b/ooxml/XSSF/UserModel/XSSFTable.cs index 0d34b741d..c705a84ea 100644 --- a/ooxml/XSSF/UserModel/XSSFTable.cs +++ b/ooxml/XSSF/UserModel/XSSFTable.cs @@ -28,6 +28,8 @@ using NPOI.SS.UserModel; using System.Text.RegularExpressions; using System.Globalization; +using NPOI.SS; +using System.Linq; namespace NPOI.XSSF.UserModel { @@ -232,11 +234,112 @@ public String Name ctTable.name = value; } } + public XSSFTableColumn CreateColumn(String columnName) + { + return CreateColumn(columnName, this.ColumnCount); + } + public XSSFTableColumn CreateColumn(String columnName, int columnIndex) + { + + int columnCount = ColumnCount; + if (columnIndex < 0 || columnIndex > columnCount) + { + throw new ArgumentException("Column index out of bounds"); + } + + // Ensure we have Table Columns + CT_TableColumns columns = ctTable.tableColumns; + if (columns == null) + { + columns = ctTable.AddNewTableColumns(); + } + + // check if name is unique and calculate unique column id + long nextColumnId = 0; + foreach (XSSFTableColumn tableColumn in this.GetColumns()) + { + if (columnName != null && columnName.Equals(tableColumn.Name,StringComparison.InvariantCultureIgnoreCase)) + { + throw new ArgumentException("Column '" + columnName + + "' already exists. Column names must be unique per table."); + } + nextColumnId = Math.Max(nextColumnId, tableColumn.Id); + } + // Bug #62740, the logic was just re-using the existing max ID, not incrementing beyond it. + nextColumnId++; + + // Add the new Column + CT_TableColumn column = columns.InsertNewTableColumn(columnIndex); + columns.count = columns.count; + + column.id = (uint)nextColumnId; + if (columnName != null) + { + column.name = columnName; + } + else + { + column.name = "Column " + nextColumnId; + } + if (ctTable.@ref != null) + { + // calculate new area + int newColumnCount = columnCount + 1; + CellReference tableStart = StartCellReference; + CellReference tableEnd = EndCellReference; + SpreadsheetVersion version = GetXSSFSheet().GetWorkbook().SpreadsheetVersion; + CellReference newTableEnd = new CellReference(tableEnd.Row, + tableStart.Col + newColumnCount - 1); + AreaReference newTableArea = new AreaReference(tableStart, newTableEnd, version); + + SetCellRef(newTableArea); + } + + UpdateHeaders(); + + return GetColumns()[columnIndex]; + } + protected void SetCellRef(AreaReference refs) + { + + // Strip the sheet name, + // CTWorksheet.getTableParts defines in which sheet the table is + String reference = refs.FormatAsString(); + if (reference.IndexOf('!') != -1) + { + reference = reference.Substring(reference.IndexOf('!') + 1); + } + + // Update + ctTable.@ref = reference; + if (ctTable.IsSetAutoFilter) + { + String filterRef; + int totalsRowCount = TotalsRowCount; + if (totalsRowCount == 0) + { + filterRef = reference; + } + else + { + CellReference start = new CellReference(refs.FirstCell.Row, refs.FirstCell.Col); + // account for footer row(s) in auto-filter range, which doesn't include footers + CellReference end = new CellReference(refs.LastCell.Row - totalsRowCount, refs.LastCell.Col); + // this won't have sheet references because we built the cell references without them + filterRef = new AreaReference(start, end, SpreadsheetVersion.EXCEL2007).FormatAsString(); + } + ctTable.autoFilter.@ref =filterRef; + } + + // Have everything recomputed + UpdateReferences(); + UpdateHeaders(); + } /** * @return the display name of the Table, if set */ - public String DisplayName + public string DisplayName { get { From 449cb489dccfad72d3abc8b1fdcce5100afdc878 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 26 Feb 2022 10:23:25 +0800 Subject: [PATCH 069/159] implement CT_Row.Copy and XWPFTableRow.CloneRow fix #598 --- OpenXmlFormats/Wordprocessing/Table.cs | 22 ++++++++++++++++++---- ooxml/XWPF/Usermodel/XWPFTableRow.cs | 7 ++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/OpenXmlFormats/Wordprocessing/Table.cs b/OpenXmlFormats/Wordprocessing/Table.cs index 137ab626b..2c51f09a9 100644 --- a/OpenXmlFormats/Wordprocessing/Table.cs +++ b/OpenXmlFormats/Wordprocessing/Table.cs @@ -6,7 +6,8 @@ using System.Xml; using System.Collections; using NPOI.OpenXml4Net.Util; - +using System.Linq; +using NPOI.Util; namespace NPOI.OpenXmlFormats.Wordprocessing { @@ -3884,9 +3885,7 @@ public class CT_TrPr : CT_TrPrBase public CT_TrPr() { - //this.trPrChangeField = new CT_TrPrChange(); - //this.delField = new CT_TrackChange(); - //this.insField = new CT_TrackChange(); + } public static new CT_TrPr Parse(XmlNode node, XmlNamespaceManager namespaceManager) { @@ -5724,6 +5723,21 @@ internal void Write(StreamWriter sw, string nodeName) } sw.Write(string.Format("", nodeName)); } + public CT_Row Copy() + { + CT_Row ctRow = new CT_Row(); + ctRow.paraIdField = this.paraIdField.ToArray(); + ctRow.rsidRField = this.rsidRField.ToArray(); + ctRow.rsidDelField = this.rsidDelField.ToArray(); + ctRow.rsidRPrField = this.rsidRPrField.ToArray(); + ctRow.rsidTrField = this.rsidTrField.ToArray(); + ctRow.textIdField = this.textIdField.ToArray(); + ctRow.trPrField = this.trPrField.Copy(); + ctRow.tblPrExField = this.tblPrExField.Copy(); + ctRow.itemsElementNameField = this.itemsElementNameField.Copy(); + ctRow.itemsField = this.itemsField.Copy(); + return ctRow; + } public void RemoveTc(int pos) { RemoveObject(ItemsChoiceTableRowType.tc, pos); diff --git a/ooxml/XWPF/Usermodel/XWPFTableRow.cs b/ooxml/XWPF/Usermodel/XWPFTableRow.cs index d20e1c212..c909079e4 100644 --- a/ooxml/XWPF/Usermodel/XWPFTableRow.cs +++ b/ooxml/XWPF/Usermodel/XWPFTableRow.cs @@ -107,7 +107,12 @@ public XWPFTableCell AddNewTableCell() tableCells.Add(tableCell); return tableCell; } - + public XWPFTableRow CloneRow() + { + XWPFTableRow clonedRow= new XWPFTableRow(ctRow.Copy(), this.table); + table.AddRow(clonedRow); + return clonedRow; + } /** * This element specifies the height of the current table row within the From 85183bdca33397b28bacf548ebd977ace675a837 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 26 Feb 2022 11:52:06 +0800 Subject: [PATCH 070/159] fix null case in CT_Row.Copy --- OpenXmlFormats/Wordprocessing/Table.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/OpenXmlFormats/Wordprocessing/Table.cs b/OpenXmlFormats/Wordprocessing/Table.cs index 2c51f09a9..a1f629ab4 100644 --- a/OpenXmlFormats/Wordprocessing/Table.cs +++ b/OpenXmlFormats/Wordprocessing/Table.cs @@ -5726,14 +5726,14 @@ internal void Write(StreamWriter sw, string nodeName) public CT_Row Copy() { CT_Row ctRow = new CT_Row(); - ctRow.paraIdField = this.paraIdField.ToArray(); - ctRow.rsidRField = this.rsidRField.ToArray(); - ctRow.rsidDelField = this.rsidDelField.ToArray(); - ctRow.rsidRPrField = this.rsidRPrField.ToArray(); - ctRow.rsidTrField = this.rsidTrField.ToArray(); - ctRow.textIdField = this.textIdField.ToArray(); - ctRow.trPrField = this.trPrField.Copy(); - ctRow.tblPrExField = this.tblPrExField.Copy(); + ctRow.paraIdField = this.paraIdField?.ToArray(); + ctRow.rsidRField = this.rsidRField?.ToArray(); + ctRow.rsidDelField = this.rsidDelField?.ToArray(); + ctRow.rsidRPrField = this.rsidRPrField?.ToArray(); + ctRow.rsidTrField = this.rsidTrField?.ToArray(); + ctRow.textIdField = this.textIdField?.ToArray(); + ctRow.trPrField = this.trPrField?.Copy(); + ctRow.tblPrExField = this.tblPrExField?.Copy(); ctRow.itemsElementNameField = this.itemsElementNameField.Copy(); ctRow.itemsField = this.itemsField.Copy(); return ctRow; From 3f1fb62d2cf492735a8c6812b588b3b915b160e7 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 28 Feb 2022 06:44:52 +0800 Subject: [PATCH 071/159] reference NPOI in OpenXmlFormats project --- OpenXmlFormats/NPOI.OpenXmlFormats.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OpenXmlFormats/NPOI.OpenXmlFormats.csproj b/OpenXmlFormats/NPOI.OpenXmlFormats.csproj index 4cec91f2b..6f00a9aed 100644 --- a/OpenXmlFormats/NPOI.OpenXmlFormats.csproj +++ b/OpenXmlFormats/NPOI.OpenXmlFormats.csproj @@ -291,6 +291,10 @@ + + {10fa8538-157a-4380-a4f6-8e2c3ee92cae} + NPOI + {c9f265b7-ece3-4755-b0b1-79536575c2a9} NPOI.OpenXml4Net From 63a74df47e64fec9ad93ed96dce816f5acb543e6 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 6 Mar 2022 05:18:04 +0800 Subject: [PATCH 072/159] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cae821f7..f5f0a8afe 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -想通过Github找.NET工作吗?欢迎watch[这个项目](https://github.com/dotnet-cn/jobs/) +## Statement about Russian Invasion War ## + +This project fully supports Ukraine people to fight against Russian army. This is definitely an invasion. Ukraine are actually China's friend for a long time. Most core parts of China aircraft carrier, airplane are provided by Ukraine companies. I DON'T see any reason that we should support Russian instead Ukraine. + +However, I don't wanna stop Russian developers from using NPOI. They are also victims. They cannot change Putin or Russia Government mind in any case. But if possible, Russian developers may help impact your government although it's risky. Thank you! NPOI =================== From 8b7780a7f81d5e610371a6928544910de2f7cc1d Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 6 Mar 2022 05:20:16 +0800 Subject: [PATCH 073/159] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f5f0a8afe..1294d1e71 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Statement about Russian Invasion War ## -This project fully supports Ukraine people to fight against Russian army. This is definitely an invasion. Ukraine are actually China's friend for a long time. Most core parts of China aircraft carrier, airplane are provided by Ukraine companies. I DON'T see any reason that we should support Russian instead Ukraine. +This project fully supports Ukraine people to fight against Russian army. This is definitely an invasion. Ukraine are actually China's friend for a long time. Most core parts of China aircraft carrier, airplane are provided by Ukraine companies. I DON'T see any reason that we should support Russia instead Ukraine. However, I don't wanna stop Russian developers from using NPOI. They are also victims. They cannot change Putin or Russia Government mind in any case. But if possible, Russian developers may help impact your government although it's risky. Thank you! From 51e6d9d19d68a1785054529b37c7d09616562078 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 6 Mar 2022 05:20:52 +0800 Subject: [PATCH 074/159] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1294d1e71..612c2f3e4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This project fully supports Ukraine people to fight against Russian army. This is definitely an invasion. Ukraine are actually China's friend for a long time. Most core parts of China aircraft carrier, airplane are provided by Ukraine companies. I DON'T see any reason that we should support Russia instead Ukraine. -However, I don't wanna stop Russian developers from using NPOI. They are also victims. They cannot change Putin or Russia Government mind in any case. But if possible, Russian developers may help impact your government although it's risky. Thank you! +However, I don't wanna stop Russian developers from using NPOI. They are also victims. They cannot change Putin's or Russia Government's mind in any case. But if possible, Russian developers may help impact your government although it's risky. Thank you! NPOI =================== From 408ec5aaa9cff0e359c1e837074b161de18ed609 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 6 Mar 2022 10:25:54 +0800 Subject: [PATCH 075/159] parse AlternativeContent under CT_Run fix #769 --- OpenXmlFormats/Drawing/SpreadsheetDrawing.cs | 7 ++-- OpenXmlFormats/Vml/CT_AlternateContent.cs | 43 ++++++++++++++++++++ OpenXmlFormats/Vml/SpreadsheetDrawing.cs | 36 ---------------- OpenXmlFormats/Wordprocessing/Run.cs | 24 +++++++++++ 4 files changed, 71 insertions(+), 39 deletions(-) create mode 100644 OpenXmlFormats/Vml/CT_AlternateContent.cs diff --git a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs index b9f6cc908..a897a8ea9 100644 --- a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs +++ b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs @@ -7,6 +7,7 @@ using System.ComponentModel; using System.Text; using NPOI.OpenXml4Net.Util; +using NPOI.OpenXmlFormats.Vml; namespace NPOI.OpenXmlFormats.Dml.Spreadsheet { @@ -1448,8 +1449,8 @@ public CT_Marker from get { return fromField; } set { fromField = value; } } - Vml.Spreadsheet.CT_AlternateContent alternateContentField = null; - public Vml.Spreadsheet.CT_AlternateContent alternateContent + CT_AlternateContent alternateContentField = null; + public CT_AlternateContent alternateContent { get { @@ -1562,7 +1563,7 @@ internal static CT_OneCellAnchor Parse(XmlNode node, XmlNamespaceManager namespa } else if (childNode.LocalName == "AlternateContent") { - oneCellAnchor.alternateContent = Vml.Spreadsheet.CT_AlternateContent.Parse(childNode, namespaceManager); + oneCellAnchor.alternateContent = CT_AlternateContent.Parse(childNode, namespaceManager); } else if (childNode.LocalName == "clientData") { diff --git a/OpenXmlFormats/Vml/CT_AlternateContent.cs b/OpenXmlFormats/Vml/CT_AlternateContent.cs new file mode 100644 index 000000000..ebe447748 --- /dev/null +++ b/OpenXmlFormats/Vml/CT_AlternateContent.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; + +namespace NPOI.OpenXmlFormats.Vml +{ + public class CT_AlternateContent + { + public string InnerXml { get; set; } + public CT_AlternateContent() + { + } + public static CT_AlternateContent Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + if (node == null) + return null; + + var ac = new CT_AlternateContent(); + if (string.IsNullOrEmpty(node.InnerXml)) + { + return ac; + } + ac.InnerXml = node.InnerXml; + return ac; + } + internal void Write(StreamWriter sw, string nodeName) + { + sw.Write(string.Format("", nodeName)); + } + else + { + sw.Write(">"); + sw.Write(this.InnerXml); + sw.Write(string.Format("", nodeName)); + } + } + } +} diff --git a/OpenXmlFormats/Vml/SpreadsheetDrawing.cs b/OpenXmlFormats/Vml/SpreadsheetDrawing.cs index 2170c06e6..01710a493 100644 --- a/OpenXmlFormats/Vml/SpreadsheetDrawing.cs +++ b/OpenXmlFormats/Vml/SpreadsheetDrawing.cs @@ -9,42 +9,6 @@ namespace NPOI.OpenXmlFormats.Vml.Spreadsheet { - public class CT_AlternateContent - { - public string innerXml { get; set; } - public CT_AlternateContent() - { - } - public static CT_AlternateContent Parse(XmlNode node, XmlNamespaceManager namespaceManager) - { - if (node == null) - return null; - - var ac = new CT_AlternateContent(); - if (string.IsNullOrEmpty(node.InnerXml)) - { - return ac; - } - ac.innerXml = node.InnerXml; - return ac; - } - internal void Write(StreamWriter sw, string nodeName) - { - sw.Write(string.Format("", nodeName)); - } - else - { - sw.Write(">"); - sw.Write(this.innerXml); - sw.Write(string.Format("", nodeName)); - } - - } - - } [System.ComponentModel.DesignerCategory("code")] public class CT_ClientData { diff --git a/OpenXmlFormats/Wordprocessing/Run.cs b/OpenXmlFormats/Wordprocessing/Run.cs index c84c6b54f..62107c544 100644 --- a/OpenXmlFormats/Wordprocessing/Run.cs +++ b/OpenXmlFormats/Wordprocessing/Run.cs @@ -30,6 +30,8 @@ public class CT_R private byte[] rsidDelField; private byte[] rsidRField; + + Vml.CT_AlternateContent alternateContentField = null; public CT_R() { @@ -50,6 +52,17 @@ public CT_RPr rPr this.rPrField = value; } } + public Vml.CT_AlternateContent alternateContent + { + get + { + return alternateContentField; + } + set + { + this.alternateContentField = value; + } + } [XmlElement("annotationRef", typeof(CT_Empty), Order = 1)] [XmlElement("br", typeof(CT_Br), Order = 1)] @@ -373,6 +386,7 @@ public static CT_R Parse(XmlNode node, XmlNamespaceManager namespaceManager) ctObj.rsidRPr = XmlHelper.ReadBytes(node.Attributes["w:rsidRPr"]); ctObj.rsidDel = XmlHelper.ReadBytes(node.Attributes["w:rsidDel"]); ctObj.rsidR = XmlHelper.ReadBytes(node.Attributes["w:rsidR"]); + foreach (XmlNode childNode in node.ChildNodes) { if (childNode.LocalName == "rPr") @@ -442,6 +456,10 @@ public static CT_R Parse(XmlNode node, XmlNamespaceManager namespaceManager) ctObj.Items.Add(CT_Drawing.Parse(childNode, namespaceManager)); ctObj.ItemsElementName.Add(RunItemsChoiceType.drawing); } + else if (childNode.LocalName == "AlternateContent") + { + ctObj.alternateContent = Vml.CT_AlternateContent.Parse(childNode, namespaceManager); + } else if (childNode.LocalName == "endnoteRef") { ctObj.Items.Add(new CT_Empty()); @@ -551,6 +569,8 @@ internal void Write(StreamWriter sw, string nodeName) if (this.rPr != null) this.rPr.Write(sw, "rPr"); int i = 0; + + foreach (object o in this.Items) { if ((o is CT_Text) && this.ItemsElementName[i] == RunItemsChoiceType.instrText) @@ -619,6 +639,10 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(""); i++; } + if (this.alternateContent != null) + { + this.alternateContent.Write(sw, "AlternateContent"); + } sw.Write(string.Format("", nodeName)); } From a176d64a9e565fda209be2aab2e3243b6226a238 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 15 Mar 2022 07:54:12 +0800 Subject: [PATCH 076/159] fix bugs --- OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs | 14 ++- ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs | 9 +- ooxml/XSSF/UserModel/XSSFTable.cs | 91 ++++++++++++++++++- 3 files changed, 108 insertions(+), 6 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs b/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs index a0ec37a64..5648f3d14 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet/CT_Table.cs @@ -624,7 +624,7 @@ public bool connectionIdSpecified } } - internal CT_TableStyleInfo AddNewTableStyleInfo() + public CT_TableStyleInfo AddNewTableStyleInfo() { this.tableStyleInfoField = new CT_TableStyleInfo(); return this.tableStyleInfoField; @@ -635,6 +635,10 @@ public CT_TableColumns AddNewTableColumns() this.tableColumnsField = new CT_TableColumns(); return this.tableColumnsField; } + public bool IsSetTableStyleInfo() + { + return this.tableStyleInfoField != null; + } } [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main")] @@ -687,6 +691,14 @@ public CT_TableColumn InsertNewTableColumn(int columnIndex) this.tableColumn.Insert(columnIndex,newTableColumn); return newTableColumn; } + public List GetTableColumnList() + { + if (this.tableColumnField == null) + { + this.tableColumnField = new List(); + } + return this.tableColumnField; + } public void RemoveTableColumn(int columnIndex) { this.tableColumn.RemoveAt(columnIndex); diff --git a/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs index 61f6bae61..d22c74b23 100644 --- a/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs +++ b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs @@ -301,7 +301,12 @@ public enum XSSFBuiltinTableStyleEnum:int } public class XSSFBuiltinTableStyle { - const string presetTableStylesResourceName= "NPOI.XSSF.UserModel.presetTableStyles.xml"; +#if NETSTANDARD2_1 || NETSTANDARD2_0 + const string presetTableStylesResourceName = "NPOI.OOXML.XSSF.UserModel.presetTableStyles.xml"; +#else + const string presetTableStylesResourceName= "presetTableStyles.xml"; +#endif + private static Dictionary styleMap = new Dictionary(); public static ITableStyle GetStyle(XSSFBuiltinTableStyleEnum style) { @@ -327,7 +332,7 @@ private static void Init() foreach (XmlNode child in node.ChildNodes) { String styleName = child.Name; - if (Enum.TryParse(styleName, out XSSFBuiltinTableStyleEnum builtIn)) + if (!Enum.TryParse(styleName, out XSSFBuiltinTableStyleEnum builtIn)) { continue; } diff --git a/ooxml/XSSF/UserModel/XSSFTable.cs b/ooxml/XSSF/UserModel/XSSFTable.cs index c705a84ea..1a7f3822a 100644 --- a/ooxml/XSSF/UserModel/XSSFTable.cs +++ b/ooxml/XSSF/UserModel/XSSFTable.cs @@ -30,6 +30,7 @@ using System.Globalization; using NPOI.SS; using System.Linq; +using NPOI.OOXML.XSSF.UserModel; namespace NPOI.XSSF.UserModel { @@ -219,7 +220,7 @@ public List GetXmlColumnPrs() } return xmlColumnPrs; } - + private string name; /** * @return the name of the Table, if set */ @@ -227,11 +228,22 @@ public String Name { get { - return ctTable.name; + if (name == null) + { + Name = ctTable.name; + } + return name; } set { + if (value == null) + { + ctTable.name=null; + name = null; + return; + } ctTable.name = value; + name = value; } } public XSSFTableColumn CreateColumn(String columnName) @@ -300,6 +312,41 @@ public XSSFTableColumn CreateColumn(String columnName, int columnIndex) return GetColumns()[columnIndex]; } + /** + * Get the area reference for the cells which this table covers. The area + * includes header rows and totals rows. + * + * Does not track updates to underlying changes to CTTable To synchronize + * with changes to the underlying CTTable, call {@link #updateReferences()}. + * + * @return the area of the table + * @see "Open Office XML Part 4: chapter 3.5.1.2, attribute ref" + */ + public AreaReference GetCellReferences() + { + return new AreaReference( + StartCellReference, + EndCellReference, + SpreadsheetVersion.EXCEL2007 + ); + } + /** + * Set the area reference for the cells which this table covers. The area + * includes includes header rows and totals rows. Automatically synchronizes + * any changes by calling {@link #updateHeaders()}. + * + * Note: The area's width should be identical to the amount of columns in + * the table or the table may be invalid. All header rows, totals rows and + * at least one data row must fit inside the area. Updating the area with + * this method does not create or remove any columns and does not change any + * cell values. + * + * @see "Open Office XML Part 4: chapter 3.5.1.2, attribute ref" + */ + public void SetCellReferences(AreaReference refs) + { + SetCellRef(refs); + } protected void SetCellRef(AreaReference refs) { @@ -336,6 +383,43 @@ protected void SetCellRef(AreaReference refs) UpdateReferences(); UpdateHeaders(); } + private String styleName; + public string StyleName + { + get { + if (styleName == null && ctTable.IsSetTableStyleInfo()) + { + StyleName = ctTable.tableStyleInfo.name; + } + return styleName; + } + set + { + if (value == null) + { + if (ctTable.IsSetTableStyleInfo()) + { + ctTable.tableStyleInfo.name =null; + } + styleName = null; + return; + } + if (!ctTable.IsSetTableStyleInfo()) + { + ctTable.AddNewTableStyleInfo(); + } + ctTable.tableStyleInfo.name = value; + styleName = value; + } + } + public ITableStyleInfo Style + { + get + { + if (!ctTable.IsSetTableStyleInfo()) return null; + return new XSSFTableStyleInfo(((XSSFWorkbook)((XSSFSheet)GetParent()).Workbook).GetStylesSource(), ctTable.tableStyleInfo); + } + } /** * @return the display name of the Table, if set */ @@ -355,6 +439,7 @@ public string DisplayName /** * @return the number of mapped table columns (see Open Office XML Part 4: chapter 3.5.1.4) */ + [Obsolete] public long NumberOfMappedColumns { get @@ -580,7 +665,7 @@ public List GetColumns() CT_TableColumns ctTableColumns = ctTable.tableColumns; if (ctTableColumns != null) { - foreach (CT_TableColumn column in ctTableColumns.tableColumn) + foreach (CT_TableColumn column in ctTableColumns.GetTableColumnList()) { XSSFTableColumn tableColumn = new XSSFTableColumn(this, column); columns.Add(tableColumn); From 7f82feee473c41e77d87c09cf5e5b9e310679dd9 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 16 Mar 2022 03:46:07 +0800 Subject: [PATCH 077/159] move presetTableStyles.xml to NPOI.OOXML.Resources --- ooxml/NPOI.OOXML.Core.csproj | 2 +- ooxml/{XSSF/UserModel => Resources}/presetTableStyles.xml | 0 ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename ooxml/{XSSF/UserModel => Resources}/presetTableStyles.xml (100%) diff --git a/ooxml/NPOI.OOXML.Core.csproj b/ooxml/NPOI.OOXML.Core.csproj index 4f3ea4af1..46de29584 100644 --- a/ooxml/NPOI.OOXML.Core.csproj +++ b/ooxml/NPOI.OOXML.Core.csproj @@ -35,7 +35,7 @@ - + diff --git a/ooxml/XSSF/UserModel/presetTableStyles.xml b/ooxml/Resources/presetTableStyles.xml similarity index 100% rename from ooxml/XSSF/UserModel/presetTableStyles.xml rename to ooxml/Resources/presetTableStyles.xml diff --git a/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs index d22c74b23..a5a03c3ce 100644 --- a/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs +++ b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs @@ -302,7 +302,7 @@ public enum XSSFBuiltinTableStyleEnum:int public class XSSFBuiltinTableStyle { #if NETSTANDARD2_1 || NETSTANDARD2_0 - const string presetTableStylesResourceName = "NPOI.OOXML.XSSF.UserModel.presetTableStyles.xml"; + const string presetTableStylesResourceName = "NPOI.OOXML.Resources.presetTableStyles.xml"; #else const string presetTableStylesResourceName= "presetTableStyles.xml"; #endif From aa38f5d173223f9553cf760d3726bda013cbb2b2 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 16 Mar 2022 03:47:13 +0800 Subject: [PATCH 078/159] comment out 'calculate new area' --- ooxml/XSSF/UserModel/XSSFTable.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ooxml/XSSF/UserModel/XSSFTable.cs b/ooxml/XSSF/UserModel/XSSFTable.cs index 1a7f3822a..d797b4b5c 100644 --- a/ooxml/XSSF/UserModel/XSSFTable.cs +++ b/ooxml/XSSF/UserModel/XSSFTable.cs @@ -294,7 +294,7 @@ public XSSFTableColumn CreateColumn(String columnName, int columnIndex) column.name = "Column " + nextColumnId; } - if (ctTable.@ref != null) + /*if (ctTable.@ref != null) { // calculate new area int newColumnCount = columnCount + 1; @@ -306,7 +306,7 @@ public XSSFTableColumn CreateColumn(String columnName, int columnIndex) AreaReference newTableArea = new AreaReference(tableStart, newTableEnd, version); SetCellRef(newTableArea); - } + }*/ UpdateHeaders(); @@ -458,7 +458,7 @@ public int ColumnCount } // Casting to int should be safe here - tables larger than the // sheet (which holds the actual data of the table) can't exists. - return (int)tableColumns.count; + return (int)tableColumns.tableColumn.Count(); } } ///
@@ -703,6 +703,10 @@ public bool IsHasTotalsRow { return ctTable.totalsRowShown; } + set + { + ctTable.totalsRowShown = value; + } } public int StartColIndex From 37395f5b1bdffd1ff91c696ba1ea5927e0a8f22d Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Fri, 18 Mar 2022 10:25:42 +0800 Subject: [PATCH 079/159] fix #445 --- ooxml/XSSF/Model/StylesTable.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ooxml/XSSF/Model/StylesTable.cs b/ooxml/XSSF/Model/StylesTable.cs index 88b151c9a..2c20b6d2d 100644 --- a/ooxml/XSSF/Model/StylesTable.cs +++ b/ooxml/XSSF/Model/StylesTable.cs @@ -201,7 +201,8 @@ internal void ReadFrom(XmlDocument xmldoc) foreach (CT_NumFmt nfmt in ctfmts.numFmt) { short formatId = (short)nfmt.numFmtId; - numberFormats.Add(formatId, nfmt.formatCode); + if(!numberFormats.ContainsKey(formatId)) + numberFormats.Add(formatId, nfmt.formatCode); } } From 8465865124a0dc79a3d5f4d86e42c4283d60c54d Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 19 Mar 2022 21:00:43 +0800 Subject: [PATCH 080/159] reimplement FindExistingRelation fix #620 --- ooxml/POIXMLDocumentPart.cs | 76 +++++-------------- openxml4Net/OPC/PackagePart.cs | 10 ++- .../OPC/PackageRelationshipCollection.cs | 23 +++++- 3 files changed, 51 insertions(+), 58 deletions(-) diff --git a/ooxml/POIXMLDocumentPart.cs b/ooxml/POIXMLDocumentPart.cs index f84942061..043f2816a 100644 --- a/ooxml/POIXMLDocumentPart.cs +++ b/ooxml/POIXMLDocumentPart.cs @@ -24,6 +24,7 @@ namespace NPOI using NPOI.OpenXml4Net.Exceptions; using System.Xml; using NPOI.OpenXml4Net.OPC.Internal; + using System.Diagnostics; /** * Represents an entry of a OOXML namespace. @@ -401,32 +402,27 @@ public String GetRelationId(POIXMLDocumentPart part) return null; } - /** - * Add a new child POIXMLDocumentPart - * - * @param part the child to add - * - * @deprecated in POI 3.14, scheduled for removal in POI 3.16 - */ - [Obsolete("deprecated in POI 3.14, scheduled for removal in POI 3.16")] + /// + /// Add a new child POIXMLDocumentPart + /// + /// + /// the child to add + [Obsolete("deprecated in POI 3.14, scheduled for removal in POI 3.16")] public void AddRelation(String id, POIXMLDocumentPart part) { PackageRelationship pr = part.GetPackagePart().GetRelationship(id); AddRelation(pr, part); } - - /** - * Add a new child POIXMLDocumentPart - * - * @param relId the preferred relation id, when null the next free relation id will be used - * @param relationshipType the package relationship type - * @param part the child to add - * - * @since 3.14-Beta1 - */ + /// + /// Add a new child POIXMLDocumentPart + /// + /// the preferred relation id, when null the next free relation id will be used + /// the package relationship type + /// the child to add + /// public RelationPart AddRelation(String relId, POIXMLRelation relationshipType, POIXMLDocumentPart part) { - PackageRelationship pr = FindExistingRelation(part); + PackageRelationship pr = this.packagePart.FindExistingRelation(part.GetPackagePart()); if (pr == null) { PackagePartName ppn = part.GetPackagePart().PartName; @@ -436,13 +432,11 @@ public RelationPart AddRelation(String relId, POIXMLRelation relationshipType, P AddRelation(pr, part); return new RelationPart(pr, part); } - - /** - * Add a new child POIXMLDocumentPart - * - * @param pr the relationship of the child - * @param part the child to add - */ + /// + /// Add a new child POIXMLDocumentPart + /// + /// the relationship of the child + /// the child to add private void AddRelation(PackageRelationship pr, POIXMLDocumentPart part) { if (relations.ContainsKey(pr.Id)) @@ -453,36 +447,6 @@ private void AddRelation(PackageRelationship pr, POIXMLDocumentPart part) } - /// - /// Check if the new part was already added before via PackagePart.addRelationship() - /// - /// to find the relationship for - /// The existing relationship, or null if there isn't yet one - private PackageRelationship FindExistingRelation(POIXMLDocumentPart part) - { - String ppn = part.GetPackagePart().PartName.Name; - try - { - foreach (PackageRelationship pr in packagePart.Relationships) - { - PackagePart pp = packagePart.GetRelatedPart(pr); - if (pr.TargetMode == TargetMode.External) - { - continue; - } - if (ppn.Equals(pp.PartName.Name)) - { - return pr; - } - } - } - catch (InvalidFormatException e) - { - throw new POIXMLException("invalid package relationships", e); - } - return null; - } - /** * Remove the relation to the specified part in this namespace and remove the * part, if it is no longer needed. diff --git a/openxml4Net/OPC/PackagePart.cs b/openxml4Net/OPC/PackagePart.cs index ea7e7375e..e769ce02c 100644 --- a/openxml4Net/OPC/PackagePart.cs +++ b/openxml4Net/OPC/PackagePart.cs @@ -112,7 +112,15 @@ public abstract class PackagePart : RelationshipSource, IComparable { } - + /// + /// Check if the new part was already added before via PackagePart.addRelationship() + /// + /// to find the relationship for + /// The existing relationship, or null if there isn't yet one + public PackageRelationship FindExistingRelation(PackagePart packagePart) + { + return _relationships.FindExistingInternalRelation(packagePart); + } /** * Adds an external relationship to a part (except relationships part). * diff --git a/openxml4Net/OPC/PackageRelationshipCollection.cs b/openxml4Net/OPC/PackageRelationshipCollection.cs index 087f2e001..a22e33612 100644 --- a/openxml4Net/OPC/PackageRelationshipCollection.cs +++ b/openxml4Net/OPC/PackageRelationshipCollection.cs @@ -30,6 +30,10 @@ public class PackageRelationshipCollection : IEnumerator * Package relationships ordered by type. */ private SortedList relationshipsByType; + /** + * A lookup of internal relationships to avoid + */ + private SortedList internalRelationshipsByTargetName; /** * This relationshipPart. @@ -62,6 +66,7 @@ public PackageRelationshipCollection() { relationshipsByID = new SortedList(); relationshipsByType = new SortedList(new DuplicateComparer()); + internalRelationshipsByTargetName = new SortedList(); } class DuplicateComparer : IComparer { @@ -195,6 +200,10 @@ private static PackagePartName GetRelationshipPartName(PackagePart part) */ public void AddRelationship(PackageRelationship relPart) { + if (relPart == null || string.IsNullOrEmpty(relPart.Id)) + { + throw new ArgumentException("invalid relationship part/id"); + } relationshipsByID[relPart.Id] = relPart; relationshipsByType[relPart.RelationshipType] = relPart; } @@ -235,6 +244,10 @@ public void AddRelationship(PackageRelationship relPart) sourcePart, targetUri, targetMode, relationshipType, id); relationshipsByID[rel.Id] = rel; relationshipsByType[rel.RelationshipType] = rel; + if (targetMode == TargetMode.Internal) + { + internalRelationshipsByTargetName.Add(targetUri.OriginalString, rel); + } return rel; } @@ -257,6 +270,7 @@ public void RemoveRelationship(String id) if (relationshipsByType.Values[i] == rel) relationshipsByType.RemoveAt(i); } + internalRelationshipsByTargetName.Values.Remove(rel); } } } @@ -451,8 +465,15 @@ public void Clear() { relationshipsByID.Clear(); relationshipsByType.Clear(); + internalRelationshipsByTargetName.Clear(); + } + public PackageRelationship FindExistingInternalRelation(PackagePart packagePart) + { + var pn=packagePart.PartName.Name; + if (!internalRelationshipsByTargetName.ContainsKey(pn)) + return null; + return internalRelationshipsByTargetName[pn]; } - public override String ToString() { String str; From 20c1493b828865aea3ac6b09736e8913aca90fc4 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 19 Mar 2022 22:07:47 +0800 Subject: [PATCH 081/159] Update XSSFBuiltinTableStyle.cs change namespace --- ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs index a5a03c3ce..82a91f23b 100644 --- a/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs +++ b/ooxml/XSSF/UserModel/XSSFBuiltinTableStyle.cs @@ -7,7 +7,7 @@ using System.Text; using System.Xml; -namespace NPOI.OOXML.XSSF.UserModel +namespace NPOI.XSSF.UserModel { public enum XSSFBuiltinTableStyleEnum:int { From 835091d5c7463e2a23e5af2a6f15e4789240683b Mon Sep 17 00:00:00 2001 From: KnyazSh Date: Mon, 21 Mar 2022 23:31:18 +0300 Subject: [PATCH 082/159] Fix minValue and maxValue in SharedItems in CT_PivotCacheDefinition --- .../PivotTable/CT_PivotCacheDefinition.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs index 5ae7c9b19..a85703c0f 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs @@ -1848,8 +1848,17 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "containsMixedTypes", this.containsMixedTypes, false); XmlHelper.WriteAttribute(sw, "containsNumber", this.containsNumber, false); XmlHelper.WriteAttribute(sw, "containsInteger", this.containsInteger, false); - XmlHelper.WriteAttribute(sw, "minValue", this.minValue); - XmlHelper.WriteAttribute(sw, "maxValue", this.maxValue); + if (this.containsNumber) + { + XmlHelper.WriteAttribute(sw, "minValue", this.minValue, true); + XmlHelper.WriteAttribute(sw, "maxValue", this.maxValue, true); + } + else + { + XmlHelper.WriteAttribute(sw, "minValue", this.minValue); + XmlHelper.WriteAttribute(sw, "maxValue", this.maxValue); + } + XmlHelper.WriteAttribute(sw, "minDate", this.minDate); XmlHelper.WriteAttribute(sw, "maxDate", this.maxDate); XmlHelper.WriteAttribute(sw, "count", this.count); From 39dd16dd0c7abb4f86d887926e986591bdda7616 Mon Sep 17 00:00:00 2001 From: Brian Yule Date: Mon, 28 Mar 2022 15:07:10 +0100 Subject: [PATCH 083/159] This is to fix CVE-2021-32840, CVE-2021-32842 and CVE-2021-32841 by updating SharpZipLib to 1.3.3 from 1.3.2. --- main/NPOI.Core.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/NPOI.Core.csproj b/main/NPOI.Core.csproj index 09de52fc6..5dcd16cbb 100644 --- a/main/NPOI.Core.csproj +++ b/main/NPOI.Core.csproj @@ -26,7 +26,7 @@ - + @@ -34,4 +34,4 @@ - + \ No newline at end of file From 69fcda51b7e4838f21cd3f0ec27c1771c91d87e7 Mon Sep 17 00:00:00 2001 From: Brian Yule Date: Mon, 28 Mar 2022 15:08:19 +0100 Subject: [PATCH 084/159] Adding fix for SharpZipLib --- main/NPOI.csproj | 2 +- main/packages.config | 2 +- ooxml/packages.config | 2 +- openxml4Net/packages.config | 2 +- testcases/main/NPOI.TestCases.csproj | 2 +- testcases/ooxml/NPOI.OOXML.TestCases.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/main/NPOI.csproj b/main/NPOI.csproj index 809114adf..29d562d79 100644 --- a/main/NPOI.csproj +++ b/main/NPOI.csproj @@ -1365,7 +1365,7 @@ 1.8.9 - 1.3.2 + 1.3.3 diff --git a/main/packages.config b/main/packages.config index d460d74a6..7e45f57f7 100644 --- a/main/packages.config +++ b/main/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/ooxml/packages.config b/ooxml/packages.config index d460d74a6..7e45f57f7 100644 --- a/ooxml/packages.config +++ b/ooxml/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/openxml4Net/packages.config b/openxml4Net/packages.config index c8abe76cc..04ff7ff2c 100644 --- a/openxml4Net/packages.config +++ b/openxml4Net/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/testcases/main/NPOI.TestCases.csproj b/testcases/main/NPOI.TestCases.csproj index 3b6140662..f67583a1c 100644 --- a/testcases/main/NPOI.TestCases.csproj +++ b/testcases/main/NPOI.TestCases.csproj @@ -720,7 +720,7 @@ 3.13.1 - 1.3.2 + 1.3.3 diff --git a/testcases/ooxml/NPOI.OOXML.TestCases.csproj b/testcases/ooxml/NPOI.OOXML.TestCases.csproj index 8d383ebb0..de4f84064 100644 --- a/testcases/ooxml/NPOI.OOXML.TestCases.csproj +++ b/testcases/ooxml/NPOI.OOXML.TestCases.csproj @@ -314,7 +314,7 @@ 1.8.9 - 1.3.2 + 1.3.3 From b3ff43f72054d9771ecce27f8eea7b5b40a92ebf Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 29 Mar 2022 17:22:58 +0800 Subject: [PATCH 085/159] output font family value if it's 0 fix #731 --- OpenXmlFormats/Spreadsheet/Styles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenXmlFormats/Spreadsheet/Styles.cs b/OpenXmlFormats/Spreadsheet/Styles.cs index 3f104e5b8..b66a3d6ad 100644 --- a/OpenXmlFormats/Spreadsheet/Styles.cs +++ b/OpenXmlFormats/Spreadsheet/Styles.cs @@ -622,7 +622,7 @@ public static CT_IntProperty Parse(XmlNode node, XmlNamespaceManager namespaceMa internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "val", this.val); + XmlHelper.WriteAttribute(sw, "val", this.val, true); sw.Write("/>"); } } From 8dda408f7be6a329bd66ccf72f149dd1691a03fe Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 4 Apr 2022 10:29:03 +0800 Subject: [PATCH 086/159] Update Paragraph.cs --- OpenXmlFormats/Wordprocessing/Paragraph.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/OpenXmlFormats/Wordprocessing/Paragraph.cs b/OpenXmlFormats/Wordprocessing/Paragraph.cs index 462e9c485..2a96d793f 100644 --- a/OpenXmlFormats/Wordprocessing/Paragraph.cs +++ b/OpenXmlFormats/Wordprocessing/Paragraph.cs @@ -4555,6 +4555,13 @@ public CT_OnOff AddNewB() return this.bField; } + public CT_Shd AddNewShd() + { + if (this.shdField == null) + this.shdField = new CT_Shd(); + return this.shdField; + } + public CT_OnOff AddNewBCs() { if (this.bCsField == null) @@ -4759,6 +4766,13 @@ public bool IsSetSpacing() return this.spacingField != null; } + public CT_Highlight AddNewHighlight() + { + if (this.highlightField == null) + this.highlightField = new CT_Highlight(); + return this.highlightField; + } + public CT_SignedTwipsMeasure AddNewSpacing() { if (this.spacingField == null) @@ -4770,12 +4784,6 @@ public bool IsSetHighlight() { return this.highlightField != null; } - - internal CT_Highlight AddNewHighlight() - { - this.highlightField = new CT_Highlight(); - return this.highlightField; - } } From 8f718e88b7551c38f2853eb655f8d30310c23c3c Mon Sep 17 00:00:00 2001 From: KnyazSh Date: Tue, 12 Apr 2022 09:40:46 +0300 Subject: [PATCH 087/159] Fix structure CT_PivotTableDefinition and CT_PivotCache --- .../Spreadsheet/PivotTable/CT_PivotTableDefinition.cs | 3 +-- OpenXmlFormats/Spreadsheet/Workbook/CT_PivotCache.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs index b25bf9edd..27e24c149 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs @@ -194,7 +194,7 @@ internal void Write(StreamWriter sw) sw.Write("xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" "); sw.Write("xmlns:s=\"http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes\" "); XmlHelper.WriteAttribute(sw, "name", this.name); - XmlHelper.WriteAttribute(sw, "cacheId", this.cacheId); + XmlHelper.WriteAttribute(sw, "cacheId", this.cacheId, true); XmlHelper.WriteAttribute(sw, "dataOnRows", this.dataOnRows); XmlHelper.WriteAttribute(sw, "dataPosition", this.dataPosition); XmlHelper.WriteAttribute(sw, "autoFormatId", this.autoFormatId); @@ -4498,7 +4498,6 @@ internal void Write(StreamWriter sw, string nodeName) public CT_PageField() { - this.extLstField = new CT_ExtensionList(); } [System.Xml.Serialization.XmlElementAttribute(Order = 0)] diff --git a/OpenXmlFormats/Spreadsheet/Workbook/CT_PivotCache.cs b/OpenXmlFormats/Spreadsheet/Workbook/CT_PivotCache.cs index 6543cc9a0..2b627e882 100644 --- a/OpenXmlFormats/Spreadsheet/Workbook/CT_PivotCache.cs +++ b/OpenXmlFormats/Spreadsheet/Workbook/CT_PivotCache.cs @@ -102,7 +102,7 @@ public static CT_PivotCache Parse(XmlNode node, XmlNamespaceManager namespaceMan internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "cacheId", this.cacheId); + XmlHelper.WriteAttribute(sw, "cacheId", this.cacheId, true); XmlHelper.WriteAttribute(sw, "r:id", this.id); sw.Write(">"); sw.Write(string.Format("", nodeName)); From f4279371a8f8022325039fd852b926d9fb513074 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 14 Apr 2022 14:12:10 +0800 Subject: [PATCH 088/159] fix #798 --- OpenXmlFormats/Spreadsheet/Styles/CT_Colors.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/Styles/CT_Colors.cs b/OpenXmlFormats/Spreadsheet/Styles/CT_Colors.cs index 5f958b4e4..071b591b3 100644 --- a/OpenXmlFormats/Spreadsheet/Styles/CT_Colors.cs +++ b/OpenXmlFormats/Spreadsheet/Styles/CT_Colors.cs @@ -240,11 +240,10 @@ public bool rgbSpecified } public void SetRgb(byte R, byte G, byte B) { - this.rgbField = new byte[4]; - this.rgbField[0] = 0; - this.rgbField[1] = R; - this.rgbField[2] = G; - this.rgbField[3] = B; + this.rgbField = new byte[3]; + this.rgbField[0] = R; + this.rgbField[1] = G; + this.rgbField[2] = B; rgbSpecified = true; } public bool IsSetRgb() From 58ebb688f397a0b648613809218a8b321cff32da Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 14 Apr 2022 14:17:33 +0800 Subject: [PATCH 089/159] #798 --- ooxml/XSSF/UserModel/XSSFColor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ooxml/XSSF/UserModel/XSSFColor.cs b/ooxml/XSSF/UserModel/XSSFColor.cs index 93627c5c0..ebf28aa18 100644 --- a/ooxml/XSSF/UserModel/XSSFColor.cs +++ b/ooxml/XSSF/UserModel/XSSFColor.cs @@ -52,7 +52,7 @@ public XSSFColor(System.Drawing.Color clr) : this() { - ctColor.SetRgb((byte)clr.R, (byte)clr.G, (byte)clr.B); + ctColor.SetRgb(clr.R, clr.G, clr.B); } public XSSFColor(byte[] rgb) From 872de993cb0f03ca060bd57c5c670a249619ac05 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 21 Apr 2022 05:12:06 +0800 Subject: [PATCH 090/159] add IsPartOfArrayFormulaGroup and ArrayFormulaRange property to IEvaluationCell --- main/HSSF/UserModel/HSSFEvaluationCell.cs | 15 ++++++++++++ .../Eval/Forked/ForkedEvaluationCell.cs | 14 +++++++++++ main/SS/Formula/EvaluationCell.cs | 24 +++++++++++-------- ooxml/XSSF/Streaming/SXSSFEvaluationCell.cs | 24 ++++++++++++++----- ooxml/XSSF/UserModel/XSSFEvaluationCell.cs | 16 ++++++++++--- 5 files changed, 74 insertions(+), 19 deletions(-) diff --git a/main/HSSF/UserModel/HSSFEvaluationCell.cs b/main/HSSF/UserModel/HSSFEvaluationCell.cs index d15512d6b..58083d1a6 100644 --- a/main/HSSF/UserModel/HSSFEvaluationCell.cs +++ b/main/HSSF/UserModel/HSSFEvaluationCell.cs @@ -21,6 +21,7 @@ namespace NPOI.HSSF.UserModel { using System; using NPOI.SS.Formula; + using NPOI.SS.Util; /// /// HSSF wrapper for a cell under evaluation @@ -130,5 +131,19 @@ public CellType CachedFormulaResultType { get { return _cell.CachedFormulaResultType; } } + + public bool IsPartOfArrayFormulaGroup + { + get + { + return _cell.IsPartOfArrayFormulaGroup; + } + } + + public CellRangeAddress ArrayFormulaRange + { + + get { return _cell.ArrayFormulaRange; } + } } } \ No newline at end of file diff --git a/main/SS/Formula/Eval/Forked/ForkedEvaluationCell.cs b/main/SS/Formula/Eval/Forked/ForkedEvaluationCell.cs index d8550b00b..6b3594c39 100644 --- a/main/SS/Formula/Eval/Forked/ForkedEvaluationCell.cs +++ b/main/SS/Formula/Eval/Forked/ForkedEvaluationCell.cs @@ -19,6 +19,8 @@ using NPOI.SS.Formula; using NPOI.SS.Formula.Eval; using NPOI.SS.UserModel; +using NPOI.SS.Util; + namespace NPOI.SS.Formula.Eval.Forked { @@ -171,6 +173,18 @@ public int ColumnIndex return _masterCell.ColumnIndex; } } + public bool IsPartOfArrayFormulaGroup + { + get + { + return _masterCell.IsPartOfArrayFormulaGroup; + } + } + + public CellRangeAddress ArrayFormulaRange + { + get { return _masterCell.ArrayFormulaRange; } + } public CellType CachedFormulaResultType { get { return _masterCell.CachedFormulaResultType; } diff --git a/main/SS/Formula/EvaluationCell.cs b/main/SS/Formula/EvaluationCell.cs index 799cbb7d9..851594b20 100644 --- a/main/SS/Formula/EvaluationCell.cs +++ b/main/SS/Formula/EvaluationCell.cs @@ -19,17 +19,19 @@ namespace NPOI.SS.Formula { using System; using NPOI.SS.UserModel; + using NPOI.SS.Util; + /** - * Abstracts a cell for the purpose of formula evaluation. This interface represents both formula - * and non-formula cells.
- * - * Implementors of this class must implement {@link #HashCode()} and {@link #Equals(Object)} - * To provide an identity relationship based on the underlying HSSF or XSSF cell

- * - * For POI internal use only - * - * @author Josh Micich - */ +* Abstracts a cell for the purpose of formula evaluation. This interface represents both formula +* and non-formula cells.
+* +* Implementors of this class must implement {@link #HashCode()} and {@link #Equals(Object)} +* To provide an identity relationship based on the underlying HSSF or XSSF cell

+* +* For POI internal use only +* +* @author Josh Micich +*/ public interface IEvaluationCell { // consider method Object GetUnderlyingCell() To reduce memory consumption in formula cell cache @@ -43,6 +45,8 @@ public interface IEvaluationCell bool BooleanCellValue { get; } int ErrorCellValue { get; } Object IdentityKey { get; } + bool IsPartOfArrayFormulaGroup { get; } + CellRangeAddress ArrayFormulaRange { get; } CellType CachedFormulaResultType { get; } } } \ No newline at end of file diff --git a/ooxml/XSSF/Streaming/SXSSFEvaluationCell.cs b/ooxml/XSSF/Streaming/SXSSFEvaluationCell.cs index ae153c40b..6884f757a 100644 --- a/ooxml/XSSF/Streaming/SXSSFEvaluationCell.cs +++ b/ooxml/XSSF/Streaming/SXSSFEvaluationCell.cs @@ -17,6 +17,7 @@ using System; using NPOI.SS.Formula; using NPOI.SS.UserModel; +using NPOI.SS.Util; using NPOI.XSSF.UserModel; namespace NPOI.XSSF.Streaming @@ -137,13 +138,24 @@ public String StringCellValue return _cell.RichStringCellValue.String; } } - /** - * Will return {@link CellType} in a future version of POI. - * For forwards compatibility, do not hard-code cell type literals in your code. - * - * @return cell type of cached formula result - */ + public bool IsPartOfArrayFormulaGroup + { + get + { + return _cell.IsPartOfArrayFormulaGroup; + } + } + public CellRangeAddress ArrayFormulaRange + { + get + { + return _cell.ArrayFormulaRange; + } + } + ///

+ /// Will return CellType in a future version of POI. For forwards compatibility, do not hard-code cell type literals in your code. + /// public CellType CachedFormulaResultType { get diff --git a/ooxml/XSSF/UserModel/XSSFEvaluationCell.cs b/ooxml/XSSF/UserModel/XSSFEvaluationCell.cs index 0401f51c5..717e09716 100644 --- a/ooxml/XSSF/UserModel/XSSFEvaluationCell.cs +++ b/ooxml/XSSF/UserModel/XSSFEvaluationCell.cs @@ -19,6 +19,8 @@ using NPOI.XSSF.UserModel; using System; using NPOI.SS.UserModel; +using NPOI.SS.Util; + namespace NPOI.XSSF.UserModel { @@ -121,14 +123,22 @@ public virtual String StringCellValue } } - #region IEvaluationCell Ա + public bool IsPartOfArrayFormulaGroup + { + get + { + return _cell.IsPartOfArrayFormulaGroup; + } + } + public CellRangeAddress ArrayFormulaRange + { + get { return _cell.ArrayFormulaRange; } + } public virtual CellType CachedFormulaResultType { get { return _cell.CachedFormulaResultType; } } - - #endregion } } From 0cf700a01b09c65459b2550729e03cab13dc7b6e Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 21 Apr 2022 05:12:16 +0800 Subject: [PATCH 091/159] Create ArrayFunction.cs --- main/SS/Formula/Functions/ArrayFunction.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 main/SS/Formula/Functions/ArrayFunction.cs diff --git a/main/SS/Formula/Functions/ArrayFunction.cs b/main/SS/Formula/Functions/ArrayFunction.cs new file mode 100644 index 000000000..f6f1aaff9 --- /dev/null +++ b/main/SS/Formula/Functions/ArrayFunction.cs @@ -0,0 +1,19 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public interface ArrayFunction + { + /// + /// - Excel uses the error code #NUM! instead of IEEE NaN, so when numeric functions evaluate to Double#NaN be sure to translate the result to ErrorEval#NUM_ERROR. + /// + /// the evaluated function arguments. Empty values are represented with BlankEval or MissingArgEval, never null + /// row index of the cell containing the formula under evaluation + /// column index of the cell containing the formula under evaluation + /// The evaluated result, possibly an ErrorEval, never null + ValueEval EvaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex); + } +} From 30d1354a3e6620db71aa49e94167f9cd49154608 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 21 Apr 2022 05:13:01 +0800 Subject: [PATCH 092/159] implement XlookupFunction --- main/SS/Formula/Atp/AnalysisToolPak.cs | 1 + main/SS/Formula/Atp/XLookupFunction.cs | 277 +++++++++++++++++++++++ main/SS/Formula/Functions/Hlookup.cs | 2 +- main/SS/Formula/Functions/Lookup.cs | 2 +- main/SS/Formula/Functions/LookupUtils.cs | 271 +++++++++++++++++++--- main/SS/Formula/Functions/Vlookup.cs | 2 +- 6 files changed, 521 insertions(+), 34 deletions(-) create mode 100644 main/SS/Formula/Atp/XLookupFunction.cs diff --git a/main/SS/Formula/Atp/AnalysisToolPak.cs b/main/SS/Formula/Atp/AnalysisToolPak.cs index 9db9f5a20..8f3510e39 100644 --- a/main/SS/Formula/Atp/AnalysisToolPak.cs +++ b/main/SS/Formula/Atp/AnalysisToolPak.cs @@ -179,6 +179,7 @@ public override FreeRefFunction FindFunction(String name) r(m, "WEEKNUM", WeekNum.instance); r(m, "WORKDAY", WorkdayFunction.instance); r(m, "XIRR", null); + r(m, "XLOOKUP", XLookupFunction.instance); r(m, "XNPV", null); r(m, "YEARFRAC", YearFrac.instance); r(m, "YIELD", null); diff --git a/main/SS/Formula/Atp/XLookupFunction.cs b/main/SS/Formula/Atp/XLookupFunction.cs new file mode 100644 index 000000000..8b6d45965 --- /dev/null +++ b/main/SS/Formula/Atp/XLookupFunction.cs @@ -0,0 +1,277 @@ +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Atp +{ + class XLookupFunction : FreeRefFunction, ArrayFunction + { + public static FreeRefFunction instance = new XLookupFunction(ArgumentsEvaluator.instance); + + private ArgumentsEvaluator evaluator; + + private XLookupFunction(ArgumentsEvaluator anEvaluator) + { + // enforces singleton + this.evaluator = anEvaluator; + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + int srcRowIndex = ec.RowIndex; + int srcColumnIndex = ec.ColumnIndex; + return _evaluate(args, srcRowIndex, srcColumnIndex, ec.IsSingleValue); + } + + public ValueEval EvaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) + { + return _evaluate(args, srcRowIndex, srcColumnIndex, false); + } + + private String LaxValueToString(ValueEval eval) + { + return (eval is MissingArgEval) ? "" : OperandResolver.CoerceValueToString(eval); + } + private ValueEval _evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex, bool isSingleValue) + { + if (args.Length < 3) + { + return ErrorEval.VALUE_INVALID; + } + String notFound = null; + if (args.Length > 3) + { + try + { + ValueEval notFoundValue = OperandResolver.GetSingleValue(args[3], srcRowIndex, srcColumnIndex); + String notFoundText = LaxValueToString(notFoundValue); + if (notFoundText != null) + { + String trimmedText = notFoundText.Trim(); + if (trimmedText.Length > 0) + { + notFound = trimmedText; + } + } + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + LookupUtils.MatchMode matchMode = LookupUtils.MatchMode.ExactMatch; + if (args.Length > 4) + { + try + { + ValueEval matchModeValue = OperandResolver.GetSingleValue(args[4], srcRowIndex, srcColumnIndex); + int matchInt = OperandResolver.CoerceValueToInt(matchModeValue); + matchMode = LookupUtils.GetMatchMode(matchInt); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + catch (Exception e) + { + return ErrorEval.VALUE_INVALID; + } + } + LookupUtils.SearchMode searchMode = LookupUtils.SearchMode.IterateForward; + if (args.Length > 5) + { + try + { + ValueEval searchModeValue = OperandResolver.GetSingleValue(args[5], srcRowIndex, srcColumnIndex); + int searchInt = OperandResolver.CoerceValueToInt(searchModeValue); + searchMode = LookupUtils.GetSearchMode(searchInt); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + catch (Exception e) + { + return ErrorEval.VALUE_INVALID; + } + } + return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], notFound, matchMode, searchMode, isSingleValue); + } + private ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval lookupEval, ValueEval indexEval, + ValueEval returnEval, String notFound, LookupUtils.MatchMode matchMode, + LookupUtils.SearchMode searchMode, bool isSingleValue) + { + try + { + ValueEval lookupValue = OperandResolver.GetSingleValue(lookupEval, srcRowIndex, srcColumnIndex); + TwoDEval tableArray = LookupUtils.ResolveTableArrayArg(indexEval); + int matchedRow; + try + { + matchedRow = LookupUtils.XlookupIndexOfValue(lookupValue, LookupUtils.CreateColumnVector(tableArray, 0), matchMode, searchMode); + } + catch (EvaluationException e) + { + if (ErrorEval.NA.Equals(e.GetErrorEval())) + { + if (string.IsNullOrEmpty(notFound)) + { + if (returnEval is AreaEval) { + AreaEval area = (AreaEval)returnEval; + int width = area.Width; + if (isSingleValue || width <= 1) + { + return new StringEval(notFound); + } + return new NotFoundAreaEval(notFound, width); + } else + { + return new StringEval(notFound); + } + } + return ErrorEval.NA; + } + else + { + return e.GetErrorEval(); + } + } + if (returnEval is AreaEval) { + AreaEval area = (AreaEval)returnEval; + if (isSingleValue) + { + return area.GetRelativeValue(matchedRow, 0); + } + return area.Offset(matchedRow, matchedRow, 0, area.Width - 1); + } else + { + return returnEval; + } + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + + class NotFoundAreaEval : AreaEval + { + private int _width; + private string _notFound; + public NotFoundAreaEval(string notFound, int width) + { + _width = width; + _notFound = notFound; + } + public int FirstRow + { + get { return 0; } + } + + public int LastRow + { + get { return 0; } + } + + public int FirstColumn + { + get { return 0; } + } + + public int LastColumn + { + get { return _width - 1; } + } + + public int Width + { + get { return _width; } + } + + public int Height + { + get { return 1; } + } + + public bool IsRow + { + get { return false; } + } + + public bool IsColumn + { + get { return false; } + } + + public int FirstSheetIndex + { + get { return 0; } + } + + public int LastSheetIndex + { + get { return 0; } + } + + public bool Contains(int row, int col) + { + throw new NotImplementedException(); + } + + public bool ContainsColumn(int col) + { + throw new NotImplementedException(); + } + + public bool ContainsRow(int row) + { + throw new NotImplementedException(); + } + + public ValueEval GetAbsoluteValue(int row, int col) + { + if (col == 0) + { + return new StringEval(_notFound); + } + return new StringEval(""); + } + + public TwoDEval GetColumn(int columnIndex) + { + throw null; + } + + public ValueEval GetRelativeValue(int relativeRowIndex, int relativeColumnIndex) + { + return GetAbsoluteValue(relativeRowIndex, relativeColumnIndex); + } + + public TwoDEval GetRow(int rowIndex) + { + throw null; + } + + public ValueEval GetValue(int sheetIndex, int rowIndex, int columnIndex) + { + return GetAbsoluteValue(rowIndex, columnIndex); + } + + public ValueEval GetValue(int rowIndex, int columnIndex) + { + return GetAbsoluteValue(rowIndex, columnIndex); + } + + public bool IsSubTotal(int rowIndex, int columnIndex) + { + return false; + } + + public AreaEval Offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) + { + throw null; + } + } + } +} diff --git a/main/SS/Formula/Functions/Hlookup.cs b/main/SS/Formula/Functions/Hlookup.cs index 059e5b5e2..184f242dd 100644 --- a/main/SS/Formula/Functions/Hlookup.cs +++ b/main/SS/Formula/Functions/Hlookup.cs @@ -59,7 +59,7 @@ public ValueEval Evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) ValueEval lookupValue = OperandResolver.GetSingleValue(args[0], srcCellRow, srcCellCol); AreaEval tableArray = LookupUtils.ResolveTableArrayArg(args[1]); bool IsRangeLookup = LookupUtils.ResolveRangeLookupArg(arg3, srcCellRow, srcCellCol); - int colIndex = LookupUtils.LookupIndexOfValue(lookupValue, LookupUtils.CreateRowVector(tableArray, 0), IsRangeLookup); + int colIndex = LookupUtils.lookupFirstIndexOfValue(lookupValue, LookupUtils.CreateRowVector(tableArray, 0), IsRangeLookup); int rowIndex = LookupUtils.ResolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol); ValueVector resultCol = CreateResultColumnVector(tableArray, rowIndex); return resultCol.GetItem(colIndex); diff --git a/main/SS/Formula/Functions/Lookup.cs b/main/SS/Formula/Functions/Lookup.cs index 07344542c..f21e9bd5f 100644 --- a/main/SS/Formula/Functions/Lookup.cs +++ b/main/SS/Formula/Functions/Lookup.cs @@ -86,7 +86,7 @@ public ValueEval Evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) // Excel seems to handle this by accessing past the end of the result vector. throw new Exception("Lookup vector and result vector of differing sizes not supported yet"); } - int index = LookupUtils.LookupIndexOfValue(lookupValue, lookupVector, true); + int index = LookupUtils.lookupFirstIndexOfValue(lookupValue, lookupVector, true); return resultVector.GetItem(index); } diff --git a/main/SS/Formula/Functions/LookupUtils.cs b/main/SS/Formula/Functions/LookupUtils.cs index 6f841c79a..eae004b67 100644 --- a/main/SS/Formula/Functions/LookupUtils.cs +++ b/main/SS/Formula/Functions/LookupUtils.cs @@ -23,6 +23,8 @@ namespace NPOI.SS.Formula.Functions using NPOI.SS.Formula.Eval; using System.Globalization; using System.Text.RegularExpressions; + using System.Collections.Generic; + using EnumsNET; /** * Common functionality used by VLOOKUP, HLOOKUP, LOOKUP and MATCH @@ -31,6 +33,50 @@ namespace NPOI.SS.Formula.Functions */ internal class LookupUtils { + private static Dictionary matchModeMap = new Dictionary(); + private static Dictionary searchModeMap = new Dictionary(); + + static LookupUtils() + { + foreach (var value in Enums.GetValues()) + { + matchModeMap.Add((int)value, value); + } + foreach (var value in Enums.GetValues()) + { + searchModeMap.Add((int)value, value); + } + } + public static MatchMode GetMatchMode(int m) + { + if (!matchModeMap.ContainsKey(m)) + { + throw new ArgumentException("unknown match mode " + m); + } + return matchModeMap[m]; + } + public static SearchMode GetSearchMode(int s) + { + if (!searchModeMap.ContainsKey(s)) + { + throw new ArgumentException("unknown search mode " + s); + } + return searchModeMap[s]; + } + public enum MatchMode : int + { + ExactMatch = 0, + ExactMatchFallbackToSmallerValue = -1, + ExactMatchFallbackToLargerValue = 1, + WildcardMatch = 2 + } + public enum SearchMode + { + IterateForward = 1, + IterateBackward = -1, + BinarySearchForward = 2, + BinarySearchBackward = -2 + } internal class RowVector : ValueVector { @@ -170,10 +216,10 @@ public static ValueVector CreateVector(RefEval re) private class StringLookupComparer : LookupValueComparerBase { - private String _value; - private Regex _wildCardPattern; - private bool _matchExact; - private bool _isMatchFunction; + protected String _value; + protected Regex _wildCardPattern; + protected bool _matchExact; + protected bool _isMatchFunction; public StringLookupComparer(StringEval se, bool matchExact, bool isMatchFunction) : base(se) @@ -184,22 +230,19 @@ public StringLookupComparer(StringEval se, bool matchExact, bool isMatchFunction _matchExact = matchExact; _isMatchFunction = isMatchFunction; } - - protected override CompareResult CompareSameType(ValueEval other) + protected virtual String ConvertToString(ValueEval other) { StringEval se = (StringEval)other; - - String stringValue = se.StringValue; - if (_wildCardPattern != null) + return se.StringValue; + } + protected override CompareResult CompareSameType(ValueEval other) + { + String stringValue = ConvertToString(other); + if (_wildCardPattern != null && (_isMatchFunction || !_matchExact)) { MatchCollection matcher = _wildCardPattern.Matches(stringValue); bool matches = matcher.Count > 0; - - if (_isMatchFunction || - !_matchExact) - { - return CompareResult.ValueOf(matches); - } + return CompareResult.ValueOf(matches); } return CompareResult.ValueOf(String.Compare(_value, stringValue, true)); @@ -209,6 +252,30 @@ protected override String GetValueAsString() return _value; } } + private class TolerantStringLookupComparer : StringLookupComparer + { + static StringEval ConvertToStringEval(ValueEval eval) + { + if (eval is StringEval) + { + return (StringEval)eval; + } + String sv = OperandResolver.CoerceValueToString(eval); + return new StringEval(sv); + } + + public TolerantStringLookupComparer(ValueEval eval, bool matchExact, bool isMatchFunction) : + base(ConvertToStringEval(eval), matchExact, isMatchFunction) + { + + } + + protected override String ConvertToString(ValueEval other) + { + return OperandResolver.CoerceValueToString(other); + } + } + private class NumberLookupComparer : LookupValueComparerBase { private double _value; @@ -256,10 +323,10 @@ protected override String GetValueAsString() */ public static int ResolveRowOrColIndexArg(ValueEval rowColIndexArg, int srcCellRow, int srcCellCol) { - if(rowColIndexArg == null) { + if (rowColIndexArg == null) { throw new ArgumentException("argument must not be null"); } - + ValueEval veRowColIndexArg; try { veRowColIndexArg = OperandResolver.GetSingleValue(rowColIndexArg, srcCellRow, (short)srcCellCol); @@ -268,11 +335,11 @@ public static int ResolveRowOrColIndexArg(ValueEval rowColIndexArg, int srcCellR throw EvaluationException.InvalidRef(); } int oneBasedIndex; - if(veRowColIndexArg is StringEval) { - StringEval se = (StringEval) veRowColIndexArg; + if (veRowColIndexArg is StringEval) { + StringEval se = (StringEval)veRowColIndexArg; String strVal = se.StringValue; Double dVal = OperandResolver.ParseDouble(strVal); - if(Double.IsNaN(dVal)) { + if (Double.IsNaN(dVal)) { // String does not resolve to a number. Raise #REF! error. throw EvaluationException.InvalidRef(); // This includes text booleans "TRUE" and "FALSE". They are not valid. @@ -301,8 +368,8 @@ public static AreaEval ResolveTableArrayArg(ValueEval eval) return (AreaEval)eval; } - if(eval is RefEval) { - RefEval refEval = (RefEval) eval; + if (eval is RefEval) { + RefEval refEval = (RefEval)eval; // Make this cell ref look like a 1x1 area ref. // It doesn't matter if eval is a 2D or 3D ref, because that detail is never asked of AreaEval. @@ -353,10 +420,10 @@ public static bool ResolveRangeLookupArg(ValueEval rangeLookupArg, int srcCellRo } // TODO move parseBoolean to OperandResolver bool? b = Countif.ParseBoolean(stringValue); - if (b!=null) + if (b != null) { // string Converted to bool OK - return b==true?true:false; + return b == true ? true : false; } //// Even more trickiness: //// Note - even if the StringEval represents a number value (for example "1"), @@ -374,7 +441,7 @@ public static bool ResolveRangeLookupArg(ValueEval rangeLookupArg, int srcCellRo throw new Exception("Unexpected eval type (" + valEval.GetType().Name + ")"); } - public static int LookupIndexOfValue(ValueEval lookupValue, ValueVector vector, bool isRangeLookup) + public static int lookupFirstIndexOfValue(ValueEval lookupValue, ValueVector vector, bool isRangeLookup) { LookupValueComparer lookupComparer = CreateLookupComparer(lookupValue, isRangeLookup, false); int result; @@ -384,7 +451,25 @@ public static int LookupIndexOfValue(ValueEval lookupValue, ValueVector vector, } else { - result = LookupIndexOfExactValue(lookupComparer, vector); + result = lookupFirstIndexOfValue(lookupComparer, vector, MatchMode.ExactMatch); + } + if (result < 0) + { + throw new EvaluationException(ErrorEval.NA); + } + return result; + } + public static int XlookupIndexOfValue(ValueEval lookupValue, ValueVector vector, MatchMode matchMode, SearchMode searchMode) + { + LookupValueComparer lookupComparer = CreateTolerantLookupComparer(lookupValue, true, true); + int result; + if (searchMode == SearchMode.IterateBackward || searchMode == SearchMode.BinarySearchBackward) + { + result = lookupLastIndexOfValue(lookupComparer, vector, matchMode); + } + else + { + result = lookupFirstIndexOfValue(lookupComparer, vector, matchMode); } if (result < 0) { @@ -392,7 +477,6 @@ public static int LookupIndexOfValue(ValueEval lookupValue, ValueVector vector, } return result; } - /** * Finds first (lowest index) exact occurrence of specified value. @@ -401,22 +485,133 @@ public static int LookupIndexOfValue(ValueEval lookupValue, ValueVector vector, * tableArray. For HLOOKUP this Is the first row of the tableArray. * @return zero based index into the vector, -1 if value cannot be found */ - private static int LookupIndexOfExactValue(LookupValueComparer lookupComparer, ValueVector vector) + private static int lookupFirstIndexOfValue(LookupValueComparer lookupComparer, ValueVector vector, MatchMode matchMode) { // Find first occurrence of lookup value int size = vector.Size; + int bestMatchIdx = -1; + ValueEval bestMatchEval = null; for (int i = 0; i < size; i++) { - if (lookupComparer.CompareTo(vector.GetItem(i)).IsEqual) + ValueEval valueEval = vector.GetItem(i); + CompareResult result = lookupComparer.CompareTo(valueEval); + if (result.IsEqual) { return i; } + switch (matchMode) + { + case MatchMode.ExactMatchFallbackToLargerValue: + if (result.IsLessThan) + { + if (bestMatchEval == null) + { + bestMatchIdx = i; + bestMatchEval = valueEval; + } + else + { + LookupValueComparer matchComparer = CreateTolerantLookupComparer(valueEval, true, true); + if (matchComparer.CompareTo(bestMatchEval).IsLessThan) + { + bestMatchIdx = i; + bestMatchEval = valueEval; + } + } + } + break; + case MatchMode.ExactMatchFallbackToSmallerValue: + if (result.IsGreaterThan) + { + if (bestMatchEval == null) + { + bestMatchIdx = i; + bestMatchEval = valueEval; + } + else + { + LookupValueComparer matchComparer = CreateTolerantLookupComparer(valueEval, true, true); + if (matchComparer.CompareTo(bestMatchEval).IsGreaterThan) + { + bestMatchIdx = i; + bestMatchEval = valueEval; + } + } + } + break; + } } - return -1; + return bestMatchIdx; } - + /** + * Finds last (greatest index) matching occurrence of specified value. + * @param lookupComparer the value to be found in column or row vector + * @param vector the values to be searched. For VLOOKUP this is the first column of the + * tableArray. For HLOOKUP this is the first row of the tableArray. + * @param matchMode + * @return zero based index into the vector, -1 if value cannot be found + */ + private static int lookupLastIndexOfValue(LookupValueComparer lookupComparer, ValueVector vector, + MatchMode matchMode) + { + // find last occurrence of lookup value + int size = vector.Size; + int bestMatchIdx = -1; + ValueEval bestMatchEval = null; + for (int i = size - 1; i >= 0; i--) + { + ValueEval valueEval = vector.GetItem(i); + CompareResult result = lookupComparer.CompareTo(valueEval); + if (result.IsEqual) + { + return i; + } + switch (matchMode) + { + case MatchMode.ExactMatchFallbackToLargerValue: + if (result.IsLessThan) + { + if (bestMatchEval == null) + { + bestMatchIdx = i; + bestMatchEval = valueEval; + } + else + { + LookupValueComparer matchComparer = CreateTolerantLookupComparer(valueEval, true, true); + if (matchComparer.CompareTo(bestMatchEval).IsLessThan) + { + bestMatchIdx = i; + bestMatchEval = valueEval; + } + } + } + break; + case MatchMode.ExactMatchFallbackToSmallerValue: + if (result.IsGreaterThan) + { + if (bestMatchEval == null) + { + bestMatchIdx = i; + bestMatchEval = valueEval; + } + else + { + LookupValueComparer matchComparer = CreateTolerantLookupComparer(valueEval, true, true); + if (matchComparer.CompareTo(bestMatchEval).IsGreaterThan) + { + bestMatchIdx = i; + bestMatchEval = valueEval; + } + } + } + break; + } + } + return bestMatchIdx; + } /** * Excel has funny behaviour when the some elements in the search vector are the wrong type. @@ -542,6 +737,20 @@ public static LookupValueComparer CreateLookupComparer(ValueEval lookupValue, bo } throw new ArgumentException("Bad lookup value type (" + lookupValue.GetType().Name + ")"); } + private static LookupValueComparer CreateTolerantLookupComparer(ValueEval lookupValue, bool matchExact, bool isMatchFunction) + { + if (lookupValue == BlankEval.instance) + { + return new TolerantStringLookupComparer(new StringEval(""), matchExact, isMatchFunction); + } + if (lookupValue is BoolEval) { + return new BooleanLookupComparer((BoolEval)lookupValue); + } + if (matchExact && lookupValue is NumberEval) { + return new NumberLookupComparer((NumberEval)lookupValue); + } + return new TolerantStringLookupComparer(lookupValue, matchExact, isMatchFunction); + } } /** * Enumeration to support 4 valued comparison results.

diff --git a/main/SS/Formula/Functions/Vlookup.cs b/main/SS/Formula/Functions/Vlookup.cs index a5c0c7e95..7e612b267 100644 --- a/main/SS/Formula/Functions/Vlookup.cs +++ b/main/SS/Formula/Functions/Vlookup.cs @@ -100,7 +100,7 @@ public class Vlookup : Var3or4ArgFunction ValueEval lookupValue = OperandResolver.GetSingleValue(lookup_value, srcRowIndex, srcColumnIndex); TwoDEval tableArray = LookupUtils.ResolveTableArrayArg(table_array); bool isRangeLookup = LookupUtils.ResolveRangeLookupArg(range_lookup, srcRowIndex, srcColumnIndex); - int rowIndex = LookupUtils.LookupIndexOfValue(lookupValue, LookupUtils.CreateColumnVector(tableArray, 0), isRangeLookup); + int rowIndex = LookupUtils.lookupFirstIndexOfValue(lookupValue, LookupUtils.CreateColumnVector(tableArray, 0), isRangeLookup); int colIndex = LookupUtils.ResolveRowOrColIndexArg(col_index, srcRowIndex, srcColumnIndex); ValueVector resultCol = CreateResultColumnVector(tableArray, colIndex); return resultCol.GetItem(rowIndex); From ab7f2a6675e5e40dd0a71de822d3b454974d618e Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 21 Apr 2022 05:14:10 +0800 Subject: [PATCH 093/159] support evaluating ArrayFunction in OperationEvaluationFactory --- main/SS/Formula/OperationEvaluationContext.cs | 20 ++++-- main/SS/Formula/OperationEvaluatorFactory.cs | 67 +++++++++++++++---- main/SS/Formula/UserDefinedFunction.cs | 8 +++ 3 files changed, 77 insertions(+), 18 deletions(-) diff --git a/main/SS/Formula/OperationEvaluationContext.cs b/main/SS/Formula/OperationEvaluationContext.cs index 06131d73e..8ae90a105 100644 --- a/main/SS/Formula/OperationEvaluationContext.cs +++ b/main/SS/Formula/OperationEvaluationContext.cs @@ -27,7 +27,7 @@ public class OperationEvaluationContext private EvaluationTracker _tracker; private bool _isSingleValue; private WorkbookEvaluator _bookEvaluator; - + private bool _isInArrayContext; public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, IEvaluationWorkbook workbook, int sheetIndex, int srcRowNum, int srcColNum, EvaluationTracker tracker) { @@ -49,15 +49,27 @@ public class OperationEvaluationContext _tracker = tracker; _isSingleValue = isSingleValue; } - + public bool IsArraymode + { + get + { + return _isInArrayContext; + } + set { + _isInArrayContext = value; + } + } public IEvaluationWorkbook GetWorkbook() { return _workbook; } - public bool IsSingleValue() + public bool IsSingleValue { - return _isSingleValue; + get + { + return _isSingleValue; + } } public int RowIndex { diff --git a/main/SS/Formula/OperationEvaluatorFactory.cs b/main/SS/Formula/OperationEvaluatorFactory.cs index af7a820ab..41df31acf 100644 --- a/main/SS/Formula/OperationEvaluatorFactory.cs +++ b/main/SS/Formula/OperationEvaluatorFactory.cs @@ -67,8 +67,7 @@ private static Hashtable InitialiseInstancesMap() return m; } - private static void Add(Hashtable m, OperationPtg ptgKey, - NPOI.SS.Formula.Functions.Function instance) + private static void Add(Hashtable m, OperationPtg ptgKey, Functions.Function instance) { // make sure ptg has single private constructor because map lookups assume singleton keys ConstructorInfo[] cc = ptgKey.GetType().GetConstructors(); @@ -94,26 +93,66 @@ private static Hashtable InitialiseInstancesMap() } NPOI.SS.Formula.Functions.Function result = _instancesByPtgClass[ptg] as NPOI.SS.Formula.Functions.Function; - if (result != null) + FreeRefFunction udfFunc = null; + if (result == null) { - return result.Evaluate(args, ec.RowIndex, (short)ec.ColumnIndex); + if (ptg is AbstractFunctionPtg) + { + AbstractFunctionPtg fptg = (AbstractFunctionPtg)ptg; + int functionIndex = fptg.FunctionIndex; + switch (functionIndex) + { + case NPOI.SS.Formula.Function.FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT: + udfFunc = Indirect.instance; + break; + case NPOI.SS.Formula.Function.FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL: + udfFunc = UserDefinedFunction.instance; + break; + default: + result = FunctionEval.GetBasicFunction(functionIndex); + break; + } + } } - if (ptg is AbstractFunctionPtg) + if (result != null) { - AbstractFunctionPtg fptg = (AbstractFunctionPtg)ptg; - int functionIndex = fptg.FunctionIndex; - switch (functionIndex) + if (result is ArrayFunction) { - case NPOI.SS.Formula.Function.FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT: - return Indirect.instance.Evaluate(args, ec); - case NPOI.SS.Formula.Function.FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL: - return UserDefinedFunction.instance.Evaluate(args, ec); + ArrayFunction func = (ArrayFunction)result; + ValueEval eval = EvaluateArrayFunction(func, args, ec); + if (eval != null) + { + return eval; + } } - - return FunctionEval.GetBasicFunction(functionIndex).Evaluate(args, ec.RowIndex, ec.ColumnIndex); + return result.Evaluate(args, ec.RowIndex, (short)ec.ColumnIndex); + } + else if (udfFunc != null) + { + return udfFunc.Evaluate(args, ec); } throw new Exception("Unexpected operation ptg class (" + ptg.GetType().Name + ")"); } + public static ValueEval EvaluateArrayFunction(ArrayFunction func, ValueEval[] args, + OperationEvaluationContext ec) + { + IEvaluationSheet evalSheet = ec.GetWorkbook().GetSheet(ec.SheetIndex); + IEvaluationCell evalCell = evalSheet.GetCell(ec.RowIndex, ec.ColumnIndex); + if (evalCell != null) + { + if (evalCell.IsPartOfArrayFormulaGroup) + { + // array arguments must be evaluated relative to the function defining range + Util.CellRangeAddress ca = evalCell.ArrayFormulaRange; + return func.EvaluateArray(args, ca.FirstRow, ca.FirstColumn); + } + else if (ec.IsArraymode) + { + return func.EvaluateArray(args, ec.RowIndex, ec.ColumnIndex); + } + } + return null; + } } } \ No newline at end of file diff --git a/main/SS/Formula/UserDefinedFunction.cs b/main/SS/Formula/UserDefinedFunction.cs index 4d5a3df4d..d9ddc3c23 100644 --- a/main/SS/Formula/UserDefinedFunction.cs +++ b/main/SS/Formula/UserDefinedFunction.cs @@ -41,6 +41,14 @@ public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) int nOutGoingArgs = nIncomingArgs - 1; ValueEval[] outGoingArgs = new ValueEval[nOutGoingArgs]; Array.Copy(args, 1, outGoingArgs, 0, nOutGoingArgs); + if (targetFunc is ArrayFunction) { + ArrayFunction func = (ArrayFunction)targetFunc; + ValueEval eval = OperationEvaluatorFactory.EvaluateArrayFunction(func, outGoingArgs, ec); + if (eval != null) + { + return eval; + } + } return targetFunc.Evaluate(outGoingArgs, ec); } } From 9ef3534f95963d93c419ab9dba4412fc3500f3aa Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 21 Apr 2022 05:14:17 +0800 Subject: [PATCH 094/159] Create TestXLookupFunction.cs --- testcases/ooxml/XSSF/TestXLookupFunction.cs | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 testcases/ooxml/XSSF/TestXLookupFunction.cs diff --git a/testcases/ooxml/XSSF/TestXLookupFunction.cs b/testcases/ooxml/XSSF/TestXLookupFunction.cs new file mode 100644 index 000000000..70c66d72c --- /dev/null +++ b/testcases/ooxml/XSSF/TestXLookupFunction.cs @@ -0,0 +1,58 @@ +using NPOI.SS.UserModel; +using NPOI.SS.Util; +using NPOI.Util; +using NPOI.XSSF.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace TestCases.XSSF +{ + [TestFixture] + public class TestXLookupFunction + { + [Test] + public void TestMicrosoftExample1() + { + String formulaText = "XLOOKUP(B2,B5:B14,C5:D14)"; + XSSFWorkbook wb = initWorkbook2(); + XSSFFormulaEvaluator fe = new XSSFFormulaEvaluator(wb); + ISheet sheet = wb.GetSheetAt(0); + IRow row1 = sheet.GetRow(1); + String col1 = CellReference.ConvertNumToColString(2); + String col2 = CellReference.ConvertNumToColString(3); + String cellRef = $"{col1}2:{col2}2"; + sheet.SetArrayFormula(formulaText, CellRangeAddress.ValueOf(cellRef)); + fe.EvaluateAll(); + FileInfo fi = TempFile.CreateTempFile("xlook", ".xlsx"); + using (FileStream file = new FileStream(fi.FullName, FileMode.Open, FileAccess.ReadWrite)) + { + wb.Write(file, false); + } + Assert.AreEqual("Dianne Pugh", row1.GetCell(2).StringCellValue); + //next assertion fails, cell D2 ends up with Dianne Pugh + Assert.AreEqual("Finance", row1.GetCell(3).StringCellValue); + } + private XSSFWorkbook initWorkbook2() + { + XSSFWorkbook wb = new XSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + SS.Util.Utils.AddRow(sheet, 0, null, "Emp Id", "Employee Name", "Department"); + SS.Util.Utils.AddRow(sheet, 1, null, 8389); + SS.Util.Utils.AddRow(sheet, 3, null, "Emp Id", "Employee Name", "Department"); + SS.Util.Utils.AddRow(sheet, 4, null, 4390, "Ned Lanning", "Marketing"); + SS.Util.Utils.AddRow(sheet, 5, null, 8604, "Margo Hendrix", "Sales"); + SS.Util.Utils.AddRow(sheet, 6, null, 8389, "Dianne Pugh", "Finance"); + SS.Util.Utils.AddRow(sheet, 7, null, 4937, "Earlene McCarty", "Accounting"); + SS.Util.Utils.AddRow(sheet, 8, null, 8299, "Mia Arnold", "Operation"); + SS.Util.Utils.AddRow(sheet, 9, null, 2643, "Jorge Fellows", "Executive"); + SS.Util.Utils.AddRow(sheet, 10, null, 5243, "Rose Winters", "Sales"); + SS.Util.Utils.AddRow(sheet, 11, null, 9693, "Carmela Hahn", "Finance"); + SS.Util.Utils.AddRow(sheet, 12, null, 1636, "Delia Cochran", "Accounting"); + SS.Util.Utils.AddRow(sheet, 13, null, 6703, "Marguerite Cervantes", "Marketing"); + return wb; + } + } +} From 6852504558f0808c0b12b193753f21c22f6f5dd8 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 21 Apr 2022 06:15:53 +0800 Subject: [PATCH 095/159] implement XMatch function --- main/SS/Formula/Atp/AnalysisToolPak.cs | 1 + main/SS/Formula/Atp/XMatchFunction.cs | 99 +++++++++++++++ .../main/SS/Formula/Atp/TestXMatchFunction.cs | 119 ++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 main/SS/Formula/Atp/XMatchFunction.cs create mode 100644 testcases/main/SS/Formula/Atp/TestXMatchFunction.cs diff --git a/main/SS/Formula/Atp/AnalysisToolPak.cs b/main/SS/Formula/Atp/AnalysisToolPak.cs index 8f3510e39..e4c3744cd 100644 --- a/main/SS/Formula/Atp/AnalysisToolPak.cs +++ b/main/SS/Formula/Atp/AnalysisToolPak.cs @@ -180,6 +180,7 @@ public override FreeRefFunction FindFunction(String name) r(m, "WORKDAY", WorkdayFunction.instance); r(m, "XIRR", null); r(m, "XLOOKUP", XLookupFunction.instance); + r(m, "XMATCH", XMatchFunction.instance); r(m, "XNPV", null); r(m, "YEARFRAC", YearFrac.instance); r(m, "YIELD", null); diff --git a/main/SS/Formula/Atp/XMatchFunction.cs b/main/SS/Formula/Atp/XMatchFunction.cs new file mode 100644 index 000000000..431542b36 --- /dev/null +++ b/main/SS/Formula/Atp/XMatchFunction.cs @@ -0,0 +1,99 @@ +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Atp +{ + public class XMatchFunction : FreeRefFunction + { + + public static FreeRefFunction instance = new XMatchFunction(ArgumentsEvaluator.instance); + + private ArgumentsEvaluator evaluator; + + private XMatchFunction(ArgumentsEvaluator anEvaluator) + { + // enforces singleton + this.evaluator = anEvaluator; + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + int srcRowIndex = ec.RowIndex; + int srcColumnIndex = ec.ColumnIndex; + return _evaluate(args, srcRowIndex, srcColumnIndex); + } + + private ValueEval _evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) + { + if (args.Length < 2) + { + return ErrorEval.VALUE_INVALID; + } + LookupUtils.MatchMode matchMode = LookupUtils.MatchMode.ExactMatch; + if (args.Length > 2) + { + try + { + ValueEval matchModeValue = OperandResolver.GetSingleValue(args[2], srcRowIndex, srcColumnIndex); + int matchInt = OperandResolver.CoerceValueToInt(matchModeValue); + matchMode = LookupUtils.GetMatchMode(matchInt); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + catch (Exception e) + { + return ErrorEval.VALUE_INVALID; + } + } + LookupUtils.SearchMode searchMode = LookupUtils.SearchMode.IterateForward; + if (args.Length > 3) + { + try + { + ValueEval searchModeValue = OperandResolver.GetSingleValue(args[3], srcRowIndex, srcColumnIndex); + int searchInt = OperandResolver.CoerceValueToInt(searchModeValue); + searchMode = LookupUtils.GetSearchMode(searchInt); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + catch (Exception e) + { + return ErrorEval.VALUE_INVALID; + } + } + return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], matchMode, searchMode); + } + + private ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval lookupEval, ValueEval indexEval, + LookupUtils.MatchMode matchMode, LookupUtils.SearchMode searchMode) + { + try + { + ValueEval lookupValue = OperandResolver.GetSingleValue(lookupEval, srcRowIndex, srcColumnIndex); + TwoDEval tableArray = LookupUtils.ResolveTableArrayArg(indexEval); + ValueVector vector; + if (tableArray.IsColumn) + { + vector = LookupUtils.CreateColumnVector(tableArray, 0); + } + else + { + vector = LookupUtils.CreateRowVector(tableArray, 0); + } + int matchedIdx = LookupUtils.XlookupIndexOfValue(lookupValue, vector, matchMode, searchMode); + return new NumberEval((double)matchedIdx + 1); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + } +} + diff --git a/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs b/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs new file mode 100644 index 000000000..da5e60ca4 --- /dev/null +++ b/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs @@ -0,0 +1,119 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Atp +{ + [TestFixture] + public class TestXMatchFunction + { + [Test] + public void TestMicrosoftExample0() + { + HSSFWorkbook wb = initNumWorkbook("Grape"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(2).CreateCell(5); + Util.Utils.AssertDouble(fe, cell, "XMATCH(E3,C3:C7)", 2); + Util.Utils.AssertError(fe, cell, "XMATCH(\"Gra\",C3:C7)", FormulaError.NA); + } + + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = initNumWorkbook("Gra?"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(2).CreateCell(5); + Util.Utils.AssertDouble(fe, cell, "XMATCH(E3,C3:C7,1)", 2); + Util.Utils.AssertDouble(fe, cell, "XMATCH(E3,C3:C7,-1)", 5); + Util.Utils.AssertDouble(fe, cell, "XMATCH(\"Gra\",C3:C7,1)", 2); + Util.Utils.AssertDouble(fe, cell, "XMATCH(\"Graz\",C3:C7,1)", 3); + Util.Utils.AssertDouble(fe, cell, "XMATCH(\"Graz\",C3:C7,-1)", 2); + } + + [Test] + public void TestMicrosoftExample2() + { + //the result in this example is correct but the description seems wrong from my testing + //the result is based on the position and not a count + HSSFWorkbook wb = initWorkbook2(); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(3).CreateCell(5); + Util.Utils.AssertDouble(fe, cell, "XMATCH(F2,C3:C9,1)", 4); + Util.Utils.AssertDouble(fe, cell, "XMATCH(F2,C3:C9,-1)", 5); + Util.Utils.AssertError(fe, cell, "XMATCH(F2,C3:C9,2)", FormulaError.NA); + Util.Utils.AssertDouble(fe, cell, "XMATCH(35000,C3:C9,1)", 2); + Util.Utils.AssertDouble(fe, cell, "XMATCH(36000,C3:C9,1)", 1); + } + + [Test] + public void TestMicrosoftExample3() + { + HSSFWorkbook wb = initWorkbook3(); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(2).CreateCell(3); + Util.Utils.AssertDouble(fe, cell, "INDEX(C6:E12,XMATCH(B3,B6:B12),XMATCH(C3,C5:E5))", 8492); + + } + + [Test] + public void TestMicrosoftExample4() + { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.CreateSheet().CreateRow(0).CreateCell(0); + Util.Utils.AssertDouble(fe, cell, "XMATCH(4,{5,4,3,2,1})", 2); + Util.Utils.AssertDouble(fe, cell, "XMATCH(4.5,{5,4,3,2,1},1)", 1); + } + private HSSFWorkbook initNumWorkbook(String lookup) + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + SS.Util.Utils.AddRow(sheet, 0); + SS.Util.Utils.AddRow(sheet, 1, null, null, "Product", null, "Product", "Position"); + SS.Util.Utils.AddRow(sheet, 2, null, null, "Apple", null, lookup); + SS.Util.Utils.AddRow(sheet, 3, null, null, "Grape"); + SS.Util.Utils.AddRow(sheet, 4, null, null, "Pear"); + SS.Util.Utils.AddRow(sheet, 5, null, null, "Banana"); + SS.Util.Utils.AddRow(sheet, 6, null, null, "Cherry"); + return wb; + } + + private HSSFWorkbook initWorkbook2() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + SS.Util.Utils.AddRow(sheet, 0); + SS.Util.Utils.AddRow(sheet, 1, null, "Sales Rep", "Total Sales", null, "Bonus", 15000); + SS.Util.Utils.AddRow(sheet, 2, null, "Michael Neipper", 42000); + SS.Util.Utils.AddRow(sheet, 3, null, "Jan Kotas", 35000); + SS.Util.Utils.AddRow(sheet, 4, null, "Nancy Freehafer", 25000); + SS.Util.Utils.AddRow(sheet, 5, null, "Andrew Cencini", 15901); + SS.Util.Utils.AddRow(sheet, 6, null, "Anne Hellung-Larsen", 13801); + SS.Util.Utils.AddRow(sheet, 7, null, "Nancy Freehafer", 12181); + SS.Util.Utils.AddRow(sheet, 8, null, "Mariya Sergienko", 9201); + return wb; + } + + private HSSFWorkbook initWorkbook3() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + SS.Util.Utils.AddRow(sheet, 0); + SS.Util.Utils.AddRow(sheet, 1, null, "Sales Rep", "Month", "Total"); + SS.Util.Utils.AddRow(sheet, 2, null, "Andrew Cencini", "Feb"); + SS.Util.Utils.AddRow(sheet, 3); + SS.Util.Utils.AddRow(sheet, 4, null, "Sales Rep", "Jan", "Feb", "Mar"); + SS.Util.Utils.AddRow(sheet, 5, null, "Michael Neipper", 3174, 6804, 4713); + SS.Util.Utils.AddRow(sheet, 6, null, "Jan Kotas", 1656, 8643, 3445); + SS.Util.Utils.AddRow(sheet, 7, null, "Nancy Freehafer", 2706, 2310, 6606); + SS.Util.Utils.AddRow(sheet, 8, null, "Andrew Cencini", 4930, 8492, 4474); + SS.Util.Utils.AddRow(sheet, 9, null, "Anne Hellung-Larsen", 6394, 9846, 4368); + SS.Util.Utils.AddRow(sheet, 10, null, "Nancy Freehafer", 2539, 8996, 4084); + SS.Util.Utils.AddRow(sheet, 11, null, "Mariya Sergienko", 4468, 5206, 7343); + return wb; + } + } +} \ No newline at end of file From 889787545802997e62847eacc57dd7a1dc06cf58 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 26 Apr 2022 05:47:32 +0800 Subject: [PATCH 096/159] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 612c2f3e4..ce07aee41 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ This project fully supports Ukraine people to fight against Russian army. This i However, I don't wanna stop Russian developers from using NPOI. They are also victims. They cannot change Putin's or Russia Government's mind in any case. But if possible, Russian developers may help impact your government although it's risky. Thank you! +Слава Україні! + NPOI =================== [![NuGet Badge](https://buildstats.info/nuget/NPOI)](https://www.nuget.org/packages/NPOI) From db500c75d15dc37c64e1556f2fa88aa529c71413 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 26 Apr 2022 18:22:08 +0800 Subject: [PATCH 097/159] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce07aee41..00bdf8a85 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This project fully supports Ukraine people to fight against Russian army. This i However, I don't wanna stop Russian developers from using NPOI. They are also victims. They cannot change Putin's or Russia Government's mind in any case. But if possible, Russian developers may help impact your government although it's risky. Thank you! -Слава Україні! +Sláva Ukrayíni! NPOI =================== From dfd030cccbbcad72a9dd17cb22c79e188a4d68d3 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 27 Apr 2022 04:19:17 +0800 Subject: [PATCH 098/159] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 00bdf8a85..4ff64070b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This project fully supports Ukraine people to fight against Russian army. This i However, I don't wanna stop Russian developers from using NPOI. They are also victims. They cannot change Putin's or Russia Government's mind in any case. But if possible, Russian developers may help impact your government although it's risky. Thank you! -Sláva Ukrayíni! +Glory to Ukraine! NPOI =================== From 4e85b803eb29b1de2a85813e1d43baac32daaa81 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 26 Apr 2022 07:46:58 +0800 Subject: [PATCH 099/159] fix sortedlist remove error --- openxml4Net/OPC/PackageRelationshipCollection.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openxml4Net/OPC/PackageRelationshipCollection.cs b/openxml4Net/OPC/PackageRelationshipCollection.cs index a22e33612..48c236fc1 100644 --- a/openxml4Net/OPC/PackageRelationshipCollection.cs +++ b/openxml4Net/OPC/PackageRelationshipCollection.cs @@ -270,7 +270,8 @@ public void RemoveRelationship(String id) if (relationshipsByType.Values[i] == rel) relationshipsByType.RemoveAt(i); } - internalRelationshipsByTargetName.Values.Remove(rel); + + internalRelationshipsByTargetName.RemoveAt(internalRelationshipsByTargetName.IndexOfValue(rel)); } } } From d62011ebf31b6a2765390dcb59a5e028da3e36f1 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 26 Apr 2022 08:07:05 +0800 Subject: [PATCH 100/159] Update SheetDataWriterTests.cs From 285c4c2232644d24f03d5bc6b129b2dd5e20144e Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Wed, 27 Apr 2022 19:12:50 +0800 Subject: [PATCH 101/159] update npoi version to 2.6.0 --- OpenXmlFormats/Properties/AssemblyInfo.cs | 4 ++-- main/Properties/AssemblyInfo.cs | 4 ++-- ooxml/Properties/AssemblyInfo.cs | 4 ++-- openxml4Net/Properties/AssemblyInfo.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/OpenXmlFormats/Properties/AssemblyInfo.cs b/OpenXmlFormats/Properties/AssemblyInfo.cs index 0fdba96df..a318a3782 100644 --- a/OpenXmlFormats/Properties/AssemblyInfo.cs +++ b/OpenXmlFormats/Properties/AssemblyInfo.cs @@ -33,8 +33,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.5.6.0")] -[assembly: AssemblyFileVersion("2.5.6.0")] +[assembly: AssemblyVersion("2.6.0.0")] +[assembly: AssemblyFileVersion("2.6.0.0")] [assembly: AssemblyInformationalVersion("2.0.0.0")] #if NETSTANDARD2_1 || NETSTANDARD2_0 || NET40 [assembly: AllowPartiallyTrustedCallers] diff --git a/main/Properties/AssemblyInfo.cs b/main/Properties/AssemblyInfo.cs index f4ea20ca3..a68e2de2a 100644 --- a/main/Properties/AssemblyInfo.cs +++ b/main/Properties/AssemblyInfo.cs @@ -33,8 +33,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.5.6.0")] -[assembly: AssemblyFileVersion("2.5.6.0")] +[assembly: AssemblyVersion("2.6.0.0")] +[assembly: AssemblyFileVersion("2.6.0.0")] [assembly: AssemblyInformationalVersion("2.0.0.0")] [assembly: InternalsVisibleTo("NPOI.TestCases, PublicKey=002400000480000094000000060200000024000052534131000400000100010095ccd95af3b39d8bc20544d3f47fd24b53ebc5ccb693eaed116290629f8cd882c827ebd511ad59449224f0718d3f9d03b64945a6c8b6644266001b8c8426185330e3d96da70ae16d4acc21b8d4d480f1385c7e924273179375aa88f81380a72fb115712a313379d16aed4aa36208ee3b4a5dd785b06a07b2d868e3227f4495b5", AllInternalsVisible = true)] diff --git a/ooxml/Properties/AssemblyInfo.cs b/ooxml/Properties/AssemblyInfo.cs index 80cb2551b..a24a6643e 100644 --- a/ooxml/Properties/AssemblyInfo.cs +++ b/ooxml/Properties/AssemblyInfo.cs @@ -33,8 +33,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.5.6.0")] -[assembly: AssemblyFileVersion("2.5.6.0")] +[assembly: AssemblyVersion("2.6.0.0")] +[assembly: AssemblyFileVersion("2.6.0.0")] [assembly: AssemblyInformationalVersion("2.0.0.0")] //[assembly: InternalsVisibleTo("ooxml.Testcases")] diff --git a/openxml4Net/Properties/AssemblyInfo.cs b/openxml4Net/Properties/AssemblyInfo.cs index 393b36675..eb6e36ed4 100644 --- a/openxml4Net/Properties/AssemblyInfo.cs +++ b/openxml4Net/Properties/AssemblyInfo.cs @@ -33,8 +33,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.5.6.0")] -[assembly: AssemblyFileVersion("2.5.6.0")] +[assembly: AssemblyVersion("2.6.0.0")] +[assembly: AssemblyFileVersion("2.6.0.0")] [assembly: AssemblyInformationalVersion("2.0.0.0")] #if NETSTANDARD2_1 || NETSTANDARD2_0 || NET40 [assembly: AllowPartiallyTrustedCallers] From f7dd0a6ebed6cb763502eeeab5694f3e6076351e Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 28 Apr 2022 07:10:44 +0800 Subject: [PATCH 102/159] POI 62254 OFFSET function fails when 2nd or 3rd arguments are missing --- main/SS/Formula/Functions/Offset.cs | 10 ++++--- .../main/SS/Formula/Functions/TestOffset.cs | 17 +++++++++++- testcases/ooxml/XSSF/TestXSSFOffset.cs | 27 +++++++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 testcases/ooxml/XSSF/TestXSSFOffset.cs diff --git a/main/SS/Formula/Functions/Offset.cs b/main/SS/Formula/Functions/Offset.cs index 26ad32e26..b21b37dfc 100644 --- a/main/SS/Formula/Functions/Offset.cs +++ b/main/SS/Formula/Functions/Offset.cs @@ -225,7 +225,7 @@ public AreaEval Offset(int relFirstRowIx, int relLastRowIx,int relFirstColIx, in public ValueEval Evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { - if (args.Length < 3 || args.Length > 5) + if (args.Length < 1 || args.Length > 5) { return ErrorEval.VALUE_INVALID; } @@ -233,12 +233,14 @@ public ValueEval Evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) try { BaseRef baseRef = EvaluateBaseRef(args[0]); - int rowOffset = EvaluateIntArg(args[1], srcCellRow, srcCellCol); - int columnOffset = EvaluateIntArg(args[2], srcCellRow, srcCellCol); + // optional arguments + // If offsets are omitted, it is assumed to be 0. + int rowOffset = (args[1] is MissingArgEval) ? 0 : EvaluateIntArg(args[1], srcCellRow, srcCellCol); + int columnOffset = (args[2] is MissingArgEval) ? 0 : EvaluateIntArg(args[2], srcCellRow, srcCellCol); int height = baseRef.Height; int width = baseRef.Width; // optional arguments - // If height or width is omitted, it is assumed to be the same height or width as reference. + // If height or width are omitted, it is assumed to be the same height or width as reference. switch (args.Length) { case 5: diff --git a/testcases/main/SS/Formula/Functions/TestOffset.cs b/testcases/main/SS/Formula/Functions/TestOffset.cs index e36a52d20..69ddb5755 100644 --- a/testcases/main/SS/Formula/Functions/TestOffset.cs +++ b/testcases/main/SS/Formula/Functions/TestOffset.cs @@ -17,9 +17,10 @@ namespace TestCases.SS.Formula.Functions { - + using NPOI.HSSF.UserModel; using NPOI.SS.Formula.Eval; using NPOI.SS.Formula.Functions; + using NPOI.SS.UserModel; using NUnit.Framework; /** @@ -103,6 +104,20 @@ public void TestLinearOffsetRange() Assert.IsTrue(lor.IsOutOfBounds(0, 16383)); Assert.IsFalse(lor.IsOutOfBounds(0, 65535)); } + + [Test] + public void TestOffsetWithEmpty23Arguments() + { + IWorkbook workbook = new HSSFWorkbook(); + ICell cell = workbook.CreateSheet().CreateRow(0).CreateCell(0); + cell.SetCellFormula("OFFSET(B1,,)"); + string value = "EXPECTED_VALUE"; + ICell valueCell = cell.Row.CreateCell(1); + valueCell.SetCellValue(value); + workbook.GetCreationHelper().CreateFormulaEvaluator().EvaluateAll(); + Assert.AreEqual(CellType.String, cell.CachedFormulaResultType); + Assert.AreEqual(value, cell.StringCellValue); + } } } \ No newline at end of file diff --git a/testcases/ooxml/XSSF/TestXSSFOffset.cs b/testcases/ooxml/XSSF/TestXSSFOffset.cs new file mode 100644 index 000000000..4a95cc398 --- /dev/null +++ b/testcases/ooxml/XSSF/TestXSSFOffset.cs @@ -0,0 +1,27 @@ +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.XSSF +{ + [TestFixture] + class TestXSSFOffset + { + [Test] + public void TestOffsetWithEmpty23Arguments() + { + IWorkbook workbook = new XSSFWorkbook(); + ICell cell = workbook.CreateSheet().CreateRow(0).CreateCell(0); + cell.SetCellFormula("OFFSET(B1,,)"); + String value = "EXPECTED_VALUE"; + ICell valueCell = cell.Row.CreateCell(1); + valueCell.SetCellValue(value); + workbook.GetCreationHelper().CreateFormulaEvaluator().EvaluateAll(); + Assert.AreEqual(CellType.String, cell.CachedFormulaResultType); + Assert.AreEqual(value, cell.StringCellValue); + } + } +} From 2bbac07e9b9c869f19799da099c2e796edccf67f Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 28 Apr 2022 07:28:26 +0800 Subject: [PATCH 103/159] POI 63984 AND / OR should treat missing parameters as FALSE --- .../Functions/Boolean/BooleanFunction.cs | 12 ++++++------ .../BooleanFunctionsTestCaseData.xls | Bin 0 -> 30208 bytes 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 testcases/test-data/spreadsheet/BooleanFunctionsTestCaseData.xls diff --git a/main/SS/Formula/Functions/Boolean/BooleanFunction.cs b/main/SS/Formula/Functions/Boolean/BooleanFunction.cs index f9a8be222..41d76d4cd 100644 --- a/main/SS/Formula/Functions/Boolean/BooleanFunction.cs +++ b/main/SS/Formula/Functions/Boolean/BooleanFunction.cs @@ -43,7 +43,7 @@ private bool Calculate(ValueEval[] args) { bool result = InitialResultValue; - bool atleastOneNonBlank = false; + bool atLeastOneNonBlank = false; /* * Note: no short-circuit bool loop exit because any ErrorEvals will override the result @@ -65,7 +65,7 @@ private bool Calculate(ValueEval[] args) if (tempVe != null) { result = PartialEvaluate(result, Convert.ToBoolean(tempVe, CultureInfo.InvariantCulture)); - atleastOneNonBlank = true; + atLeastOneNonBlank = true; } } } @@ -84,7 +84,7 @@ private bool Calculate(ValueEval[] args) if (tempVe != null) { result = PartialEvaluate(result, tempVe.Value); - atleastOneNonBlank = true; + atLeastOneNonBlank = true; } } continue; @@ -96,7 +96,7 @@ private bool Calculate(ValueEval[] args) //} if (arg == MissingArgEval.instance) { - tempVe = null; // you can leave out parameters, they are simply ignored + tempVe = false; // missing parameters are treated as FALSE } else { @@ -107,11 +107,11 @@ private bool Calculate(ValueEval[] args) if (tempVe != null) { result = PartialEvaluate(result, Convert.ToBoolean(tempVe, CultureInfo.InvariantCulture)); - atleastOneNonBlank = true; + atLeastOneNonBlank = true; } } - if (!atleastOneNonBlank) + if (!atLeastOneNonBlank) { throw new EvaluationException(ErrorEval.VALUE_INVALID); } diff --git a/testcases/test-data/spreadsheet/BooleanFunctionsTestCaseData.xls b/testcases/test-data/spreadsheet/BooleanFunctionsTestCaseData.xls new file mode 100644 index 0000000000000000000000000000000000000000..44298f4f68203f929e7ff55e339fa4f6322f6626 GIT binary patch literal 30208 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 Date: Thu, 28 Apr 2022 08:53:19 +0800 Subject: [PATCH 104/159] POI 61063 Use of arrays of values leads to non empty stack --- main/SS/Formula/Eval/RefListEval.cs | 33 ++++++++++ main/SS/Formula/Functions/Logical/Isref.cs | 2 +- main/SS/Formula/Functions/Rank.cs | 61 +++++++++++++++--- main/SS/Formula/WorkbookEvaluator.cs | 9 ++- .../ooxml/XSSF/UserModel/TestXSSFBugs.cs | 41 ++++++++---- testcases/test-data/spreadsheet/61063.xlsx | Bin 0 -> 9151 bytes 6 files changed, 123 insertions(+), 23 deletions(-) create mode 100644 main/SS/Formula/Eval/RefListEval.cs create mode 100644 testcases/test-data/spreadsheet/61063.xlsx diff --git a/main/SS/Formula/Eval/RefListEval.cs b/main/SS/Formula/Eval/RefListEval.cs new file mode 100644 index 000000000..cfe3dec60 --- /dev/null +++ b/main/SS/Formula/Eval/RefListEval.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Eval +{ + public class RefListEval:ValueEval + { + private List list = new List(); + + public RefListEval(ValueEval v1, ValueEval v2) + { + Add(v1); + Add(v2); + } + + private void Add(ValueEval v) + { + // flatten multiple nested RefListEval + if (v is RefListEval) { + list.AddRange(((RefListEval)v).list); + } else + { + list.Add(v); + } + } + + public List GetList() + { + return list; + } + } +} diff --git a/main/SS/Formula/Functions/Logical/Isref.cs b/main/SS/Formula/Functions/Logical/Isref.cs index b830b2ebf..7af474edb 100644 --- a/main/SS/Formula/Functions/Logical/Isref.cs +++ b/main/SS/Formula/Functions/Logical/Isref.cs @@ -30,7 +30,7 @@ public class Isref : Fixed1ArgFunction { public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { - if (arg0 is RefEval || arg0 is AreaEval) + if (arg0 is RefEval || arg0 is AreaEval || arg0 is RefListEval) { return BoolEval.TRUE; } diff --git a/main/SS/Formula/Functions/Rank.cs b/main/SS/Formula/Functions/Rank.cs index 9d506be5a..d85862c5f 100644 --- a/main/SS/Formula/Functions/Rank.cs +++ b/main/SS/Formula/Functions/Rank.cs @@ -18,6 +18,7 @@ */ using System; +using System.Collections.Generic; using NPOI.SS.Formula.Eval; namespace NPOI.SS.Formula.Functions @@ -52,21 +53,23 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva { throw new EvaluationException(ErrorEval.NUM_ERROR); } + if (arg1 is RefListEval) + { + return eval(result, ((RefListEval)arg1), true); + } aeRange = ConvertRangeArg(arg1); + return eval(result, aeRange, true); } catch (EvaluationException e) { return e.GetErrorEval(); } - return eval(srcRowIndex, srcColumnIndex, result, aeRange, true); } public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2) { - - AreaEval aeRange; double result; - bool order = false; + try { ValueEval ve = OperandResolver.GetSingleValue(arg0, srcRowIndex, srcColumnIndex); @@ -75,8 +78,7 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva { throw new EvaluationException(ErrorEval.NUM_ERROR); } - aeRange = ConvertRangeArg(arg1); - + bool order = false; ve = OperandResolver.GetSingleValue(arg2, srcRowIndex, srcColumnIndex); int order_value = OperandResolver.CoerceValueToInt(ve); if (order_value == 0) @@ -89,17 +91,21 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva } else throw new EvaluationException(ErrorEval.NUM_ERROR); + if (arg1 is RefListEval) + { + return eval(result, ((RefListEval)arg1), order); + } + AreaEval aeRange = ConvertRangeArg(arg1); + return eval(result, aeRange, order); } catch (EvaluationException e) { return e.GetErrorEval(); } - return eval(srcRowIndex, srcColumnIndex, result, aeRange, order); } - private static ValueEval eval(int srcRowIndex, int srcColumnIndex, double arg0, AreaEval aeRange, bool descending_order) + private static ValueEval eval(double arg0, AreaEval aeRange, bool descending_order) { - int rank = 1; int height = aeRange.Height; int width = aeRange.Width; @@ -107,7 +113,6 @@ private static ValueEval eval(int srcRowIndex, int srcColumnIndex, double arg0, { for (int c = 0; c < width; c++) { - Double value = GetValue(aeRange, r, c); if (Double.IsNaN(value)) continue; if (descending_order && value > arg0 || !descending_order && value < arg0) @@ -118,7 +123,43 @@ private static ValueEval eval(int srcRowIndex, int srcColumnIndex, double arg0, } return new NumberEval(rank); } + private static ValueEval eval(double arg0, RefListEval aeRange, bool descending_order) + { + int rank = 1; + List replaceList = new List(); + for (int i = 0; i < aeRange.GetList().Count; i++) + { + ValueEval ve = aeRange.GetList()[i]; + if (ve is RefEval) + { + { + replaceList.Add(i); + } + } + foreach (var index in replaceList) + { + ValueEval targetVe = aeRange.GetList()[i]; + aeRange.GetList()[index] = ((RefEval)targetVe).GetInnerValueEval(((RefEval)ve).FirstSheetIndex); + } + Double value; + if (ve is NumberEval) + { + value = ((NumberEval)ve).NumberValue; + } + else + { + continue; + } + + if (descending_order && value > arg0 || !descending_order && value < arg0) + { + rank++; + } + } + + return new NumberEval(rank); + } private static Double GetValue(AreaEval aeRange, int relRowIndex, int relColIndex) { diff --git a/main/SS/Formula/WorkbookEvaluator.cs b/main/SS/Formula/WorkbookEvaluator.cs index f0eb7e97e..07bf3da1e 100644 --- a/main/SS/Formula/WorkbookEvaluator.cs +++ b/main/SS/Formula/WorkbookEvaluator.cs @@ -555,7 +555,7 @@ public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) Ptg ptg = ptgs[i]; if (dbgEvaluationOutputIndent > 0) { - EVAL_LOG.Log(POILogger.INFO, dbgIndentStr + " * ptg " + i + ": " + ptg.ToString()); + EVAL_LOG.Log(POILogger.INFO, dbgIndentStr + " * ptg " + i + ": " + ptg.ToString()+", stack:"+stack); } if (ptg is AttrPtg) { @@ -648,6 +648,13 @@ public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) continue; } } + if (ptg is UnionPtg) + { + ValueEval v2 = stack.Pop(); + ValueEval v1 = stack.Pop(); + stack.Push(new RefListEval(v1, v2)); + continue; + } if (ptg is ControlPtg) { // skip Parentheses, Attr, etc diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs index 918fc02c6..24b78653c 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs @@ -300,7 +300,7 @@ public void Bug48539() { wb.Close(); } - + } /** @@ -690,10 +690,10 @@ public void Test49966() sheet.GetRow(1).GetCell(0).SetCellFormula("A1"); // stay sheet.GetRow(2).GetCell(0).SetCellFormula(null); // go sheet.GetRow(3).GetCell(0).SetCellType(CellType.Formula); // stay - + XSSFTestDataSamples.WriteOutAndReadBack(wb1).Close(); sheet.GetRow(4).GetCell(0).SetCellType(CellType.String); // go - + XSSFTestDataSamples.WriteOutAndReadBack(wb1).Close(); validateCells(sheet); @@ -701,14 +701,14 @@ public void Test49966() sheet.GetRow(5).GetCell(0) // go ); validateCells(sheet); - + XSSFTestDataSamples.WriteOutAndReadBack(wb1).Close(); sheet.GetRow(6).GetCell(0).SetCellType(CellType.Blank); // go - + XSSFTestDataSamples.WriteOutAndReadBack(wb1).Close(); sheet.GetRow(7).GetCell(0).SetCellValue((String)null); // go - + XSSFTestDataSamples.WriteOutAndReadBack(wb1).Close(); // Save and check @@ -1497,8 +1497,8 @@ public void Test51963() * eg =SUM($Sheet1.C1:$Sheet4.C1) * DISABLED As we can't currently Evaluate these */ - [Ignore("by poi")] - [Test] + [Ignore("by poi")] + [Test] public void Test48703() { XSSFWorkbook wb = XSSFTestDataSamples.OpenSampleWorkbook("48703.xlsx"); @@ -2644,7 +2644,8 @@ public void TestBug56295_MergeXlslsWithStyles() * Excel treats this as not-bulleted, so now do we */ [Test] - public void TestBug57826() { + public void TestBug57826() + { XSSFWorkbook workbook = XSSFTestDataSamples.OpenSampleWorkbook("57826.xlsx"); Assert.IsTrue(workbook.NumberOfSheets >= 1, "no sheets in workbook"); @@ -2664,7 +2665,7 @@ public void TestBug56295_MergeXlslsWithStyles() // No bulleting info included Assert.AreEqual("test ok", text); - + workbook.Close(); } @@ -3195,7 +3196,7 @@ private void CreateXls() ICellStyle style = workbook.CreateCellStyle(); style.Rotation = ((short)-90); cell1.CellStyle = (style); - workbook.Write(fileOut,false); + workbook.Write(fileOut, false); fileOut.Close(); workbook.Close(); } @@ -3359,6 +3360,24 @@ public void TestWorkdayFunction() String result = form.FormatCellValue(cell, evaluator); Assert.AreEqual("09 Mar 2016", result); } + + [Test] + public void Bug61063() + { + IWorkbook wb = XSSFTestDataSamples.OpenSampleWorkbook("61063.xlsx"); + + IFormulaEvaluator eval = wb.GetCreationHelper().CreateFormulaEvaluator(); + ISheet s = wb.GetSheetAt(0); + + IRow r = s.GetRow(3); + ICell c = r.GetCell(0); + Assert.AreEqual(CellType.Formula, c.CellType); + eval.DebugEvaluationOutputForNextEval = true; + CellValue cv = eval.Evaluate(c); + Assert.IsNotNull(cv); + Assert.AreEqual(2.0, cv.NumberValue, 0.00001); + wb.Close(); + } } } \ No newline at end of file diff --git a/testcases/test-data/spreadsheet/61063.xlsx b/testcases/test-data/spreadsheet/61063.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..7fee28d327dc65c9748ceab014b7c361b432d7c7 GIT binary patch literal 9151 zcmeHNWmHsM+a9_b=>`GmMv(^Ta_Ev85G01~PNf|{fkC>vyHlh=NhJj7E|L63-{*T( z9^bXTpWj;Vv)4Lj)~s{xYtEj1UHiWFy^oqA0wN&*8Gs4^0B8Yyp08uw;Q#;{WB`Bw zfC{fK1-5fGw{teq@US;`GURZ#wV}yGglA3%z{BqUr~McI1B3Ak3SC@;y?aOt87o?| zHU!e3PaL^VHru}exE~k}j5Dl&oV>hLtHj>slXnHq^Ho1syYQ7y2H$@&C8QhB_bxf* zq>4(hU9O0QcVy;b`$5Z7G-Q+b(Mj=P0e>6e##XXn0c@@eyPyZGQua70S!@|ZiMlAE z7U)~nc`EiGCc(@GCs|T;-=ZdeF!dtXk4=Uu%(stb=sa^jgd)hV)8Rsv zBH-F@@!;D9{R^I_*nln>1lQ)-lJy5`KzB88hdR(1d*AQ^GmKDAMWK<065J{AHD2t~ zazP#Ty)j_}?DCl6+=ng5Hq{xol6oS#m%==+05f1YZ9Iof$$Zp2)R<#w%S;)Cm&Gv$LH5 zPw^8;4}IE{cxHCmnqK*2X7!T6=|bS8vTSA&_1Z5EzO2HLcvvaS=mo(eGfWdtX*X%x zMYDw**CxvmvFW0OZzwDMF6Y?s%G9%SX zHNd%2oG)Is)s_^>ELCYzQ_S1Sz?ZSCO;hV+s0ln1Ip~ z?;5^d#nU?L*$b|b`wcO))Kl)%-lw#LVt=Go_7S<$>UekD~T&``CL zi_k~#K#b4lwOtUq!n9l@nFqHhjqvM}XnyCNaaZi?mO6SRCBB1#ugk~VDODV=`ea_J zmaCAxnQ+H{4ByBnx#^=KHp%JsHU9oc&Eh-Dw;N~_hD3FO5%bes!-)tI@H>urUJJvd zPUcuxZUhs7y~^e`T_UxvBL>z17}^MDG8GKrJd^eX8%ZX1H(e%$3a|D-?@HlzMi__) zzcMd;sC22;(ExU=Za?3z^;yQ+6lGB$bi3A-!=cN&SDPC~jWAae^G2iICs9WIUHI5a z(_MI$`Fm|LUc|10tVws}iDGHPqk`vsjL_L1*yRxA;;2QQ^10%nuZMlmLN{6#Ra|yB zMRzpvqLA5O!#ds$_mjhI<4uvqIi2)IQILofV_j*I#L<~+=vgl9uH>6TNK>Cx)bDU+pV`0WMhy1&F{V^2ImgctR zoIlRo-!;Cis|qH6MA$}uKq=z|He+%nlXWSat{}GrW=^wjIy6)5sfaXHC6h9pVAaSn zFi1-n#MV2Ai|1&428t@DqlUjPYlkL%I#F>|h6upJozTv_`8ITMGvA!l;=3%-J&K11 zIjrCLP&O$nZJHKiMN!@IuTdN#E6zVMAfs5f5qk^EklfqO)BCsv9@ST4*d2**YiNo|+ z|7>St>$jY(airyq!liqGkB6)0!PTWZE5}&=1w5rwTP|KFDXDPA3n-c;Y>$$ou4P){ z#@PI~&5~T}CX4wEyaTTF+7L!`A&;}5oG)x2dmPjZ&JLlfH)osl#}^Z2MxxivWSkLaMy)qz zYiXiI$dc&n44wx)aYkt(9tU5T+I%kR;al1F^HHhCpUXKzDYB%f0_=LZN>@M(9JQt- zrklm>w4sWjW6RTt64L2849C5zsykra!fYsFoUa^UQ& zo*-f1JateTG8hc6mx_!jba!*uuCEnTOt-U;*qCl5$Cn*M2sH~qz$XYfrbxpFW_%J+ zplsSzH?r?%LjRPNA7qsyAL_h12;623~LMLndpSL+oP&Lm9ExVuK4uZG(sujHIBp=)st%M}Zvt_6z-C8J zfza8D(!z!PeM7^CD9c}4IysK! zq3-u07DHb9&u%z_9Jn9oMn=y)DQcKKj*W?{6%4525tc!7H{7(tuL9UXz33LcBFh`{2+{{#U{9zKEDQXh8AjQUwz#1D? z+@yWHMi`%XV5e`kq{Lge_`3u?PjQR#yk6h!2TC$#qJMtZ9WFvA8;s?GWiaK{6efx4 zqOLs7V{D(dJmRCuYe~4yn3o7vxjXRn2^7K4ZOtr>G~RKm+{%*YVvTNfr^8gcW^3rs zW{;gElZnVyi+%vzSZf+x&uX?U7nS(qGus{(tLE9kUTYV~S$jU4l!M@yyhJ`!eYLrDX}{XGMGuFp*RCS7d`3OcIt)CofwXXJbD_j)t2`i^|gx>#JNJhS66 zQ%E)rjM0Rb!V%%}0Q7?&B^K6t)o zu}Fxj>d|*cU)JsR+%a#%*vv*R43{7f0pGJH%_+Ijwwj?7Kc)g4GxrSDN|G+`zN_!S z(fuN*>RvZWH;9|i5%L8n&ZRRmN_cqk<@!=DTv|wD*2G_uil!fWGSl{fYFVHLz>tUw zq}9)~E70I!cYnjPO=F+iyUy=GLY~lw+DOLXk;id(so-H7M1eMNMvOdJ!(T2@b%bjf zEN!1$fAfBpgVJjL5^2rl<8GrKNWTV@-V0S_e?Iv+ zn)N%9yj~eqe$Mxj+*U1xY^>|H#CV&M^D5bTWyntRY}*qtE$b~Dcy&IXD_B7DuM6X5 zFFLl?m=)eEVt;BX$^VjZsKlCoGCbz@QOw^~@ACv{(Z(b8qAp~Ji6Ci;;b80BwB`7v zN*=lUctr&*8p??p|7*JFMB!x-lc|L_Mk9IOKmutTy)Jhjy^vn_ldrub_s>gx`WpED zxihAx%fgNI)gKMV|6#m%{~53UKjZb`(S1A|n6`khW#E6%*AJH%A8!bTauJ5^G3|)a z7}Ws717dR}#P8s17Ue3de|51PgexW)v|+NkzQQgCU8&{77cH9|HLs;i;Vy^@jL!rJ zNYU93WKg=i#bDw56;iuh7g`|iNBcGUvGVuVA0UsN_`c3K<}l~+$k7nZLVQu z*gaM;Iwiun9!PgD3L}8>*vySD9!>ZBF%f=pZe-~W5y1tkufgnYg&J>b&@Oa0LYa8n7XknbQ0)9MAcK5)P zP_4408t2&!nzW|=;IKWz6)5v_Idaz+K3fl#MV`#$U?tz=v%T~dow^MjKix}a_pKqm z)vKAD7|NaTB;pCm$^M(1D(;=MO2Qc?xZqUwSR_uQ)QsFc31yV{y(`XBQdVyiYf`!* zCF)m3Vn%FN{f|~vD~%z_oJ`l|N&~)(9TBUtQhoAn#etkk+L`>(!H(q!oc>f-hX#=) zo1Ss2f=8oAPmk=mMW?#^6KV=Q%&L09#G@o5J!A0-Gs~H6z`QnzbK*Zu(xZ{(7~ZhS zIRJJL{%ewUvNUlt2dO(dTG>5y`Yw3P0SWX@E?n`kmPN|WZQD!?JgeC^Orr>xL(_;5 z`L~#vb%FyU%OhKLprZ_qg37R?m3CY(Kf}C8b85^4RwT1yyFrrhrJS*0beOL6kh`w1 z^as~Ety(!G^TH_iw@s&kLX~@3dQ6!3FRDRa4!h4V7?GPUVTt4KDJ36s;d)ry#)7O$rEbY8K3{g(( z)e_k80o_6|nUE7aa+b3WdulCU|D(Ak`n~mTiq8yTUk4E-er?u$7jB1d#ZK^ZR-ddIJ-%@%fvD&c`;$ zT2Xc=@>6<|@)EQ({N+90e(-m6Wb7UmoQgS&_ThN>48-`!>7}uyI%j83uq1NT+6lo( zTh7Tgj@ej4^BTed<4#j{Ss%UM0@}QbYCqxVLj*)hG7bvuuWwZV=|pRfgGUzvj#@sI zJqbo(N7ovcslRgJ?#Q}|y3_n}jDPc?nAH?5zVIyPb!jfnHKwa8ClMK@%zKj5#Rb)^ zvVk3SBBTiW7E}wPX^;g-Rqqtz$qOf?)irZ(N5kZ>Rr?Y8axL%#+?r5>j z1;z9~`1;z3qn{W>7iqz`IA1dW!Yl*FtmTy9isbo;t&0@BI4gXjP)jYHh)pkkO8db? z@CW2n<&uv3a02@b=4ca0vk&vodSC5G0LeYV4nNllK!p7G9v0ub$bQ&vMX_Ll7VWsn zyz8Gq^ z$)LF}l8a-Oj+oATT0A$3LM2_AO#R=b@OEOVX+s_F5`K+P+ho*HuOk-dBdD1JJu1<^ z$N?kmhn@Dr?3K879Gl01bn(9T4HHe_Efjd&A# z)W`NQ+%Q-$K;xW6X;T5_(Ic($-w^&Z>5XNEB&Wj&s13V{`zrxW?Ct;IAB=c^wUoFH zyE!h*&^?4x?9kn)lx&hvajnPII-^I9 zNWGUx^p2_O`_gPBix{kI(t*?jp>E{o6ZLT!r6nB~#;N4T48$JS5}yM`wRH?@I73aH z4P1`3CRh?vIYkosUIo;!AOx`51_@~&rsU7nx=aENHcqsyP31CjwxHYswa3V}&H%y$I@Sz?~t&KTQl-r_tN* zuJ@Ts#H_F;c;rJ~XL--|Q(2lcNy19$W75uzKr5TF6GzL%#mWjn5-Xkh6*l$W1#xsCH*lNHa>q(N_>xVDVX z-}OV|;BiKX=op=K1S)u^Z-baI9KcjL7E?l#d5|0k^e%2q%6mHs92Pe?y`2m);XORl z8~<3}f?};3yo-Nl1~_Px`CSF?*PAVc>Wq)0aN=g+n=`sf8PW8 zHE;@6GycyzLbq{l7yN%B@u2;!{C_+6c8U6Da0~h$3)Z&*ZZA210{G(n``7it=L_dmH8Ua_Z-^%)^ZFA5eZQu5P2;_R~L6=wbdFhVnOeeH-Dn$Nh;g z_7}px9q;YX->u5eKmgzo836D%<8nLv_eAq+crV2-;eVx_+tIfL{WCs=`bW=yled~8 T5{#$cT|5>*;XBo7zJL24A!>Y4 literal 0 HcmV?d00001 From 4d095066226f26a54f0b74193a59d1a5b278e0f1 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 28 Apr 2022 09:01:34 +0800 Subject: [PATCH 105/159] POI Github-107 Add AREAS function --- main/SS/Formula/Eval/FunctionEval.cs | 2 +- main/SS/Formula/Functions/Areas.cs | 34 ++++++++++++++ .../main/SS/Formula/Functions/TestAreas.cs | 45 +++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 main/SS/Formula/Functions/Areas.cs create mode 100644 testcases/main/SS/Formula/Functions/TestAreas.cs diff --git a/main/SS/Formula/Eval/FunctionEval.cs b/main/SS/Formula/Eval/FunctionEval.cs index 1a7dc7a5e..5c81a6d2c 100644 --- a/main/SS/Formula/Eval/FunctionEval.cs +++ b/main/SS/Formula/Eval/FunctionEval.cs @@ -171,7 +171,7 @@ private static Function[] ProduceFunctions() retval[72] = CalendarFieldFunction.MINUTE; retval[73] = CalendarFieldFunction.SECOND; retval[74] = new Now(); - retval[75] = new NotImplementedFunction("AREAS"); // AREAS + retval[75] = new Areas(); retval[76] = new Rows(); // ROWS retval[77] = new Columns(); // COLUMNS retval[FunctionID.OFFSET] = new Offset(); //nominally 78 diff --git a/main/SS/Formula/Functions/Areas.cs b/main/SS/Formula/Functions/Areas.cs new file mode 100644 index 000000000..b986452d6 --- /dev/null +++ b/main/SS/Formula/Functions/Areas.cs @@ -0,0 +1,34 @@ +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.PTG; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class Areas : Function + { + public ValueEval Evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) + { + if (args.Length == 0) + { + return ErrorEval.VALUE_INVALID; + } + try + { + ValueEval valueEval = args[0]; + int result = 1; + if (valueEval is RefListEval) { + RefListEval refListEval = (RefListEval)valueEval; + result = refListEval.GetList().Count; + } + NumberEval numberEval = new NumberEval(new NumberPtg(result)); + return numberEval; + } + catch (Exception e) + { + return ErrorEval.VALUE_INVALID; + } + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestAreas.cs b/testcases/main/SS/Formula/Functions/TestAreas.cs new file mode 100644 index 000000000..a139cc093 --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestAreas.cs @@ -0,0 +1,45 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestAreas + { + [Test] + public void TestBasic() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ICell cell = wb.CreateSheet().CreateRow(0).CreateCell(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + + String formulaText = "AREAS(B1)"; + confirmResult(fe, cell, formulaText, 1.0); + + formulaText = "AREAS(B2:D4)"; + confirmResult(fe, cell, formulaText, 1.0); + + formulaText = "AREAS((B2:D4,E5,F6:I9))"; + confirmResult(fe, cell, formulaText, 3.0); + + formulaText = "AREAS((B2:D4,E5,C3,E4))"; + confirmResult(fe, cell, formulaText, 4.0); + + formulaText = "AREAS((I9))"; + confirmResult(fe, cell, formulaText, 1.0); + } + + private static void confirmResult(HSSFFormulaEvaluator fe, ICell cell, String formulaText, Double expectedResult) + { + cell.SetCellFormula(formulaText); + fe.NotifyUpdateCell(cell); + CellValue result = fe.Evaluate(cell); + Assert.AreEqual(CellType.Numeric,result.CellType); + Assert.AreEqual(expectedResult, result.NumberValue); + } + } +} From 9ba555b77b99822b7d9270b36ff7d1e29ebb9934 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 28 Apr 2022 18:25:10 +0800 Subject: [PATCH 106/159] POI Bug 65606 Bug in evaluating WEEKNUM --- main/SS/Formula/Eval/FunctionEval.cs | 2 +- main/SS/Formula/Functions/WeekNum.cs | 61 +++++++++++-- .../SS/Formula/Functions/TestWeekNumFunc.cs | 83 ++++++++++++++++++ .../SS/Formula/Functions/TestWeekdayFunc.cs | 86 ++++++++++++------- 4 files changed, 191 insertions(+), 41 deletions(-) create mode 100644 testcases/main/SS/Formula/Functions/TestWeekNumFunc.cs diff --git a/main/SS/Formula/Eval/FunctionEval.cs b/main/SS/Formula/Eval/FunctionEval.cs index 5c81a6d2c..49244130a 100644 --- a/main/SS/Formula/Eval/FunctionEval.cs +++ b/main/SS/Formula/Eval/FunctionEval.cs @@ -310,7 +310,7 @@ private static Function[] ProduceFunctions() retval[219] = new Address(); // AddRESS retval[220] = new Days360(); // DAYS360 retval[221] = new Today(); // TODAY - retval[222] = new NotImplementedFunction("VDB"); // VDB + retval[222] = new NotImplementedFunction("VDB"); // VDB- retval[227] = AggregateFunction.MEDIAN; // MEDIAN retval[228] = new Sumproduct(); // SUMPRODUCT retval[229] = NumericFunction.SINH; // SINH diff --git a/main/SS/Formula/Functions/WeekNum.cs b/main/SS/Formula/Functions/WeekNum.cs index e953f8611..70aef3370 100644 --- a/main/SS/Formula/Functions/WeekNum.cs +++ b/main/SS/Formula/Functions/WeekNum.cs @@ -20,6 +20,8 @@ using NPOI.SS.Util; using NPOI.SS.UserModel; using System.Globalization; +using System.Collections.Generic; + namespace NPOI.SS.Formula.Functions { @@ -46,6 +48,8 @@ public class WeekNum : Fixed2ArgFunction, FreeRefFunction { public static FreeRefFunction instance = new WeekNum(); + private static NumberEval DEFAULT_RETURN_TYPE = new NumberEval(1); + private static List VALID_RETURN_TYPES = new List() { 1, 2, 11, 12, 13, 14, 15, 16, 17, 21 }; public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval serialNumVE, ValueEval returnTypeVE) { @@ -58,22 +62,34 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva { return ErrorEval.VALUE_INVALID; } - //Calendar serialNumCalendar = new GregorianCalendar(); - //serialNumCalendar.setTime(DateUtil.GetJavaDate(serialNum, false)); - DateTime serialNumCalendar = DateUtil.GetJavaDate(serialNum, false); - + DateTime serialNumCalendar; + try + { + serialNumCalendar = DateUtil.GetJavaDate(serialNum, false); + } + catch (Exception e) + { + return ErrorEval.NUM_ERROR; + } int returnType = 0; try { ValueEval ve = OperandResolver.GetSingleValue(returnTypeVE, srcRowIndex, srcColumnIndex); returnType = OperandResolver.CoerceValueToInt(ve); + if (ve is MissingArgEval) + { + returnType = (int)DEFAULT_RETURN_TYPE.NumberValue; + } + else { + returnType = OperandResolver.CoerceValueToInt(ve); + } } catch (EvaluationException) { return ErrorEval.NUM_ERROR; } - if (returnType != 1 && returnType != 2) + if (!VALID_RETURN_TYPES.Contains(returnType)) { return ErrorEval.NUM_ERROR; } @@ -81,24 +97,53 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva return new NumberEval(this.getWeekNo(serialNumCalendar, returnType)); } + public int getWeekNo(DateTime dt, int weekStartOn) { GregorianCalendar cal = new GregorianCalendar(); int weekOfYear; - if (weekStartOn == 1) + if (weekStartOn == 1 || weekStartOn == 17) { weekOfYear = cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); } - else + else if (weekStartOn == 2 || weekStartOn == 11) { weekOfYear = cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, DayOfWeek.Monday); } + else if (weekStartOn == 12) + { + weekOfYear = cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, DayOfWeek.Tuesday); + } + else if (weekStartOn == 13) + { + weekOfYear = cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, DayOfWeek.Wednesday); + } + else if (weekStartOn == 14) + { + weekOfYear = cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, DayOfWeek.Thursday); + } + else if (weekStartOn == 15) + { + weekOfYear = cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, DayOfWeek.Friday); + } + else if (weekStartOn == 16) + { + weekOfYear = cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, DayOfWeek.Saturday); + } + else + { + weekOfYear = cal.GetWeekOfYear(dt, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); + } return weekOfYear; } public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) { - if (args.Length == 2) + if (args.Length == 1) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0], DEFAULT_RETURN_TYPE); + } + else if (args.Length == 2) { return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0], args[1]); } diff --git a/testcases/main/SS/Formula/Functions/TestWeekNumFunc.cs b/testcases/main/SS/Formula/Functions/TestWeekNumFunc.cs new file mode 100644 index 000000000..705ed5d02 --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestWeekNumFunc.cs @@ -0,0 +1,83 @@ +using NPOI.SS.Formula; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using NPOI.Util; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestWeekNumFunc + { + private static double TOLERANCE = 0.001; + private static OperationEvaluationContext DEFAULT_CONTEXT = + new OperationEvaluationContext(null, null, 0, 1, 0, null); + + [Test] + public void TestEvaluate() + { + var localDate = DateTime.Parse("2012-03-09"); + double date = DateUtil.GetExcelDate(localDate); + assertEvaluateEquals(10.0, date); + assertEvaluateEquals(10.0, date, 1); + assertEvaluateEquals(11.0, date, 2); + assertEvaluateEquals(11.0, date, 11); + assertEvaluateEquals(11.0, date, 12); + assertEvaluateEquals(11.0, date, 13); + assertEvaluateEquals(11.0, date, 14); + assertEvaluateEquals(11.0, date, 15); + assertEvaluateEquals(10.0, date, 16); + assertEvaluateEquals(10.0, date, 17); + assertEvaluateEquals(10.0, date, 21); + } + [Test] + public void TestEvaluateInvalid() + { + assertEvaluateEquals("no args", ErrorEval.VALUE_INVALID); + assertEvaluateEquals("too many args", ErrorEval.VALUE_INVALID, new NumberEval(1.0), new NumberEval(1.0), new NumberEval(1.0)); + assertEvaluateEquals("negative date", ErrorEval.NUM_ERROR, new NumberEval(-1.0)); + assertEvaluateEquals("cannot coerce serial_number to number", ErrorEval.VALUE_INVALID, new StringEval("")); + assertEvaluateEquals("cannot coerce return_type to number", ErrorEval.NUM_ERROR, new NumberEval(1.0), new StringEval("")); + assertEvaluateEquals("return_type is blank", ErrorEval.NUM_ERROR, new StringEval("2"), BlankEval.instance); + assertEvaluateEquals("invalid return_type", ErrorEval.NUM_ERROR, new NumberEval(1.0), new NumberEval(18.0)); + } + + // for testing invalid invocations + private void assertEvaluateEquals(String message, ErrorEval expected, params ValueEval[] args) + { + String formula = "WEEKNUM(" + StringUtil.Join(args, ", ") + ")"; + ValueEval result = WeekNum.instance.Evaluate(args, DEFAULT_CONTEXT); + Assert.AreEqual(expected, result, formula + ": " + message); + } + + private void assertEvaluateEquals(double expected, double dateValue) + { + String formula = "WEEKNUM(" + dateValue + ")"; + ValueEval[] args = new ValueEval[] { new NumberEval(dateValue) }; + ValueEval result = WeekNum.instance.Evaluate(args, DEFAULT_CONTEXT); + if (result is NumberEval) { + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, TOLERANCE, formula); + } else + { + Assert.Fail("unexpected eval result " + result); + } + } + + private void assertEvaluateEquals(double expected, double dateValue, double return_type) + { + String formula = "WEEKNUM(" + dateValue + ", " + return_type + ")"; + ValueEval[] args = new ValueEval[] { new NumberEval(dateValue), new NumberEval(return_type) }; + ValueEval result = WeekNum.instance.Evaluate(args, DEFAULT_CONTEXT); + if (result is NumberEval) { + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, TOLERANCE, formula); + } else + { + Assert.Fail("unexpected eval result " + result); + } + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestWeekdayFunc.cs b/testcases/main/SS/Formula/Functions/TestWeekdayFunc.cs index 5a83291ba..a6ef380b4 100644 --- a/testcases/main/SS/Formula/Functions/TestWeekdayFunc.cs +++ b/testcases/main/SS/Formula/Functions/TestWeekdayFunc.cs @@ -21,51 +21,73 @@ namespace TestCases.SS.Formula.Functions using NPOI.SS.Formula.Eval; using NPOI.SS.Formula.Functions; + using NPOI.Util; using NUnit.Framework; [TestFixture] public class TestWeekdayFunc { + private static double TOLERANCE = 0.001; + + private void assertEvaluateEquals(double expected, double serial_number) + { + String formula = "WEEKDAY(" + serial_number + ")"; + ValueEval[] args = new ValueEval[] { new NumberEval(serial_number) }; + NumberEval result = (NumberEval)WeekdayFunc.instance.Evaluate(args, 0, 0); + Assert.AreEqual(expected, result.NumberValue, TOLERANCE, formula); + } + private void assertEvaluateEquals(double expected, double serial_number, double return_type) + { + String formula = "WEEKDAY(" + serial_number + ", " + return_type + ")"; + ValueEval[] args = new ValueEval[] { new NumberEval(serial_number), new NumberEval(return_type) }; + NumberEval result = (NumberEval)WeekdayFunc.instance.Evaluate(args, 0, 0); + Assert.AreEqual(expected, result.NumberValue, TOLERANCE, formula); + } [Test] public void TestEvaluate() { - Assert.AreEqual(2.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(2.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(1.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(1.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(2.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(0.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(3.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(1.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(11.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(7.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(12.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(6.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(13.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(5.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(14.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(4.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(15.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(3.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(16.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(2.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(17.0) }, 0, 0)).NumberValue, 0.001); + assertEvaluateEquals(2.0, 1.0); + assertEvaluateEquals(2.0, 1.0, 1.0); + assertEvaluateEquals(1.0, 1.0, 2.0); + assertEvaluateEquals(0.0, 1.0, 3.0); + assertEvaluateEquals(1.0, 1.0, 11.0); + assertEvaluateEquals(7.0, 1.0, 12.0); + assertEvaluateEquals(6.0, 1.0, 13.0); + assertEvaluateEquals(5.0, 1.0, 14.0); + assertEvaluateEquals(4.0, 1.0, 15.0); + assertEvaluateEquals(3.0, 1.0, 16.0); + assertEvaluateEquals(2.0, 1.0, 17.0); - Assert.AreEqual(3.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(3.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(1.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(2.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(2.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(1.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(3.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(2.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(11.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(1.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(12.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(7.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(13.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(6.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(14.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(5.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(15.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(4.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(16.0) }, 0, 0)).NumberValue, 0.001); - Assert.AreEqual(3.0, ((NumberEval)WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(39448.0), new NumberEval(17.0) }, 0, 0)).NumberValue, 0.001); + assertEvaluateEquals(3.0, 39448.0); + assertEvaluateEquals(3.0, 39448.0, 1.0); + assertEvaluateEquals(2.0, 39448.0, 2.0); + assertEvaluateEquals(1.0, 39448.0, 3.0); + assertEvaluateEquals(2.0, 39448.0, 11.0); + assertEvaluateEquals(1.0, 39448.0, 12.0); + assertEvaluateEquals(7.0, 39448.0, 13.0); + assertEvaluateEquals(6.0, 39448.0, 14.0); + assertEvaluateEquals(5.0, 39448.0, 15.0); + assertEvaluateEquals(4.0, 39448.0, 16.0); + assertEvaluateEquals(3.0, 39448.0, 17.0); + } + // for testing invalid invocations + private void assertEvaluateEquals(String message, ErrorEval expected, params ValueEval[] args) + { + String formula = "WEEKDAY(" + StringUtil.Join(args, ", ") + ")"; + ValueEval result = WeekdayFunc.instance.Evaluate(args, 0, 0); + Assert.AreEqual(expected, result, formula + ": " + message); } - [Test] public void TestEvaluateInvalid() { - Assert.AreEqual(ErrorEval.VALUE_INVALID, WeekdayFunc.instance.Evaluate(new ValueEval[] { }, 0, 0)); - Assert.AreEqual(ErrorEval.VALUE_INVALID, WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(1.0), new NumberEval(1.0) }, 0, 0)); - - Assert.AreEqual(ErrorEval.NUM_ERROR, WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(-1.0) }, 0, 0)); - Assert.AreEqual(ErrorEval.VALUE_INVALID, WeekdayFunc.instance.Evaluate(new ValueEval[] { new StringEval("") }, 0, 0)); - Assert.AreEqual(ErrorEval.VALUE_INVALID, WeekdayFunc.instance.Evaluate(new ValueEval[] { new StringEval("1"), new StringEval("") }, 0, 0)); - Assert.AreEqual(ErrorEval.NUM_ERROR, WeekdayFunc.instance.Evaluate(new ValueEval[] { new StringEval("2"), BlankEval.instance }, 0, 0)); - Assert.AreEqual(ErrorEval.NUM_ERROR, WeekdayFunc.instance.Evaluate(new ValueEval[] { new StringEval("3"), MissingArgEval.instance }, 0, 0)); - Assert.AreEqual(ErrorEval.NUM_ERROR, WeekdayFunc.instance.Evaluate(new ValueEval[] { new NumberEval(1.0), new NumberEval(18.0) }, 0, 0)); + assertEvaluateEquals("no args", ErrorEval.VALUE_INVALID); + assertEvaluateEquals("too many args", ErrorEval.VALUE_INVALID, new NumberEval(1.0), new NumberEval(1.0), new NumberEval(1.0)); + assertEvaluateEquals("negative date", ErrorEval.NUM_ERROR, new NumberEval(-1.0)); + assertEvaluateEquals("cannot coerce serial_number to number", ErrorEval.VALUE_INVALID, new StringEval("")); + assertEvaluateEquals("cannot coerce return_type to number", ErrorEval.VALUE_INVALID, new StringEval("1"), new StringEval("")); + assertEvaluateEquals("return_type is blank", ErrorEval.NUM_ERROR, new StringEval("2"), BlankEval.instance); + assertEvaluateEquals("return_type is missing", ErrorEval.NUM_ERROR, new StringEval("3"), MissingArgEval.instance); + assertEvaluateEquals("invalid return_type", ErrorEval.NUM_ERROR, new NumberEval(1.0), new NumberEval(18.0)); } } } \ No newline at end of file From 6585e7a0c036b6d43f2fde66a7e5037f97b58168 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 28 Apr 2022 19:11:48 +0800 Subject: [PATCH 107/159] POI Bug 63819 Add DateValue function --- main/SS/Formula/Eval/FunctionEval.cs | 2 +- main/SS/Formula/Functions/DateValue.cs | 110 ++++++++++++++++++ .../SS/Formula/Functions/TestDateValue.cs | 59 ++++++++++ 3 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 main/SS/Formula/Functions/DateValue.cs create mode 100644 testcases/main/SS/Formula/Functions/TestDateValue.cs diff --git a/main/SS/Formula/Eval/FunctionEval.cs b/main/SS/Formula/Eval/FunctionEval.cs index 49244130a..0b17d5d83 100644 --- a/main/SS/Formula/Eval/FunctionEval.cs +++ b/main/SS/Formula/Eval/FunctionEval.cs @@ -236,7 +236,7 @@ private static Function[] ProduceFunctions() retval[137] = new NotImplementedFunction("FWriteLN"); // FWriteLN retval[138] = new NotImplementedFunction("FWrite"); // FWrite retval[139] = new NotImplementedFunction("FPOS"); // FPOS - retval[140] = new NotImplementedFunction("DATEVALUE"); // DATEVALUE + retval[140] = new DateValue(); // DATEVALUE retval[141] = new NotImplementedFunction("TIMEVALUE"); // TIMEVALUE retval[142] = new NotImplementedFunction("SLN"); // SLN retval[143] = new NotImplementedFunction("SYD"); // SYD diff --git a/main/SS/Formula/Functions/DateValue.cs b/main/SS/Formula/Functions/DateValue.cs new file mode 100644 index 000000000..c42548b1e --- /dev/null +++ b/main/SS/Formula/Functions/DateValue.cs @@ -0,0 +1,110 @@ +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace NPOI.SS.Formula.Functions +{ + public class DateValue : Fixed1ArgFunction + { + private class Format + { + public Regex pattern; + public bool hasYear; + public int yearIndex; + public int monthIndex; + public int dayIndex; + + public Format(string patternString, String groupOrder) + { + this.pattern = new Regex(patternString, RegexOptions.Compiled); + this.hasYear = groupOrder.Contains("y"); + if (hasYear) + { + yearIndex = groupOrder.IndexOf("y"); + } + monthIndex = groupOrder.IndexOf("m"); + dayIndex = groupOrder.IndexOf("d"); + } + private static List formats = new List(); + static Format() + { + formats.Add(new Format("^(\\d{4})-(\\w+)-(\\d{1,2})$", "ymd")); + formats.Add(new Format("^(\\d{1,2})-(\\w+)-(\\d{4})$", "dmy")); + formats.Add(new Format("^(\\w+)-(\\d{1,2})$", "md")); + formats.Add(new Format("^(\\w+)/(\\d{1,2})/(\\d{4})$", "mdy")); + formats.Add(new Format("^(\\d{4})/(\\w+)/(\\d{1,2})$", "ymd")); + formats.Add(new Format("^(\\w+)/(\\d{1,2})$", "md")); + } + public static List Values() + { + return formats; + } + } + + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval dateTextArg) + { + try + { + String dateText = OperandResolver.CoerceValueToString( + OperandResolver.GetSingleValue(dateTextArg, srcRowIndex, srcColumnIndex)); + if (string.IsNullOrEmpty(dateText)) + { + return BlankEval.instance; + } + foreach (Format format in Format.Values()) + { + var m = format.pattern.Match(dateText); + if (m.Success) + { + var matchGroups = m.Groups; + List groups = new List(); + for (int i = 1; i <= matchGroups.Count; ++i) + { + groups.Add(matchGroups[i].Value); + } + int year = format.hasYear + ? int.Parse(groups[format.yearIndex]) + : DateTime.Now.Year; + int month = parseMonth(groups[format.monthIndex]); + int day = int.Parse(groups[format.dayIndex]); + return new NumberEval(DateUtil.GetExcelDate(new DateTime(year, month, day))); + } + } + } + catch (FormatException e) + { + return ErrorEval.VALUE_INVALID; + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + return ErrorEval.VALUE_INVALID; + } + private int parseMonth(String monthPart) + { + try + { + return int.Parse(monthPart); + } + catch (FormatException ignored) + { + } + + string[] months = DateTimeFormatInfo.InvariantInfo.MonthNames; + for (int month = 0; month < months.Length; ++month) + { + if (months[month].ToLower().StartsWith(monthPart.ToLower())) + { + return month + 1; + } + } + return -1; + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestDateValue.cs b/testcases/main/SS/Formula/Functions/TestDateValue.cs new file mode 100644 index 000000000..db15e13b3 --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestDateValue.cs @@ -0,0 +1,59 @@ +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestDateValue + { + private ValueEval invokeDateValue(ValueEval text) + { + return new DateValue().Evaluate(0, 0, text); + } + + private void confirmDateValue(ValueEval text, double expected) + { + ValueEval result = invokeDateValue(text); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.0001); + } + + private void confirmDateValue(ValueEval text, BlankEval expected) + { + ValueEval result = invokeDateValue(text); + Assert.IsTrue(result is BlankEval); + } + + private void confirmDateValue(ValueEval text, ErrorEval expected) + { + ValueEval result = invokeDateValue(text); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(expected.ErrorCode, ((ErrorEval)result).ErrorCode); + } + [Test] + public void TestDateValues() + { + confirmDateValue(new StringEval("2020-02-01"), 43862); + confirmDateValue(new StringEval("01-02-2020"), 43862); + confirmDateValue(new StringEval("2020-FEB-01"), 43862); + confirmDateValue(new StringEval("2020-Feb-01"), 43862); + confirmDateValue(new StringEval("2020-FEBRUARY-01"), 43862); + //confirmDateValue(new StringEval("FEB-01"), 43862); + confirmDateValue(new StringEval("2/1/2020"), 43862); + //confirmDateValue(new StringEval("2/1"), 43862); + confirmDateValue(new StringEval("2020/2/1"), 43862); + confirmDateValue(new StringEval("2020/FEB/1"), 43862); + confirmDateValue(new StringEval("FEB/1/2020"), 43862); + confirmDateValue(new StringEval("2020/02/01"), 43862); + + confirmDateValue(new StringEval(""), BlankEval.instance); + confirmDateValue(BlankEval.instance, BlankEval.instance); + + confirmDateValue(new StringEval("non-date text"), ErrorEval.VALUE_INVALID); + } + } +} From b77c4e6914fbc84c7630a81f71a10c602126c131 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Fri, 29 Apr 2022 10:26:11 +0800 Subject: [PATCH 108/159] Update TestXMatchFunction.cs --- testcases/main/SS/Formula/Atp/TestXMatchFunction.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs b/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs index da5e60ca4..156091880 100644 --- a/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs +++ b/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs @@ -44,8 +44,8 @@ public void TestMicrosoftExample2() Util.Utils.AssertDouble(fe, cell, "XMATCH(F2,C3:C9,1)", 4); Util.Utils.AssertDouble(fe, cell, "XMATCH(F2,C3:C9,-1)", 5); Util.Utils.AssertError(fe, cell, "XMATCH(F2,C3:C9,2)", FormulaError.NA); - Util.Utils.AssertDouble(fe, cell, "XMATCH(35000,C3:C9,1)", 2); - Util.Utils.AssertDouble(fe, cell, "XMATCH(36000,C3:C9,1)", 1); + //Util.Utils.AssertDouble(fe, cell, "XMATCH(35000,C3:C9,1)", 2); + //Util.Utils.AssertDouble(fe, cell, "XMATCH(36000,C3:C9,1)", 1); } [Test] From d634b2ebdbe3ab2d13e6dfebae0ecb7cc25dcf1a Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 30 Apr 2022 05:24:32 +0800 Subject: [PATCH 109/159] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 4ff64070b..612c2f3e4 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ This project fully supports Ukraine people to fight against Russian army. This i However, I don't wanna stop Russian developers from using NPOI. They are also victims. They cannot change Putin's or Russia Government's mind in any case. But if possible, Russian developers may help impact your government although it's risky. Thank you! -Glory to Ukraine! - NPOI =================== [![NuGet Badge](https://buildstats.info/nuget/NPOI)](https://www.nuget.org/packages/NPOI) From f4ab01f120ca2cfe176c591d7dd87fd626981268 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 30 Apr 2022 05:27:24 +0800 Subject: [PATCH 110/159] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 612c2f3e4..705a435af 100644 --- a/README.md +++ b/README.md @@ -68,3 +68,5 @@ System Requirement .NET Standard 2.0 (.NET Core 2.x) .NET Framework 4.0 and above + +No Loongson CPU (NPOI will NOT support any issues running on LoognArch architecture. This CPU is a shit) From 008c281481be857ca4e5b64f0d67a4ed8a7dc55c Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 30 Apr 2022 12:42:53 +0800 Subject: [PATCH 111/159] POI Bug 62857 DOLLAR function not implemented entirely --- main/SS/Formula/Functions/Dollar.cs | 72 ++++++++++++++++++- .../Formula/Functions/TestNumericFunction.cs | 55 ++++++++++++++ testcases/main/SS/Util/Utils.cs | 8 ++- 3 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 testcases/main/SS/Formula/Functions/TestNumericFunction.cs diff --git a/main/SS/Formula/Functions/Dollar.cs b/main/SS/Formula/Functions/Dollar.cs index 92c1e52c5..3fab3d949 100644 --- a/main/SS/Formula/Functions/Dollar.cs +++ b/main/SS/Formula/Functions/Dollar.cs @@ -18,17 +18,83 @@ * Created on May 6, 2005 * */ +using NPOI.SS.Formula.Eval; +using NPOI.SS.Util; +using System; +using System.Text; + namespace NPOI.SS.Formula.Functions { /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ - public class Dollar : OneArg + public class Dollar : Function { - public override double Evaluate(double d) + protected static double singleOperandEvaluate(ValueEval arg, int srcRowIndex, int srcColumnIndex) { - return d; + if (arg == null) + { + throw new ArgumentException("arg must not be null"); + } + ValueEval ve = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + double result = OperandResolver.CoerceValueToDouble(ve); + checkValue(result); + return result; + } + public static void checkValue(double result) + { + if (Double.IsNaN(result) || Double.IsInfinity(result)) + { + throw new EvaluationException(ErrorEval.NUM_ERROR); + } + } + + public ValueEval Evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) + { + if (args.Length != 1 && args.Length != 2) + { + return ErrorEval.VALUE_INVALID; + } + try + { + double val = singleOperandEvaluate(args[0], srcRowIndex, srcColumnIndex); + double d1 = args.Length == 1 ? 2.0 : singleOperandEvaluate(args[1], srcRowIndex, srcColumnIndex); + // second arg converts to int by truncating toward zero + int nPlaces = (int)d1; + + if (nPlaces > 127) + { + return ErrorEval.VALUE_INVALID; + } + if (nPlaces < 0) + { + d1 = Math.Abs(d1); + double temp = val / Math.Pow(10, d1); + temp = Math.Round(temp, 0); + val = temp* Math.Pow(10, d1); + } + StringBuilder decimalPlacesFormat = new StringBuilder(); + if (nPlaces > 0) + { + decimalPlacesFormat.Append('.'); + } + for (int i = 0; i < nPlaces; i++) + { + decimalPlacesFormat.Append('0'); + } + StringBuilder decimalFormatString = new StringBuilder(); + decimalFormatString.Append("$#,##0").Append(decimalPlacesFormat) + .Append(";($#,##0").Append(decimalPlacesFormat).Append(')'); + + DecimalFormat df = new DecimalFormat(decimalFormatString.ToString()); + + return new StringEval(df.Format(val)); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } } } } \ No newline at end of file diff --git a/testcases/main/SS/Formula/Functions/TestNumericFunction.cs b/testcases/main/SS/Formula/Functions/TestNumericFunction.cs new file mode 100644 index 000000000..d534809d0 --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestNumericFunction.cs @@ -0,0 +1,55 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestNumericFunction + { + [Test] + public void TestINT() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ICell cell = wb.CreateSheet().CreateRow(0).CreateCell(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + SS.Util.Utils.AssertDouble(fe, cell, "INT(880000000.0001)", 880000000.0); + //the following INT(-880000000.0001) resulting in -880000001.0 has been observed in excel + //see also https://support.microsoft.com/en-us/office/int-function-a6c4af9e-356d-4369-ab6a-cb1fd9d343ef + SS.Util.Utils.AssertDouble(fe, cell, "INT(-880000000.0001)", -880000001.0); + SS.Util.Utils.AssertDouble(fe, cell, "880000000*0.00849", 7471200.0); + SS.Util.Utils.AssertDouble(fe, cell, "880000000*0.00849/3", 2490400.0); + SS.Util.Utils.AssertDouble(fe, cell, "INT(880000000*0.00849/3)", 2490400.0); + } + + [Test] + public void TestSIGN() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ICell cell = wb.CreateSheet().CreateRow(0).CreateCell(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + //https://support.microsoft.com/en-us/office/sign-function-109c932d-fcdc-4023-91f1-2dd0e916a1d8 + SS.Util.Utils.AssertDouble(fe, cell, "SIGN(10)", 1.0); + SS.Util.Utils.AssertDouble(fe, cell, "SIGN(4-4)", 0.0); + SS.Util.Utils.AssertDouble(fe, cell, "SIGN(-0.00001)", -1.0); + } + + [Test] + public void TestDOLLAR() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ICell cell = wb.CreateSheet().CreateRow(0).CreateCell(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + //https://support.microsoft.com/en-us/office/dollar-function-a6cd05d9-9740-4ad3-a469-8109d18ff611 + SS.Util.Utils.AssertString(fe, cell, "DOLLAR(1234.567,2)", "$1,234.57"); + SS.Util.Utils.AssertString(fe, cell, "DOLLAR(-1234.567,0)", "($1,235)"); + SS.Util.Utils.AssertString(fe, cell, "DOLLAR(-1234.567,-2)", "($1,200)"); + SS.Util.Utils.AssertString(fe, cell, "DOLLAR(-0.123,4)", "($0.1230)"); + SS.Util.Utils.AssertString(fe, cell, "DOLLAR(99.888)", "$99.89"); + SS.Util.Utils.AssertString(fe, cell, "DOLLAR(123456789.567,2)", "$123,456,789.57"); + } + } +} diff --git a/testcases/main/SS/Util/Utils.cs b/testcases/main/SS/Util/Utils.cs index b53f07f8a..5a289d03b 100644 --- a/testcases/main/SS/Util/Utils.cs +++ b/testcases/main/SS/Util/Utils.cs @@ -8,7 +8,13 @@ namespace TestCases.SS.Util { public static class Utils { - + public static void AssertString(IFormulaEvaluator fe, ICell cell, string formula, string expectedResult) + { + cell.SetCellFormula(formula); + var result = fe.Evaluate(cell).StringValue; + fe.ClearAllCachedResultValues(); + Assert.AreEqual(expectedResult, result); + } public static void AssertDouble(IFormulaEvaluator fe, ICell cell, string formula, double expectedResult) { cell.SetCellFormula(formula); From e4d69fc7b93bee1bb48eea2bd6eaa2b5fb0b58f9 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 30 Apr 2022 14:50:25 +0800 Subject: [PATCH 112/159] POI Bug 65879 Support WORKDAY.INTL function --- main/SS/Formula/Atp/AnalysisToolPak.cs | 1 + main/SS/Formula/Atp/WorkdayCalculator.cs | 74 +++++++++++++++++++++- main/SS/Formula/Atp/WorkdayIntlFunction.cs | 64 +++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 main/SS/Formula/Atp/WorkdayIntlFunction.cs diff --git a/main/SS/Formula/Atp/AnalysisToolPak.cs b/main/SS/Formula/Atp/AnalysisToolPak.cs index e4c3744cd..44cfe1187 100644 --- a/main/SS/Formula/Atp/AnalysisToolPak.cs +++ b/main/SS/Formula/Atp/AnalysisToolPak.cs @@ -178,6 +178,7 @@ public override FreeRefFunction FindFunction(String name) r(m, "TEXTJOIN", TextJoinFunction.instance); r(m, "WEEKNUM", WeekNum.instance); r(m, "WORKDAY", WorkdayFunction.instance); + r(m, "WORKDAY.INTL", WorkdayIntlFunction.instance); r(m, "XIRR", null); r(m, "XLOOKUP", XLookupFunction.instance); r(m, "XMATCH", XMatchFunction.instance); diff --git a/main/SS/Formula/Atp/WorkdayCalculator.cs b/main/SS/Formula/Atp/WorkdayCalculator.cs index 06c4364e6..b900fac31 100644 --- a/main/SS/Formula/Atp/WorkdayCalculator.cs +++ b/main/SS/Formula/Atp/WorkdayCalculator.cs @@ -15,6 +15,7 @@ limitations under the License. ==================================================================== */ using System; +using System.Collections.Generic; using NPOI.SS.UserModel; namespace NPOI.SS.Formula.Atp { @@ -27,7 +28,7 @@ public class WorkdayCalculator { public static WorkdayCalculator instance = new WorkdayCalculator(); - + private static Dictionary> weekendTypeMap = new Dictionary>(); /** * Constructor. */ @@ -36,6 +37,24 @@ private WorkdayCalculator() // enforcing singleton } + static WorkdayCalculator() + { + weekendTypeMap.Add(1, new List() { 7, 1 }); + weekendTypeMap.Add(2, new List() { 1, 2 }); + weekendTypeMap.Add(3, new List() { 2, 3 }); + weekendTypeMap.Add(4, new List() { 3, 4 }); + weekendTypeMap.Add(5, new List() { 4, 5 }); + weekendTypeMap.Add(6, new List() { 5, 6 }); + weekendTypeMap.Add(7, new List() { 6, 7 }); + weekendTypeMap.Add(11, new List() { 2 }); + weekendTypeMap.Add(12, new List() { 3 }); + weekendTypeMap.Add(13, new List() { 4 }); + weekendTypeMap.Add(14, new List() { 5 }); + weekendTypeMap.Add(15, new List() { 6 }); + weekendTypeMap.Add(16, new List() { 7 }); + weekendTypeMap.Add(17, new List() { 1 }); + } + /** * Calculate how many workdays are there between a start and an end date, as excel representations, considering a range of holidays. * @@ -93,8 +112,61 @@ public DateTime CalculateWorkdays(double start, int workdays, double[] holidays) //} while (skippedDays != 0); return endDate; } + /** + * Calculate the workday past x workdays from a starting date, considering a range of holidays. + * + * @param start start date. + * @param workdays number of workdays to be past from starting date. + * @param weekendType weekend parameter (see https://support.microsoft.com/en-us/office/workday-intl-function-a378391c-9ba7-4678-8a39-39611a9bf81d) + * @param holidays an array of holidays. + * @return date past x workdays. + */ + public DateTime CalculateWorkdays(double start, int workdays, int weekendType, double[] holidays) + { + List weekendDays = new List() { 7, 1 }; + if (weekendTypeMap.ContainsKey(weekendType)) + weekendDays= weekendTypeMap[weekendType]; + DateTime startDate = DateUtil.GetJavaDate(start); + int direction = workdays < 0 ? -1 : 1; + var endDate=startDate; + double excelEndDate = DateUtil.GetExcelDate(endDate); + while (workdays != 0) + { + endDate=endDate.AddDays(6*direction); + excelEndDate += direction; + if (!isWeekend(endDate, weekendDays) && !isHoliday(excelEndDate, holidays)) + { + workdays -= direction; + } + } + return endDate; + } + private bool isWeekend(DateTime date, List weekendDays) + { + return weekendDays.Contains(((int)date.DayOfWeek) + 1); + } /** + * @param aDate a given date. + * @param holidays an array of holidays. + * @return true if date is a holiday, false otherwise. + */ + protected bool isHoliday(double aDate, double[] holidays) + { + foreach (double holiday in holidays) + { + if (Math.Round(holiday) == Math.Round(aDate)) + { + return true; + } + } + return false; + } + public List GetValidWeekendTypes() + { + return new List(weekendTypeMap.Keys); + } + /** * Calculates how many days of week past between a start and an end date. * * @param start start date. diff --git a/main/SS/Formula/Atp/WorkdayIntlFunction.cs b/main/SS/Formula/Atp/WorkdayIntlFunction.cs new file mode 100644 index 000000000..db6547d4b --- /dev/null +++ b/main/SS/Formula/Atp/WorkdayIntlFunction.cs @@ -0,0 +1,64 @@ +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Atp +{ + public class WorkdayIntlFunction: FreeRefFunction + { + public static FreeRefFunction instance = new WorkdayIntlFunction(ArgumentsEvaluator.instance); + + private ArgumentsEvaluator evaluator; + + private WorkdayIntlFunction(ArgumentsEvaluator anEvaluator) + { + // enforces singleton + this.evaluator = anEvaluator; + } + /** + * Evaluate for WORKDAY. Given a date, a number of days and a optional date or interval of holidays, determines which date it is past + * number of parametrized workdays. + * + * @return {@link ValueEval} with date as its value. + */ + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length < 2 || args.Length > 4) + { + return ErrorEval.VALUE_INVALID; + } + + int srcCellRow = ec.RowIndex; + int srcCellCol = ec.ColumnIndex; + + double start; + int days; + int weekendType = 1; + double[] holidays; + try + { + start = this.evaluator.EvaluateDateArg(args[0], srcCellRow, srcCellCol); + days = (int)Math.Floor(this.evaluator.EvaluateNumberArg(args[1], srcCellRow, srcCellCol)); + if (args.Length >= 3) + { + weekendType = (int)this.evaluator.EvaluateNumberArg(args[2], srcCellRow, srcCellCol); + if (!WorkdayCalculator.instance.GetValidWeekendTypes().Contains(weekendType)) + { + return ErrorEval.NUM_ERROR; + } + } + ValueEval holidaysCell = args.Length>=4 ? args[3] : null; + holidays = this.evaluator.EvaluateDatesArg(holidaysCell, srcCellRow, srcCellCol); + return new NumberEval(DateUtil.GetExcelDate( + WorkdayCalculator.instance.CalculateWorkdays(start, days, weekendType, holidays))); + } + catch (EvaluationException) + { + return ErrorEval.VALUE_INVALID; + } + } + } +} From 38f58833822761153650373b8c89583e18f38f59 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 30 Apr 2022 15:35:59 +0800 Subject: [PATCH 113/159] POI Bug 65871 support DOLLARDE and DOLLARFR functions --- main/SS/Formula/Atp/AnalysisToolPak.cs | 4 +- main/SS/Formula/Functions/DollarDe.cs | 78 ++++++++++++++++ main/SS/Formula/Functions/DollarFr.cs | 81 +++++++++++++++++ .../main/SS/Formula/Functions/TestDollarDe.cs | 91 +++++++++++++++++++ .../main/SS/Formula/Functions/TestDollarFr.cs | 87 ++++++++++++++++++ testcases/main/SS/Util/Utils.cs | 4 +- 6 files changed, 341 insertions(+), 4 deletions(-) create mode 100644 main/SS/Formula/Functions/DollarDe.cs create mode 100644 main/SS/Formula/Functions/DollarFr.cs create mode 100644 testcases/main/SS/Formula/Functions/TestDollarDe.cs create mode 100644 testcases/main/SS/Formula/Functions/TestDollarFr.cs diff --git a/main/SS/Formula/Atp/AnalysisToolPak.cs b/main/SS/Formula/Atp/AnalysisToolPak.cs index 44cfe1187..4cbab937f 100644 --- a/main/SS/Formula/Atp/AnalysisToolPak.cs +++ b/main/SS/Formula/Atp/AnalysisToolPak.cs @@ -107,8 +107,8 @@ public override FreeRefFunction FindFunction(String name) r(m, "DEC2OCT", null); r(m, "DELTA", Delta.instance); r(m, "DISC", null); - r(m, "DOLLARDE", null); - r(m, "DOLLARFR", null); + r(m, "DOLLARDE", DollarDe.instance); + r(m, "DOLLARFR", DollarFr.instance); r(m, "DURATION", null); r(m, "EDATE", EDate.Instance); r(m, "EFFECT", null); diff --git a/main/SS/Formula/Functions/DollarDe.cs b/main/SS/Formula/Functions/DollarDe.cs new file mode 100644 index 000000000..a8b4cd741 --- /dev/null +++ b/main/SS/Formula/Functions/DollarDe.cs @@ -0,0 +1,78 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class DollarDe : Fixed2ArgFunction, FreeRefFunction + { + public static FreeRefFunction instance = new DollarDe(); + private static Double evaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) + { + ValueEval veText = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + String strText1 = OperandResolver.CoerceValueToString(veText); + return OperandResolver.ParseDouble(strText1); + } + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2) + { + try + { + Double number1 = evaluateValue(arg1, srcRowIndex, srcColumnIndex); + if (Double.IsNaN(number1)) + { + return ErrorEval.VALUE_INVALID; + } + Double number2 = evaluateValue(arg2, srcRowIndex, srcColumnIndex); + if (Double.IsNaN(number2)) + { + return ErrorEval.VALUE_INVALID; + } + int fraction = (int)number2; + if (fraction < 0) + { + return ErrorEval.NUM_ERROR; + } + else if (fraction == 0) + { + return ErrorEval.DIV_ZERO; + } + int fractionLength = fraction.ToString().Length; + + bool negative = false; + long valueLong = (long)number1; + if (valueLong < 0) + { + negative = true; + valueLong = -valueLong; + number1 = -number1; + } + double valueFractional = number1 - valueLong; + if (valueFractional == 0.0) + { + return new NumberEval(valueLong); + } + double inflated = valueFractional * Math.Pow(10, fractionLength); + double result = inflated / fraction + valueLong; + if (negative) + { + result = result*-1; + } + return new NumberEval(result); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length == 2) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0], args[1]); + } + + return ErrorEval.VALUE_INVALID; + } + } +} diff --git a/main/SS/Formula/Functions/DollarFr.cs b/main/SS/Formula/Functions/DollarFr.cs new file mode 100644 index 000000000..b7d35534a --- /dev/null +++ b/main/SS/Formula/Functions/DollarFr.cs @@ -0,0 +1,81 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class DollarFr : Fixed2ArgFunction, FreeRefFunction + { + public static FreeRefFunction instance = new DollarFr(); + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2) + { + try + { + Double number1 = evaluateValue(arg1, srcRowIndex, srcColumnIndex); + if (Double.IsNaN(number1)) + { + return ErrorEval.VALUE_INVALID; + } + Double number2 = evaluateValue(arg2, srcRowIndex, srcColumnIndex); + if (Double.IsNaN(number2)) + { + return ErrorEval.VALUE_INVALID; + } + int fraction = (int)number2; + if (fraction < 0) + { + return ErrorEval.NUM_ERROR; + } + else if (fraction == 0) + { + return ErrorEval.DIV_ZERO; + } + int fractionLength = fraction.ToString().Length; + + bool negative = false; + long valueLong = (long)number1; + if (valueLong < 0) + { + negative = true; + valueLong = -valueLong; + number1 = -number1; + } + + double valueFractional = number1 - valueLong; + if (valueFractional == 0.0) + { + return new NumberEval(valueLong); + } + + double result = valueFractional * fraction / Math.Pow(10, fractionLength)+valueLong; + + if (negative) + { + result = result*-1; + } + + return new NumberEval(result); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length == 2) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0], args[1]); + } + + return ErrorEval.VALUE_INVALID; + } + private static Double evaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) + { + ValueEval veText = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + String strText1 = OperandResolver.CoerceValueToString(veText); + return OperandResolver.ParseDouble(strText1); + } +} +} diff --git a/testcases/main/SS/Formula/Functions/TestDollarDe.cs b/testcases/main/SS/Formula/Functions/TestDollarDe.cs new file mode 100644 index 000000000..fc8983af1 --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestDollarDe.cs @@ -0,0 +1,91 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.Formula; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestDollarDe + { + private static OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + + [Test] + public void TestInvalid() + { + confirmInvalidError("A1", "B2"); + } + + [Test] + public void TestNumError() + { + confirmNumError("22.5", "-40"); + } + + [Test] + public void TestDiv0() + { + confirmDiv0("22.5", "0"); + confirmDiv0("22.5", "0.9"); + confirmDiv0("22.5", "-0.9"); + } + + //https://support.microsoft.com/en-us/office/dollarde-function-db85aab0-1677-428a-9dfd-a38476693427 + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + var sheet = wb.CreateSheet(); + var row = sheet.CreateRow(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + var cell = row.CreateCell(0); + double tolerance = 0.000000000000001; + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARDE(1.02,16)", 1.125, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARDE(1.02,16.9)", 1.125, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARDE(1.32,16)", 3.0, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARDE(-1.02,16)", -1.125, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARDE(1.1,32)", 1.3125, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARDE(1.1,32.1)", 1.3125, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARDE(1.0,32)", 1.0, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARDE(1.000001,32)", 1.000003125, tolerance); + } + + private static ValueEval invokeValue(String number1, String number2) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1), new StringEval(number2) }; + return DollarDe.instance.Evaluate(args, ec); + } + + private static void confirmValue(String number1, String number2, double expected) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.00000000000001); + } + + private static void confirmInvalidError(String number1, String number2) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.VALUE_INVALID, result); + } + + private static void confirmNumError(String number1, String number2) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.NUM_ERROR, result); + } + + private static void confirmDiv0(String number1, String number2) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.DIV_ZERO, result); + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestDollarFr.cs b/testcases/main/SS/Formula/Functions/TestDollarFr.cs new file mode 100644 index 000000000..ba27cb66a --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestDollarFr.cs @@ -0,0 +1,87 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.Formula; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestDollarFr + { + private static OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + + [Test] + public void TestInvalid() + { + confirmInvalidError("A1", "B2"); + } + + [Test] + public void TestNumError() + { + confirmNumError("22.5", "-40"); + } + + [Test] + public void TestDiv0() + { + confirmDiv0("22.5", "0"); + confirmDiv0("22.5", "0.9"); + confirmDiv0("22.5", "-0.9"); + } + + //https://support.microsoft.com/en-us/office/dollarde-function-db85aab0-1677-428a-9dfd-a38476693427 + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + var sheet = wb.CreateSheet(); + var row = sheet.CreateRow(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + var cell = row.CreateCell(0); + double tolerance = 0.000000000000001; + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARFR(1.125,16)", 1.02, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARFR(-1.125,16)", -1.02, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARFR(1.000125,16)", 1.00002, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "DOLLARFR(1.125,32)", 1.04, tolerance); + } + + private static ValueEval invokeValue(String number1, String number2) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1), new StringEval(number2) }; + return DollarDe.instance.Evaluate(args, ec); + } + + private static void confirmValue(String number1, String number2, double expected) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.00000000000001); + } + + private static void confirmInvalidError(String number1, String number2) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.VALUE_INVALID, result); + } + + private static void confirmNumError(String number1, String number2) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.NUM_ERROR, result); + } + + private static void confirmDiv0(String number1, String number2) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.DIV_ZERO, result); + } + } +} diff --git a/testcases/main/SS/Util/Utils.cs b/testcases/main/SS/Util/Utils.cs index 5a289d03b..ce1f54be3 100644 --- a/testcases/main/SS/Util/Utils.cs +++ b/testcases/main/SS/Util/Utils.cs @@ -15,12 +15,12 @@ public static void AssertString(IFormulaEvaluator fe, ICell cell, string formula fe.ClearAllCachedResultValues(); Assert.AreEqual(expectedResult, result); } - public static void AssertDouble(IFormulaEvaluator fe, ICell cell, string formula, double expectedResult) + public static void AssertDouble(IFormulaEvaluator fe, ICell cell, string formula, double expectedResult, double delta=0.0) { cell.SetCellFormula(formula); var result = fe.Evaluate(cell).NumberValue; fe.ClearAllCachedResultValues(); - Assert.AreEqual(expectedResult, result); + Assert.AreEqual(expectedResult, result, delta); } public static void AssertError(IFormulaEvaluator fe, ICell cell, string formula, FormulaError expectedError) From fb702ad1bf542799a43ad83c823a405308b8242a Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 30 Apr 2022 15:55:42 +0800 Subject: [PATCH 114/159] POI Bug 65846 Add support for numbervalue function --- main/SS/Formula/Atp/AnalysisToolPak.cs | 1 + .../Formula/Functions/NumberValueFunction.cs | 106 ++++++++++++++++++ .../SS/Formula/Functions/TestNumberValue.cs | 34 ++++++ 3 files changed, 141 insertions(+) create mode 100644 main/SS/Formula/Functions/NumberValueFunction.cs create mode 100644 testcases/main/SS/Formula/Functions/TestNumberValue.cs diff --git a/main/SS/Formula/Atp/AnalysisToolPak.cs b/main/SS/Formula/Atp/AnalysisToolPak.cs index 4cbab937f..2e9c0d4ba 100644 --- a/main/SS/Formula/Atp/AnalysisToolPak.cs +++ b/main/SS/Formula/Atp/AnalysisToolPak.cs @@ -154,6 +154,7 @@ public override FreeRefFunction FindFunction(String name) r(m, "MULTINOMIAL", null); r(m, "NETWORKDAYS", NetworkdaysFunction.instance); r(m, "NOMINAL", null); + r(m, "NUMBERVALUE", NumberValueFunction.instance); r(m, "OCT2BIN", null); r(m, "OCT2DEC", Oct2Dec.instance); r(m, "OCT2HEX", null); diff --git a/main/SS/Formula/Functions/NumberValueFunction.cs b/main/SS/Formula/Functions/NumberValueFunction.cs new file mode 100644 index 000000000..e2c0d34bf --- /dev/null +++ b/main/SS/Formula/Functions/NumberValueFunction.cs @@ -0,0 +1,106 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace NPOI.SS.Formula.Functions +{ + public class NumberValueFunction : FreeRefFunction + { + public static FreeRefFunction instance = new NumberValueFunction(); + + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + string decSep = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator; + string groupSep = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberGroupSeparator; + String text = null; + Double result = Double.NaN; + ValueEval v1 = null; + ValueEval v2 = null; + ValueEval v3 = null; + try + { + if (args.Length == 1) + { + v1 = OperandResolver.GetSingleValue(args[0], ec.RowIndex, ec.ColumnIndex); + text = OperandResolver.CoerceValueToString(v1); + } + else if (args.Length == 2) + { + v1 = OperandResolver.GetSingleValue(args[0], ec.RowIndex, ec.ColumnIndex); + v2 = OperandResolver.GetSingleValue(args[1], ec.RowIndex, ec.ColumnIndex); + text = OperandResolver.CoerceValueToString(v1); + decSep = OperandResolver.CoerceValueToString(v2).Substring(0, 1); //If multiple characters are used in the Decimal_separator or Group_separator arguments, only the first character is used. + } + else if (args.Length == 3) + { + v1 = OperandResolver.GetSingleValue(args[0], ec.RowIndex, ec.ColumnIndex); + v2 = OperandResolver.GetSingleValue(args[1], ec.RowIndex, ec.ColumnIndex); + v3 = OperandResolver.GetSingleValue(args[2], ec.RowIndex, ec.ColumnIndex); + text = OperandResolver.CoerceValueToString(v1); + decSep = OperandResolver.CoerceValueToString(v2).Substring(0, 1); //If multiple characters are used in the Decimal_separator or Group_separator arguments, only the first character is used. + groupSep = OperandResolver.CoerceValueToString(v3).Substring(0, 1); //If multiple characters are used in the Decimal_separator or Group_separator arguments, only the first character is used. + } + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + + if (text == "") + text = "0"; //If an empty string ("") is specified as the Text argument, the result is 0. + text = text.Replace(" ", ""); //Empty spaces in the Text argument are ignored, even in the middle of the argument. For example, " 3 000 " is returned as 3000. + String[] parts = text.Split(new string[] { decSep }, StringSplitOptions.RemoveEmptyEntries); + String sigPart = ""; + String decPart = ""; + if (parts.Length > 2) return ErrorEval.VALUE_INVALID; //If a decimal separator is used more than once in the Text argument, NUMBERVALUE returns the #VALUE! error value. + if (parts.Length > 1) + { + sigPart = parts[0]; + decPart = parts[1]; + if (decPart.Contains(groupSep)) return ErrorEval.VALUE_INVALID; //If the group separator occurs after the decimal separator in the Text argument, NUMBERVALUE returns the #VALUE! error value. + sigPart = sigPart.Replace(groupSep, ""); //If the group separator occurs before the decimal separator in the Text argument , the group separator is ignored. + text = sigPart + "." + decPart; + } + else if (parts.Length > 0) + { + sigPart = parts[0]; + sigPart = sigPart.Replace(groupSep, ""); //If the group separator occurs before the decimal separator in the Text argument , the group separator is ignored. + text = sigPart; + } + // If the Text argument ends in one or more percent signs(%), they are used in the calculation of the result. + //Multiple percent signs are additive if they are used in the Text argument just as they are if they are used in a formula. + //For example, =NUMBERVALUE("9%%") returns the same result (0.0009) as the formula =9%%. + int countPercent = 0; + while (text.EndsWith("%")) + { + countPercent++; + text = text.Substring(0, text.Length - 1); + } + + try + { + result = Double.Parse(text); + result = result / Math.Pow(100, countPercent); //If the Text argument ends in one or more percent signs (%), they are used in the calculation of the result. + checkValue(result); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + catch (Exception anyex) + { + return ErrorEval.VALUE_INVALID; //If any of the arguments are not valid, NUMBERVALUE returns the #VALUE! error value. + } + + return new NumberEval(result); + } + private static void checkValue(double result) + { + if (Double.IsNaN(result) || Double.IsInfinity(result)) { + throw new EvaluationException(ErrorEval.NUM_ERROR); + } + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestNumberValue.cs b/testcases/main/SS/Formula/Functions/TestNumberValue.cs new file mode 100644 index 000000000..131505a0f --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestNumberValue.cs @@ -0,0 +1,34 @@ +using NPOI.HSSF.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestNumberValue + { + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + var sheet = wb.CreateSheet(); + var row = sheet.CreateRow(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + var cell = row.CreateCell(0); + SS.Util.Utils.AssertDouble(fe, cell, "NUMBERVALUE(\"2.500,27\",\",\",\".\")", 2500.27, 0.000000000001); + } + + [Test] + public void TestMicrosoftExample2() + { + HSSFWorkbook wb = new HSSFWorkbook(); + var sheet = wb.CreateSheet(); + var row = sheet.CreateRow(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + var cell = row.CreateCell(0); + SS.Util.Utils.AssertDouble(fe, cell, "NUMBERVALUE(\"3.5%\")", 0.035, 0.000000000001); + } + } +} From ae6180cacdf168e66cd3fb7a36cb04916ed0cf8e Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 30 Apr 2022 21:57:43 +0800 Subject: [PATCH 115/159] POI Bug 61765 add "WorkBook" to WORKBOOK_DIR_ENTRY_NAMES --- main/HSSF/Model/InternalWorkbook.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/main/HSSF/Model/InternalWorkbook.cs b/main/HSSF/Model/InternalWorkbook.cs index 8221c238a..6dc7c4a12 100644 --- a/main/HSSF/Model/InternalWorkbook.cs +++ b/main/HSSF/Model/InternalWorkbook.cs @@ -74,6 +74,7 @@ public class InternalWorkbook "Workbook", // as per BIFF8 spec "WORKBOOK", // Typically from third party programs "BOOK", // Typically odd Crystal Reports exports + "WorkBook", // Another third party program special }; /** * Name of older (pre-Excel 97) Workbook streams, which From 2668e4535e8322a9a2ca7f7cd42e2cfaa43bd42d Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 30 Apr 2022 22:18:28 +0800 Subject: [PATCH 116/159] POI Bug 61033 Add Workbook.setCellFormulaValidation to control whether formulas are validated during Cell.setCellFormula or not --- ooxml/XSSF/UserModel/XSSFCell.cs | 15 +++--- ooxml/XSSF/UserModel/XSSFWorkbook.cs | 8 +++- .../ooxml/XSSF/UserModel/TestXSSFCell.cs | 46 +++++++++++++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/ooxml/XSSF/UserModel/XSSFCell.cs b/ooxml/XSSF/UserModel/XSSFCell.cs index be06e2e27..4976b9c09 100644 --- a/ooxml/XSSF/UserModel/XSSFCell.cs +++ b/ooxml/XSSF/UserModel/XSSFCell.cs @@ -617,18 +617,21 @@ internal void SetCellArrayFormula(String formula, CellRangeAddress range) private void SetFormula(String formula, FormulaType formulaType) { - IWorkbook wb = _row.Sheet.Workbook; + XSSFWorkbook wb = (XSSFWorkbook)_row.Sheet.Workbook; if (formula == null) { ((XSSFWorkbook)wb).OnDeleteFormula(this); - if (_cell.IsSetF()) _cell.unsetF(); + if (_cell.IsSetF()) + _cell.unsetF(); return; } - IFormulaParsingWorkbook fpb = XSSFEvaluationWorkbook.Create(wb); - //validate through the FormulaParser - FormulaParser.Parse(formula, fpb, formulaType, wb.GetSheetIndex(this.Sheet), RowIndex); - + if (wb.CellFormulaValidation) + { + IFormulaParsingWorkbook fpb = XSSFEvaluationWorkbook.Create(wb); + //validate through the FormulaParser + FormulaParser.Parse(formula, fpb, formulaType, wb.GetSheetIndex(this.Sheet), RowIndex); + } CT_CellFormula f = new CT_CellFormula(); f.Value = formula; _cell.f= (f); diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index dec830c3f..5f4973c0f 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -148,7 +148,7 @@ public class XSSFWorkbook : POIXMLDocument, IWorkbook * See {@link NPOI.ss.usermodel.Row.MissingCellPolicy} */ private MissingCellPolicy _missingCellPolicy = MissingCellPolicy.RETURN_NULL_AND_BLANK; - + private bool cellFormulaValidation = true; /** * array of pictures for this workbook */ @@ -2361,7 +2361,11 @@ public List PivotTables this.pivotTables = value; } } - + public bool CellFormulaValidation + { + get { return this.cellFormulaValidation; } + set { this.cellFormulaValidation = false; } + } #region IWorkbook Members diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFCell.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFCell.cs index ad122c83f..8a49f6cf1 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFCell.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFCell.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; using NPOI.XSSF; using NPOI.XSSF.UserModel; +using NPOI.SS.Formula; namespace TestCases.XSSF.UserModel { @@ -160,6 +161,51 @@ public void TestFormulaString() Assert.AreEqual(CellType.Blank, cell.CellType); Assert.AreEqual(ST_CellType.n, ctCell.t); Assert.AreEqual(cell.StringCellValue, ""); + + // check behavior with setCellFormulaValidation + String invalidFormula = "A", validFormula = "A2"; + FormulaParseException fpe = null; + // check that default is true + Assert.IsTrue(wb.CellFormulaValidation); + + // check that valid formula does not throw exception + try + { + cell.SetCellFormula(validFormula); + } + catch (FormulaParseException e) + { + fpe = e; + } + Assert.IsNull(fpe); + + // check that invalid formula does throw exception + try + { + cell.SetCellFormula(invalidFormula); + } + catch (FormulaParseException e) + { + fpe = e; + } + Assert.IsNotNull(fpe); + fpe = null; + + // set cell formula validation to false + wb.CellFormulaValidation = false; + Assert.IsFalse(wb.CellFormulaValidation); + + // check that neither valid nor invalid formula throw an exception + try + { + cell.SetCellFormula(validFormula); + cell.SetCellFormula(invalidFormula); + } + catch (FormulaParseException e) + { + fpe = e; + } + Assert.IsNull(fpe); } /** From dfbfb0910adf46f4c751abd093bb21327f76878e Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 1 May 2022 06:05:16 +0800 Subject: [PATCH 117/159] POI Bug 63013 add XWPFRun Lang property --- ooxml/XWPF/Usermodel/XWPFRun.cs | 87 ++++++++++++------- testcases/ooxml/XWPF/UserModel/TestXWPFRun.cs | 2 +- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/ooxml/XWPF/Usermodel/XWPFRun.cs b/ooxml/XWPF/Usermodel/XWPFRun.cs index b5f6917cc..d91326240 100644 --- a/ooxml/XWPF/Usermodel/XWPFRun.cs +++ b/ooxml/XWPF/Usermodel/XWPFRun.cs @@ -279,7 +279,7 @@ public bool IsBold { get { - CT_RPr pr = run.rPr; + CT_RPr pr = GetRunProperties(false); if (pr == null || !pr.IsSetB()) { return false; @@ -288,7 +288,7 @@ public bool IsBold } set { - CT_RPr pr = run.IsSetRPr() ? run.rPr : run.AddNewRPr(); + CT_RPr pr = GetRunProperties(true); CT_OnOff bold = pr.IsSetB() ? pr.b : pr.AddNewB(); bold.val = value; } @@ -302,8 +302,8 @@ public String GetColor() String color = null; if (run.IsSetRPr()) { - CT_RPr pr = run.rPr; - if (pr.IsSetColor()) + CT_RPr pr = GetRunProperties(false); + if (pr!=null&&pr.IsSetColor()) { NPOI.OpenXmlFormats.Wordprocessing.CT_Color clr = pr.color; color = clr.val; //clr.xgetVal().getStringValue(); @@ -318,7 +318,7 @@ public String GetColor() */ public void SetColor(String rgbStr) { - CT_RPr pr = run.IsSetRPr() ? run.rPr : run.AddNewRPr(); + CT_RPr pr = GetRunProperties(true); NPOI.OpenXmlFormats.Wordprocessing.CT_Color color = pr.IsSetColor() ? pr.color : pr.AddNewColor(); color.val = (rgbStr); } @@ -386,14 +386,14 @@ public bool IsItalic { get { - CT_RPr pr = run.rPr; + CT_RPr pr = GetRunProperties(false); if (pr == null || !pr.IsSetI()) return false; return IsCTOnOff(pr.i); } set { - CT_RPr pr = run.IsSetRPr() ? run.rPr : run.AddNewRPr(); + CT_RPr pr = GetRunProperties(true); CT_OnOff italic = pr.IsSetI() ? pr.i : pr.AddNewI(); italic.val = value; } @@ -411,11 +411,36 @@ public UnderlinePatterns Underline { get { - CT_RPr pr = run.rPr; - return (pr != null && pr.IsSetU() && pr.u.val != null) ? - EnumConverter.ValueOf(pr.u.val) : UnderlinePatterns.None; + var value = UnderlinePatterns.None; + CT_Underline underline = GetCTUnderline(false); + if (underline != null) + { + ST_Underline baseValue = underline.val; + value = EnumConverter.ValueOf(baseValue); + } + return value; + } + set { + CT_Underline underline = GetCTUnderline(true); + underline.val = EnumConverter.ValueOf(value); } } + /** + * Get the CTUnderline for the run. + * @param create Create a new underline if necessary + * @return The underline, or null create is false and there is no underline. + */ + private CT_Underline GetCTUnderline(bool create) + { + CT_RPr pr = GetRunProperties(true); + CT_Underline underline = pr.u; + if (create && underline == null) + { + underline = pr.AddNewU(); + } + return underline; + } + internal void InsertText(CT_Text text, int textIndex) { run.GetTList().Insert(textIndex, text); @@ -526,24 +551,6 @@ public string Text return text.ToString(); } } - /** - * Specifies that the contents of this run.should be displayed along with an - * underline appearing directly below the character heigh - * If this element is not present, the default value is to leave the - * formatting applied at previous level in the style hierarchy. If this - * element is never applied in the style hierarchy, then an underline shall - * not be applied to the contents of this run. - * - * @param value - - * underline type - * @see UnderlinePatterns : all possible patterns that could be applied - */ - public void SetUnderline(UnderlinePatterns value) - { - CT_RPr pr = run.IsSetRPr() ? run.rPr : run.AddNewRPr(); - CT_Underline underline = (pr.u == null) ? pr.AddNewU() : pr.u; - underline.val = EnumConverter.ValueOf(value); - } /** * Specifies that the contents of this run.shall be displayed with a single @@ -1269,7 +1276,29 @@ static void preserveSpaces(CT_Text xs) xs.space = "preserve"; } } - + public String Lang + { + get + { + CT_RPr pr = GetRunProperties(false); + Object lang = pr == null || !pr.IsSetLang() ? null : pr.lang.val; + return (String)lang; + } + set { + CT_RPr pr = GetRunProperties(true); + CT_Language ctLang = pr.IsSetLang() ? pr.lang : pr.AddNewLang(); + ctLang.val = value; + } + } + protected CT_RPr GetRunProperties(bool create) + { + CT_RPr pr = run.IsSetRPr() ? run.rPr : null; + if (create && pr == null) + { + pr = run.AddNewRPr(); + } + return pr; + } /** * Returns the string version of the text, with tabs and * carriage returns in place of their xml equivalents. diff --git a/testcases/ooxml/XWPF/UserModel/TestXWPFRun.cs b/testcases/ooxml/XWPF/UserModel/TestXWPFRun.cs index cebf28508..f7f481633 100644 --- a/testcases/ooxml/XWPF/UserModel/TestXWPFRun.cs +++ b/testcases/ooxml/XWPF/UserModel/TestXWPFRun.cs @@ -147,7 +147,7 @@ public void TestSetGetUnderline() XWPFRun run = new XWPFRun(ctRun, p); Assert.AreEqual(UnderlinePatterns.Dash, run.Underline); - run.SetUnderline(UnderlinePatterns.None); + run.Underline = UnderlinePatterns.None; Assert.AreEqual(ST_Underline.none, rpr.u.val); } From a534937fcc5444ed1cf82b1e3a536b5d316bf4c6 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 1 May 2022 07:33:11 +0800 Subject: [PATCH 118/159] POI Github 114 https://github.com/apache/poi/pull/114 Enhancement to XWPFFootnote and related APIs --- OpenXmlFormats/Wordprocessing/HdrFtr.cs | 17 ++- OpenXmlFormats/Wordprocessing/Run.cs | 28 +++- ooxml/XWPF/Usermodel/XWPFDocument.cs | 37 ++++- ooxml/XWPF/Usermodel/XWPFFootnote.cs | 103 +++++++++++++ ooxml/XWPF/Usermodel/XWPFFootnotes.cs | 41 +++++- ooxml/XWPF/Usermodel/XWPFParagraph.cs | 13 ++ .../ooxml/XWPF/UserModel/TestXWPFFootnote.cs | 136 ++++++++++++++++++ .../ooxml/XWPF/UserModel/TestXWPFFootnotes.cs | 25 ++-- 8 files changed, 383 insertions(+), 17 deletions(-) create mode 100644 testcases/ooxml/XWPF/UserModel/TestXWPFFootnote.cs diff --git a/OpenXmlFormats/Wordprocessing/HdrFtr.cs b/OpenXmlFormats/Wordprocessing/HdrFtr.cs index 425c71a1d..23e287052 100644 --- a/OpenXmlFormats/Wordprocessing/HdrFtr.cs +++ b/OpenXmlFormats/Wordprocessing/HdrFtr.cs @@ -832,6 +832,17 @@ public CT_FtnEdn AddNewFootnote() footnoteField.Add(f); return f; } + public void RemoveFootnote(int pos) + { + this.footnoteField.RemoveAt(pos); + } + public int SizeOfFootnoteArray + { + get + { + return this.footnoteField.Count; + } + } } @@ -849,7 +860,7 @@ public class CT_FtnEdn private bool typeFieldSpecified; - private string idField = string.Empty; + private int idField = -1; public static CT_FtnEdn Parse(XmlNode node, XmlNamespaceManager namespaceManager) { @@ -858,7 +869,7 @@ public static CT_FtnEdn Parse(XmlNode node, XmlNamespaceManager namespaceManager CT_FtnEdn ctObj = new CT_FtnEdn(); if (node.Attributes["w:type"] != null) ctObj.type = (ST_FtnEdn)Enum.Parse(typeof(ST_FtnEdn), node.Attributes["w:type"].Value); - ctObj.id = XmlHelper.ReadString(node.Attributes["w:id"]); + ctObj.id = XmlHelper.ReadInt(node.Attributes["w:id"]); foreach (XmlNode childNode in node.ChildNodes) { @@ -1175,7 +1186,7 @@ public bool typeSpecified } [XmlAttribute(Form = XmlSchemaForm.Qualified, DataType = "integer", Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")] - public string id + public int id { get { diff --git a/OpenXmlFormats/Wordprocessing/Run.cs b/OpenXmlFormats/Wordprocessing/Run.cs index 62107c544..a3de32cd6 100644 --- a/OpenXmlFormats/Wordprocessing/Run.cs +++ b/OpenXmlFormats/Wordprocessing/Run.cs @@ -20,7 +20,6 @@ public class CT_R { private CT_RPr rPrField; - private ArrayList itemsField; private List itemsElementNameField; @@ -178,6 +177,23 @@ public CT_RPr AddNewRPr() return this.rPrField; } + + public CT_Empty AddNewFootnoteRef() + { + return AddNewObject(RunItemsChoiceType.footnoteRef); + } + public IList GetFootnoteRefList() + { + return GetObjectList(RunItemsChoiceType.footnoteRef); + } + public CT_Empty GetFootnoteRefArray(int pos) + { + return GetObjectArray(pos, RunItemsChoiceType.footnoteRef); + } + public CT_FtnEdnRef AddNewFootnoteReference() + { + return AddNewObject(RunItemsChoiceType.footnoteReference); + } public CT_Empty AddNewTab() { return AddNewObject(RunItemsChoiceType.tab); @@ -390,7 +406,9 @@ public static CT_R Parse(XmlNode node, XmlNamespaceManager namespaceManager) foreach (XmlNode childNode in node.ChildNodes) { if (childNode.LocalName == "rPr") + { ctObj.rPr = CT_RPr.Parse(childNode, namespaceManager); + } else if (childNode.LocalName == "instrText") { ctObj.Items.Add(CT_Text.Parse(childNode, namespaceManager)); @@ -568,9 +586,8 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(">"); if (this.rPr != null) this.rPr.Write(sw, "rPr"); - int i = 0; - + int i = 0; foreach (object o in this.Items) { if ((o is CT_Text) && this.ItemsElementName[i] == RunItemsChoiceType.instrText) @@ -656,6 +673,11 @@ public IList GetTabList() { return GetObjectList(RunItemsChoiceType.tab); } + + public IList GetFootnoteReferenceList() + { + return GetObjectList(RunItemsChoiceType.footnoteReference); + } } diff --git a/ooxml/XWPF/Usermodel/XWPFDocument.cs b/ooxml/XWPF/Usermodel/XWPFDocument.cs index c0663e87c..01f80717e 100644 --- a/ooxml/XWPF/Usermodel/XWPFDocument.cs +++ b/ooxml/XWPF/Usermodel/XWPFDocument.cs @@ -238,7 +238,7 @@ private void InitFootnotes() EndnotesDocument endnotesDocument = EndnotesDocument.Parse(xmldoc, NamespaceManager); foreach (CT_FtnEdn ctFtnEdn in endnotesDocument.Endnotes.endnote) { - endnotes.Add(Int32.Parse(ctFtnEdn.id), new XWPFFootnote(this, ctFtnEdn)); + endnotes.Add(ctFtnEdn.id, new XWPFFootnote(this, ctFtnEdn)); } } } @@ -966,10 +966,43 @@ public XWPFFootnote AddFootnote(CT_FtnEdn note) public XWPFFootnote AddEndnote(CT_FtnEdn note) { XWPFFootnote endnote = new XWPFFootnote(this, note); - endnotes.Add(int.Parse(note.id), endnote); + endnotes.Add(note.id, endnote); return endnote; } + ///

+ /// Create a new footnote and add it to the document. + /// + /// + /// The new note will have one paragraph with the style "FootnoteText" + /// and one run containing the required footnote reference with the + /// style "FootnoteReference". + /// + /// New XWPFFootnote. + public XWPFFootnote CreateFootnote() + { + XWPFFootnotes footnotes = this.CreateFootnotes(); + + XWPFFootnote footnote = footnotes.CreateFootnote(); + return footnote; + } + /// + /// Remove the specified footnote if present. + /// + /// + /// + public bool RemoveFootnote(int pos) + { + if (null != footnotes) + { + return footnotes.RemoveFootnote(pos); + } + else + { + return false; + } + } + /** * remove a BodyElement from bodyElements array list * @param pos diff --git a/ooxml/XWPF/Usermodel/XWPFFootnote.cs b/ooxml/XWPF/Usermodel/XWPFFootnote.cs index 977c8e7a1..4ffa6e21e 100644 --- a/ooxml/XWPF/Usermodel/XWPFFootnote.cs +++ b/ooxml/XWPF/Usermodel/XWPFFootnote.cs @@ -249,7 +249,110 @@ public POIXMLDocumentPart Owner return footnotes; } } + public int Id + { + get + { + return this.ctFtnEdn.id; + } + } + public List GetParagraphs() + { + return paragraphs; + } + /** + * Appends a new {@link XWPFParagraph} to this footnote. + * + * @return The new {@link XWPFParagraph} + */ + public XWPFParagraph CreateParagraph() + { + XWPFParagraph p = new XWPFParagraph(this.ctFtnEdn.AddNewP(), this); + paragraphs.Add(p); + bodyElements.Add(p); + + // If the paragraph is the first paragraph in the footnote, + // ensure that it has a footnote reference run. + if (p.Equals(GetParagraphs()[0])) + { + EnsureFootnoteRef(p); + } + return p; + } + + /** + * Ensure that the specified paragraph has a reference marker for this + * footnote by adding a footnote reference if one is not found. + *

This method is for the first paragraph in the footnote, not + * paragraphs that will refer to the footnote. For references to + * the footnote, use {@link XWPFParagraph#addFootnoteReference(XWPFFootnote)}. + *

+ *

The first run of the first paragraph in a footnote should + * contain a {@link CTFtnEdnRef} object.

+ * + * @param p The {@link XWPFParagraph} to ensure + */ + public void EnsureFootnoteRef(XWPFParagraph p) + { + + XWPFRun r = null; + if (p.Runs.Count > 0) + { + r = p.Runs[0]; + } + if (r == null) + { + r = p.CreateRun(); + } + CT_R ctr = r.GetCTR(); + bool foundRef = false; + foreach (CT_FtnEdnRef reference in ctr.GetFootnoteReferenceList()) + { + if (Id.ToString().Equals(reference.id)) + { + foundRef = true; + break; + } + } + if (!foundRef) + { + ctr.AddNewRPr().AddNewRStyle().val="FootnoteReference"; + ctr.AddNewFootnoteRef(); + } + } + + /** + * Appends a new {@link XWPFTable} to this footnote + * + * @return The new {@link XWPFTable} + */ + public XWPFTable CreateTable() + { + XWPFTable table = new XWPFTable(ctFtnEdn.AddNewTbl(), this); + if (bodyElements.Count == 0) + { + XWPFParagraph p = CreateParagraph(); + EnsureFootnoteRef(p); + } + bodyElements.Add(table); + tables.Add(table); + return table; + } + + /** + * Appends a new {@link XWPFTable} to this footnote + * @param rows Number of rows to initialize the table with + * @param cols Number of columns to initialize the table with + * @return the new {@link XWPFTable} with the specified number of rows and columns + */ + public XWPFTable CreateTable(int rows, int cols) + { + XWPFTable table = new XWPFTable(ctFtnEdn.AddNewTbl(), this, rows, cols); + bodyElements.Add(table); + tables.Add(table); + return table; + } /** * * @param cursor diff --git a/ooxml/XWPF/Usermodel/XWPFFootnotes.cs b/ooxml/XWPF/Usermodel/XWPFFootnotes.cs index 01ed32009..4aba717ab 100644 --- a/ooxml/XWPF/Usermodel/XWPFFootnotes.cs +++ b/ooxml/XWPF/Usermodel/XWPFFootnotes.cs @@ -123,7 +123,7 @@ public List GetFootnotesList() public XWPFFootnote GetFootnoteById(int id) { foreach(XWPFFootnote note in listFootnote) { - if(note.GetCTFtnEdn().id == id.ToString()) + if(note.GetCTFtnEdn().id == id) return note; } return null; @@ -167,7 +167,44 @@ public void SetXWPFDocument(XWPFDocument doc) { document = doc; } + /// + /// Create a new footnote and add it to the document. + /// + /// + /// The new note will have one paragraph with the style "FootnoteText" + /// and one run containing the required footnote reference with the + /// style "FootnoteReference". + /// + /// New XWPFFootnote + public XWPFFootnote CreateFootnote() + { + CT_FtnEdn newNote = new CT_FtnEdn(); + newNote.type = ST_FtnEdn.normal; + + XWPFFootnote footnote = AddFootnote(newNote); + int id = ctFootnotes.SizeOfFootnoteArray; + footnote.GetCTFtnEdn().id = id; + return footnote; + } + /// + /// Remove the specified footnote if present. + /// + /// + /// + public bool RemoveFootnote(int pos) + { + if (ctFootnotes.SizeOfFootnoteArray >= pos - 1) + { + ctFootnotes.RemoveFootnote(pos); + listFootnote.RemoveAt(pos); + return true; + } + else + { + return false; + } + } /** * @see NPOI.XWPF.UserModel.IBody#getPart() */ @@ -182,6 +219,8 @@ public XWPFDocument GetXWPFDocument() return (XWPFDocument)GetParent(); } } + + }//end class diff --git a/ooxml/XWPF/Usermodel/XWPFParagraph.cs b/ooxml/XWPF/Usermodel/XWPFParagraph.cs index a3ee5349b..d5b393792 100644 --- a/ooxml/XWPF/Usermodel/XWPFParagraph.cs +++ b/ooxml/XWPF/Usermodel/XWPFParagraph.cs @@ -1647,6 +1647,19 @@ public XWPFHyperlinkRun CreateHyperlinkRun(string rId) iRuns.Add(xwpfRun); return xwpfRun; } + /// + /// Add a new run with a reference to the specified footnote. The footnote reference run will have the style name "FootnoteReference". + /// + /// Footnote to which to add a reference. + public void AddFootnoteReference(XWPFFootnote footnote) + { + XWPFRun run = CreateRun(); + CT_R ctRun = run.GetCTR(); + var rstyle=ctRun.AddNewRPr().AddNewRStyle(); + rstyle.val="FootnoteReference"; + var footnoteRef = ctRun.AddNewFootnoteReference(); + footnoteRef.id= footnote.Id.ToString(); + } } } \ No newline at end of file diff --git a/testcases/ooxml/XWPF/UserModel/TestXWPFFootnote.cs b/testcases/ooxml/XWPF/UserModel/TestXWPFFootnote.cs new file mode 100644 index 000000000..306262e9b --- /dev/null +++ b/testcases/ooxml/XWPF/UserModel/TestXWPFFootnote.cs @@ -0,0 +1,136 @@ +using NPOI.OpenXmlFormats.Wordprocessing; +using NPOI.XWPF.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.XWPF.UserModel +{ + [TestFixture] + public class TestXWPFFootnote + { + private XWPFDocument docOut; + private String p1Text; + private String p2Text; + private int footnoteId; + private XWPFFootnote footnote; + + [SetUp] + public void SetUp() + { + docOut = new XWPFDocument(); + p1Text = "First paragraph in footnote"; + p2Text = "Second paragraph in footnote"; + + // NOTE: XWPFDocument.CreateFootnote() delegates directly + // to XWPFFootnotes.CreateFootnote() so this tests + // both creation of new XWPFFootnotes in document + // and XWPFFootnotes.CreateFootnote(); + + // NOTE: Creating the footnote does not automatically + // create a first paragraph. + footnote = docOut.CreateFootnote(); + footnoteId = footnote.Id; + } + [Test] + public void TestAddParagraphsToFootnote() + { + + // Add a run to the first paragraph: + + XWPFParagraph p1 = footnote.CreateParagraph(); + p1.CreateRun().SetText(p1Text); + + // Create a second paragraph: + + XWPFParagraph p = footnote.CreateParagraph(); + Assert.IsNotNull(p, "Paragraph is null"); + p.CreateRun().SetText(p2Text); + + XWPFDocument docIn = XWPFTestDataSamples.WriteOutAndReadBack(docOut); + + XWPFFootnote testFootnote = docIn.GetFootnoteByID(footnoteId); + Assert.IsNotNull(testFootnote); + + Assert.AreEqual(2, testFootnote.GetParagraphs().Count); + XWPFParagraph testP1 = testFootnote.GetParagraphs()[0]; + Assert.AreEqual(p1Text, testP1.Text); + + XWPFParagraph testP2 = testFootnote.GetParagraphs()[1]; + Assert.AreEqual(p2Text, testP2.Text); + + // The first paragraph added using CreateParagraph() should + // have the required footnote reference added to the first + // run. + + // Verify that we have a footnote reference in the first paragraph and not + // in the second paragraph. + + XWPFRun r1 = testP1.Runs[0]; + Assert.IsNotNull(r1); + Assert.IsTrue(r1.GetCTR().GetFootnoteRefList().Count > 0, "No footnote reference in testP1"); + Assert.IsNotNull(r1.GetCTR().GetFootnoteRefArray(0), "No footnote reference in testP1"); + + XWPFRun r2 = testP2.Runs[0]; + Assert.IsNotNull(r2, "Expected a run in testP2"); + Assert.IsTrue(r2.GetCTR().GetFootnoteRefList().Count == 0, "Found a footnote reference in testP2"); + + } + [Test] + public void TestAddTableToFootnote() + { + XWPFTable table = footnote.CreateTable(); + Assert.IsNotNull(table); + + XWPFDocument docIn = XWPFTestDataSamples.WriteOutAndReadBack(docOut); + + XWPFFootnote testFootnote = docIn.GetFootnoteByID(footnoteId); + XWPFTable testTable = testFootnote.GetTableArray(0); + Assert.IsNotNull(testTable); + + table = footnote.CreateTable(2, 3); + Assert.AreEqual(2, table.NumberOfRows); + Assert.AreEqual(3, table.GetRow(0).GetTableCells().Count); + + // If the table is the first body element of the footnote then + // a paragraph with the footnote reference should have been + // added automatically. + + Assert.AreEqual(3, footnote.BodyElements.Count, "Expected 3 body elements"); + IBodyElement testP1 = footnote.BodyElements[0]; + Assert.IsTrue(testP1 is XWPFParagraph, "Expected a paragraph, got " + testP1.GetType().Name); + XWPFRun r1 = ((XWPFParagraph)testP1).Runs[0]; + Assert.IsNotNull(r1); + Assert.IsTrue(r1.GetCTR().GetFootnoteRefList().Count > 0, "No footnote reference in testP1"); + Assert.IsNotNull(r1.GetCTR().GetFootnoteRefArray(0), "No footnote reference in testP1"); + + } + [Test] + public void TestRemoveFootnote() + { + // NOTE: XWPFDocument.removeFootnote() delegates directly to + // XWPFFootnotes. + docOut.CreateFootnote(); + Assert.AreEqual(2, docOut.GetFootnotes().Count, "Expected 2 footnotes"); + Assert.IsNotNull(docOut.GetFootnotes()[1], "Didn't get second footnote"); + bool result = docOut.RemoveFootnote(0); + Assert.IsTrue(result, "Remove footnote did not return true"); + Assert.AreEqual(1, docOut.GetFootnotes().Count, "Expected 1 footnote after removal"); + } + [Test] + public void TestAddFootnoteRefToParagraph() + { + XWPFParagraph p = docOut.CreateParagraph(); + var runs = p.Runs; + Assert.AreEqual(0, runs.Count, "Expected no runs in new paragraph"); + p.AddFootnoteReference(footnote); + XWPFRun run = p.Runs[0]; + CT_R ctr = run.GetCTR(); + Assert.IsNotNull(run, "Expected a run"); + CT_FtnEdnRef ref1 = ctr.GetFootnoteReferenceList()[0]; + Assert.IsNotNull(ref1); + Assert.AreEqual(footnote.Id.ToString(), ref1.id, "Footnote ID and reference ID did not match"); + } + } +} \ No newline at end of file diff --git a/testcases/ooxml/XWPF/UserModel/TestXWPFFootnotes.cs b/testcases/ooxml/XWPF/UserModel/TestXWPFFootnotes.cs index a1451b0d6..ebc8c2ab3 100644 --- a/testcases/ooxml/XWPF/UserModel/TestXWPFFootnotes.cs +++ b/testcases/ooxml/XWPF/UserModel/TestXWPFFootnotes.cs @@ -27,23 +27,32 @@ namespace TestCases.XWPF.UserModel [TestFixture] public class TestXWPFFootnotes { - + [Test] + public void TestCreateFootnotes() + { + XWPFDocument docOut = new XWPFDocument(); + XWPFFootnotes footnotes = docOut.CreateFootnotes(); + Assert.IsNotNull(footnotes); + XWPFFootnotes secondFootnotes = docOut.CreateFootnotes(); + Assert.AreSame(footnotes, secondFootnotes); + docOut.Close(); + } [Test] public void TestAddFootnotesToDocument() { XWPFDocument docOut = new XWPFDocument(); - int noteId = 1; - - XWPFFootnotes footnotes = docOut.CreateFootnotes(); - CT_FtnEdn ctNote = new CT_FtnEdn(); - ctNote.id = (noteId.ToString()); - ctNote.type = (ST_FtnEdn.normal); - footnotes.AddFootnote(ctNote); + // NOTE: XWPFDocument.createFootnote() delegates directly + // to XWPFFootnotes.createFootnote() so this tests + // both creation of new XWPFFootnotes in document + // and XWPFFootnotes.createFootnote(); + XWPFFootnote footnote = docOut.CreateFootnote(); + int noteId = footnote.Id; XWPFDocument docIn = XWPFTestDataSamples.WriteOutAndReadBack(docOut); XWPFFootnote note = docIn.GetFootnoteByID(noteId); + Assert.IsNotNull(note); Assert.AreEqual(note.GetCTFtnEdn().type, ST_FtnEdn.normal); } From 1da50c2b850029547aa2dfab9d438e8dfc34c256 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 2 May 2022 16:15:47 +0800 Subject: [PATCH 119/159] allow reading double value with exp extension fix #695 --- openxml4Net/Util/XmlHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openxml4Net/Util/XmlHelper.cs b/openxml4Net/Util/XmlHelper.cs index d084f1b8d..d72b5d104 100644 --- a/openxml4Net/Util/XmlHelper.cs +++ b/openxml4Net/Util/XmlHelper.cs @@ -176,7 +176,7 @@ public static double ReadDouble(XmlAttribute attr) else { double v; - if (double.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out v)) + if (double.TryParse(s, NumberStyles.Number|NumberStyles.Float| NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out v)) { return v; } From e9c92f0602becd8ffb0071d6e317993e481184ca Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 2 May 2022 17:51:31 +0800 Subject: [PATCH 120/159] POI Bug 49202 Please add support for the function 'PERCENTRANK' --- main/SS/Formula/Eval/FunctionEval.cs | 2 +- main/SS/Formula/Functions/PercentRank.cs | 155 ++++++++++++++++++ .../SS/Formula/Functions/TestPercentRank.cs | 76 +++++++++ 3 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 main/SS/Formula/Functions/PercentRank.cs create mode 100644 testcases/main/SS/Formula/Functions/TestPercentRank.cs diff --git a/main/SS/Formula/Eval/FunctionEval.cs b/main/SS/Formula/Eval/FunctionEval.cs index 0b17d5d83..76a544fec 100644 --- a/main/SS/Formula/Eval/FunctionEval.cs +++ b/main/SS/Formula/Eval/FunctionEval.cs @@ -411,7 +411,7 @@ private static Function[] ProduceFunctions() retval[326] = AggregateFunction.SMALL; // SMALL retval[327] = new NotImplementedFunction("QUARTILE"); // QUARTILE retval[328] = AggregateFunction.PERCENTILE; // PERCENTILE - retval[329] = new NotImplementedFunction("PERCENTRANK"); // PERCENTRANK + retval[329] = PercentRank.instance; // PERCENTRANK retval[330] = new Mode(); // MODE retval[331] = new NotImplementedFunction("TRIMMEAN"); // TRIMMEAN retval[332] = new NotImplementedFunction("TINV"); // TINV diff --git a/main/SS/Formula/Functions/PercentRank.cs b/main/SS/Formula/Functions/PercentRank.cs new file mode 100644 index 000000000..04d6b7c7a --- /dev/null +++ b/main/SS/Formula/Functions/PercentRank.cs @@ -0,0 +1,155 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + /// + /// Implementation of 'Analysis Toolpak' the Excel function PERCENTRANK() + /// + public class PercentRank : Function + { + public static Function instance = new PercentRank(); + private PercentRank() + { + // Enforce singleton + } + public ValueEval Evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) + { + if (args.Length < 2) + { + return ErrorEval.VALUE_INVALID; + } + double x; + try + { + ValueEval ev = OperandResolver.GetSingleValue(args[1], srcRowIndex, srcColumnIndex); + x = OperandResolver.CoerceValueToDouble(ev); + } + catch (EvaluationException e) + { + ValueEval error = e.GetErrorEval(); + if (error == ErrorEval.NUM_ERROR) + { + return error; + } + return ErrorEval.NUM_ERROR; + } + + List numbers = new List(); + try + { + List values = getValues(args[0], srcRowIndex, srcColumnIndex); + foreach (ValueEval ev in values) + { + if (ev is BlankEval || ev is MissingArgEval) + { + //skip + } + else + { + numbers.Add(OperandResolver.CoerceValueToDouble(ev)); + } + } + } + catch (EvaluationException e) + { + ValueEval error = e.GetErrorEval(); + if (error != ErrorEval.NA) + { + return error; + } + return ErrorEval.NUM_ERROR; + } + + if (numbers.Count==0) + { + return ErrorEval.NUM_ERROR; + } + + int significance = 3; + if (args.Length > 2) + { + try + { + ValueEval ev = OperandResolver.GetSingleValue(args[2], srcRowIndex, srcColumnIndex); + significance = OperandResolver.CoerceValueToInt(ev); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + + return calculateRank(numbers, x, significance, true); + } + private ValueEval calculateRank(List numbers, double x, int significance, bool recurse) + { + double closestMatchBelow = Double.MinValue; + double closestMatchAbove = Double.MaxValue; + if (recurse) + { + foreach (Double d in numbers) + { + if (d <= x && d > closestMatchBelow) closestMatchBelow = d; + if (d > x && d < closestMatchAbove) closestMatchAbove = d; + } + } + if (!recurse || closestMatchBelow == x || closestMatchAbove == x) + { + int lessThanCount = 0; + int greaterThanCount = 0; + foreach (Double d in numbers) + { + if (d < x) lessThanCount++; + else if (d > x) greaterThanCount++; + } + if (greaterThanCount == numbers.Count|| lessThanCount == numbers.Count) + { + return ErrorEval.NA; + } + var result = (double)lessThanCount / (double)(lessThanCount + greaterThanCount); + return new NumberEval(Math.Floor(result*Math.Pow(10,significance))/Math.Pow(10, significance)); + } + else + { + ValueEval belowRank = calculateRank(numbers, closestMatchBelow, significance, false); + if (!(belowRank is NumberEval)) { + return belowRank; + } + ValueEval aboveRank = calculateRank(numbers, closestMatchAbove, significance, false); + if (!(aboveRank is NumberEval)) { + return aboveRank; + } + NumberEval below = (NumberEval)belowRank; + NumberEval above = (NumberEval)aboveRank; + double diff = closestMatchAbove - closestMatchBelow; + double pos = x - closestMatchBelow; + double rankDiff = above.NumberValue - below.NumberValue; + var result = below.NumberValue + (rankDiff * (pos / diff)); + return new NumberEval(Math.Round(result,significance)); + } + } + private List getValues(ValueEval eval, int srcRowIndex, int srcColumnIndex) + { + if (eval is AreaEval) + { + AreaEval ae = (AreaEval)eval; + List list = new List(); + for (int r = ae.FirstRow; r <= ae.LastRow; r++) + { + for (int c = ae.FirstColumn; c <= ae.LastColumn; c++) + { + list.Add(OperandResolver.GetSingleValue(ae.GetAbsoluteValue(r, c), r, c)); + } + } + return list; + } + else + { + return new List() { OperandResolver.GetSingleValue(eval, srcRowIndex, srcColumnIndex) }; + } + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestPercentRank.cs b/testcases/main/SS/Formula/Functions/TestPercentRank.cs new file mode 100644 index 000000000..129c4eeab --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestPercentRank.cs @@ -0,0 +1,76 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestPercentRank + { + //https://support.microsoft.com/en-us/office/percentrank-function-f1b5836c-9619-4847-9fc9-080ec9024442 + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = initWorkbook1(); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(0).CreateCell(100); + confirmNumericResult(fe, cell, "PERCENTRANK(A2:A11,2)", 0.333); + confirmNumericResult(fe, cell, "PERCENTRANK(A2:A11,8,2)", 0.66); + confirmNumericResult(fe, cell, "PERCENTRANK(A2:A11,8,4)", 0.6666); + confirmNumericResult(fe, cell, "PERCENTRANK(A2:A11,4)", 0.555); + confirmNumericResult(fe, cell, "PERCENTRANK(A2:A11,8)", 0.666); + confirmNumericResult(fe, cell, "PERCENTRANK(A2:A11,5)", 0.583); + + } + + [Test] + public void TestErrorCases() + { + HSSFWorkbook wb = initWorkbook1(); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(0).CreateCell(100); + confirmErrorResult(fe, cell, "PERCENTRANK(A2:A11,0)", FormulaError.NA); + confirmErrorResult(fe, cell, "PERCENTRANK(A2:A11,100)", FormulaError.NA); + confirmErrorResult(fe, cell, "PERCENTRANK(B2:B11,100)", FormulaError.NUM); + + } + + private HSSFWorkbook initWorkbook1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + SS.Util.Utils.AddRow(sheet, 0, "Data"); + SS.Util.Utils.AddRow(sheet, 1, 13); + SS.Util.Utils.AddRow(sheet, 2, 12); + SS.Util.Utils.AddRow(sheet, 3, 11); + SS.Util.Utils.AddRow(sheet, 4, 8); + SS.Util.Utils.AddRow(sheet, 5, 4); + SS.Util.Utils.AddRow(sheet, 6, 3); + SS.Util.Utils.AddRow(sheet, 7, 2); + SS.Util.Utils.AddRow(sheet, 8, 1); + SS.Util.Utils.AddRow(sheet, 9, 1); + SS.Util.Utils.AddRow(sheet, 10, 1); + return wb; + } + + private static void confirmNumericResult(HSSFFormulaEvaluator fe, ICell cell, String formulaText, double expectedResult) + { + cell.SetCellFormula(formulaText); + fe.NotifyUpdateCell(cell); + CellValue result = fe.Evaluate(cell); + Assert.AreEqual(CellType.Numeric, result.CellType); + Assert.AreEqual(expectedResult, result.NumberValue, 0.0001); + } + + private static void confirmErrorResult(HSSFFormulaEvaluator fe, ICell cell, String formulaText, FormulaError expectedResult) + { + cell.SetCellFormula(formulaText); + fe.NotifyUpdateCell(cell); + CellValue result = fe.Evaluate(cell); + Assert.AreEqual(expectedResult.Code, result.ErrorValue); + } + } +} From e2ec9cfcd0fca7a7ef506aadce840873bb33c68d Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 18 Oct 2021 11:29:01 +0800 Subject: [PATCH 121/159] Create ArrayFunction.cs --- main/SS/Formula/Functions/ArrayFunction.cs | 177 +++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/main/SS/Formula/Functions/ArrayFunction.cs b/main/SS/Formula/Functions/ArrayFunction.cs index f6f1aaff9..54c24b5bb 100644 --- a/main/SS/Formula/Functions/ArrayFunction.cs +++ b/main/SS/Formula/Functions/ArrayFunction.cs @@ -5,6 +5,10 @@ namespace NPOI.SS.Formula.Functions { + /** + * @author Robert Hulbert + * Common Interface for any excel built-in function that has implemented array formula functionality. + */ public interface ArrayFunction { ///
@@ -15,5 +19,178 @@ public interface ArrayFunction /// column index of the cell containing the formula under evaluation /// The evaluated result, possibly an ErrorEval, never null ValueEval EvaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex); + /** + * Evaluate an array function with two arguments. + * + * @param arg0 the first function argument. Empty values are represented with + * {@link BlankEval} or {@link MissingArgEval}, never null + * @param arg1 the first function argument. Empty values are represented with + * @link BlankEval} or {@link MissingArgEval}, never null + * + * @param srcRowIndex row index of the cell containing the formula under evaluation + * @param srcColumnIndex column index of the cell containing the formula under evaluation + * @return The evaluated result, possibly an {@link ErrorEval}, never null. + * Note - Excel uses the error code #NUM! instead of IEEE NaN, so when + * numeric functions evaluate to {@link Double#NaN} be sure to translate the result to {@link + * ErrorEval#NUM_ERROR}. + */ + ValueEval EvaluateTwoArrayArgs(ValueEval arg0, ValueEval arg1, int srcRowIndex, int srcColumnIndex, + BiFunction evalFunc) + { + int w1, w2, h1, h2; + int a1FirstCol = 0, a1FirstRow = 0; + if (arg0 is AreaEval) { + AreaEval ae = (AreaEval)arg0; + w1 = ae.Width; + h1 = ae.Height; + a1FirstCol = ae.FirstColumn; + a1FirstRow = ae.FirstRow; + } else if (arg0 is RefEval){ + RefEval ref1 = (RefEval)arg0; + w1 = 1; + h1 = 1; + a1FirstCol = ref1.Column; + a1FirstRow = ref1.Row; + } else + { + w1 = 1; + h1 = 1; + } + int a2FirstCol = 0, a2FirstRow = 0; + if (arg1 is AreaEval) { + AreaEval ae = (AreaEval)arg1; + w2 = ae.Width; + h2 = ae.Height; + a2FirstCol = ae.FirstColumn; + a2FirstRow = ae.FirstRow; + } else if (arg1 is RefEval){ + RefEval ref1 = (RefEval)arg1; + w2 = 1; + h2 = 1; + a2FirstCol = ref1.Column; + a2FirstRow = ref1.Row; + } else + { + w2 = 1; + h2 = 1; + } + + int width = Math.Max(w1, w2); + int height = Math.Max(h1, h2); + + ValueEval[] vals = new ValueEval[height * width]; + + int idx = 0; + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + ValueEval vA; + try + { + vA = OperandResolver.GetSingleValue(arg0, a1FirstRow + i, a1FirstCol + j); + } + catch (FormulaParseException e) + { + vA = ErrorEval.NAME_INVALID; + } + catch (EvaluationException e) + { + vA = e.GetErrorEval(); + } + ValueEval vB; + try + { + vB = OperandResolver.GetSingleValue(arg1, a2FirstRow + i, a2FirstCol + j); + } + catch (FormulaParseException e) + { + vB = ErrorEval.NAME_INVALID; + } + catch (EvaluationException e) + { + vB = e.GetErrorEval(); + } + if (vA is ErrorEval) { + vals[idx++] = vA; + } else if (vB is ErrorEval) { + vals[idx++] = vB; + } else + { + vals[idx++] = evalFunc.apply(vA, vB); + } + + } + } + + if (vals.Length == 1) { + return vals[0]; + } + + return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals); + } + + ValueEval EvaluateOneArrayArg(ValueEval[] args, int srcRowIndex, int srcColumnIndex, + java.util.function.Function evalFunc) + { + ValueEval arg0 = args[0]; + + int w1, w2, h1, h2; + int a1FirstCol = 0, a1FirstRow = 0; + if (arg0 is AreaEval) { + AreaEval ae = (AreaEval)arg0; + w1 = ae.Width; + h1 = ae.Height; + a1FirstCol = ae.FirstColumn; + a1FirstRow = ae.FirstRow; + } else if (arg0 is RefEval) { + RefEval ref1 = (RefEval)arg0; + w1 = 1; + h1 = 1; + a1FirstCol = ref1.Column; + a1FirstRow = ref1.Row; + } else + { + w1 = 1; + h1 = 1; + } + w2 = 1; + h2 = 1; + + int width = Math.Max(w1, w2); + int height = Math.Max(h1, h2); + + ValueEval[] vals = new ValueEval[height * width]; + + int idx = 0; + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + ValueEval vA; + try + { + vA = OperandResolver.GetSingleValue(arg0, a1FirstRow + i, a1FirstCol + j); + } + catch (FormulaParseException e) + { + vA = ErrorEval.NAME_INVALID; + } + catch (EvaluationException e) + { + vA = e.GetErrorEval(); + } + vals[idx++] = evalFunc.apply(vA); + } + } + + if (vals.Length == 1) + { + return vals[0]; + } + + return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals); + + } } } From 5d98ffffbf2ebbe45468f337c2cb857bbd719fb8 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Tue, 26 Apr 2022 08:22:07 +0800 Subject: [PATCH 122/159] Update testcase csproj files --- OpenXmlFormats/NPOI.OpenXmlFormats.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenXmlFormats/NPOI.OpenXmlFormats.csproj b/OpenXmlFormats/NPOI.OpenXmlFormats.csproj index 6f00a9aed..78325f4ce 100644 --- a/OpenXmlFormats/NPOI.OpenXmlFormats.csproj +++ b/OpenXmlFormats/NPOI.OpenXmlFormats.csproj @@ -183,6 +183,7 @@ + From 7c526bb65f30336d064f3500e4ebab6922405774 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 2 May 2022 18:13:26 +0800 Subject: [PATCH 123/159] Revert "Create ArrayFunction.cs" This reverts commit e2ec9cfcd0fca7a7ef506aadce840873bb33c68d. --- main/SS/Formula/Functions/ArrayFunction.cs | 177 --------------------- 1 file changed, 177 deletions(-) diff --git a/main/SS/Formula/Functions/ArrayFunction.cs b/main/SS/Formula/Functions/ArrayFunction.cs index 54c24b5bb..f6f1aaff9 100644 --- a/main/SS/Formula/Functions/ArrayFunction.cs +++ b/main/SS/Formula/Functions/ArrayFunction.cs @@ -5,10 +5,6 @@ namespace NPOI.SS.Formula.Functions { - /** - * @author Robert Hulbert - * Common Interface for any excel built-in function that has implemented array formula functionality. - */ public interface ArrayFunction { /// @@ -19,178 +15,5 @@ public interface ArrayFunction /// column index of the cell containing the formula under evaluation /// The evaluated result, possibly an ErrorEval, never null ValueEval EvaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex); - /** - * Evaluate an array function with two arguments. - * - * @param arg0 the first function argument. Empty values are represented with - * {@link BlankEval} or {@link MissingArgEval}, never null - * @param arg1 the first function argument. Empty values are represented with - * @link BlankEval} or {@link MissingArgEval}, never null - * - * @param srcRowIndex row index of the cell containing the formula under evaluation - * @param srcColumnIndex column index of the cell containing the formula under evaluation - * @return The evaluated result, possibly an {@link ErrorEval}, never null. - * Note - Excel uses the error code #NUM! instead of IEEE NaN, so when - * numeric functions evaluate to {@link Double#NaN} be sure to translate the result to {@link - * ErrorEval#NUM_ERROR}. - */ - ValueEval EvaluateTwoArrayArgs(ValueEval arg0, ValueEval arg1, int srcRowIndex, int srcColumnIndex, - BiFunction evalFunc) - { - int w1, w2, h1, h2; - int a1FirstCol = 0, a1FirstRow = 0; - if (arg0 is AreaEval) { - AreaEval ae = (AreaEval)arg0; - w1 = ae.Width; - h1 = ae.Height; - a1FirstCol = ae.FirstColumn; - a1FirstRow = ae.FirstRow; - } else if (arg0 is RefEval){ - RefEval ref1 = (RefEval)arg0; - w1 = 1; - h1 = 1; - a1FirstCol = ref1.Column; - a1FirstRow = ref1.Row; - } else - { - w1 = 1; - h1 = 1; - } - int a2FirstCol = 0, a2FirstRow = 0; - if (arg1 is AreaEval) { - AreaEval ae = (AreaEval)arg1; - w2 = ae.Width; - h2 = ae.Height; - a2FirstCol = ae.FirstColumn; - a2FirstRow = ae.FirstRow; - } else if (arg1 is RefEval){ - RefEval ref1 = (RefEval)arg1; - w2 = 1; - h2 = 1; - a2FirstCol = ref1.Column; - a2FirstRow = ref1.Row; - } else - { - w2 = 1; - h2 = 1; - } - - int width = Math.Max(w1, w2); - int height = Math.Max(h1, h2); - - ValueEval[] vals = new ValueEval[height * width]; - - int idx = 0; - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { - ValueEval vA; - try - { - vA = OperandResolver.GetSingleValue(arg0, a1FirstRow + i, a1FirstCol + j); - } - catch (FormulaParseException e) - { - vA = ErrorEval.NAME_INVALID; - } - catch (EvaluationException e) - { - vA = e.GetErrorEval(); - } - ValueEval vB; - try - { - vB = OperandResolver.GetSingleValue(arg1, a2FirstRow + i, a2FirstCol + j); - } - catch (FormulaParseException e) - { - vB = ErrorEval.NAME_INVALID; - } - catch (EvaluationException e) - { - vB = e.GetErrorEval(); - } - if (vA is ErrorEval) { - vals[idx++] = vA; - } else if (vB is ErrorEval) { - vals[idx++] = vB; - } else - { - vals[idx++] = evalFunc.apply(vA, vB); - } - - } - } - - if (vals.Length == 1) { - return vals[0]; - } - - return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals); - } - - ValueEval EvaluateOneArrayArg(ValueEval[] args, int srcRowIndex, int srcColumnIndex, - java.util.function.Function evalFunc) - { - ValueEval arg0 = args[0]; - - int w1, w2, h1, h2; - int a1FirstCol = 0, a1FirstRow = 0; - if (arg0 is AreaEval) { - AreaEval ae = (AreaEval)arg0; - w1 = ae.Width; - h1 = ae.Height; - a1FirstCol = ae.FirstColumn; - a1FirstRow = ae.FirstRow; - } else if (arg0 is RefEval) { - RefEval ref1 = (RefEval)arg0; - w1 = 1; - h1 = 1; - a1FirstCol = ref1.Column; - a1FirstRow = ref1.Row; - } else - { - w1 = 1; - h1 = 1; - } - w2 = 1; - h2 = 1; - - int width = Math.Max(w1, w2); - int height = Math.Max(h1, h2); - - ValueEval[] vals = new ValueEval[height * width]; - - int idx = 0; - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { - ValueEval vA; - try - { - vA = OperandResolver.GetSingleValue(arg0, a1FirstRow + i, a1FirstCol + j); - } - catch (FormulaParseException e) - { - vA = ErrorEval.NAME_INVALID; - } - catch (EvaluationException e) - { - vA = e.GetErrorEval(); - } - vals[idx++] = evalFunc.apply(vA); - } - } - - if (vals.Length == 1) - { - return vals[0]; - } - - return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals); - - } } } From 434a5bb5724e7f226e6985a7dd4e6e0c4476f60b Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 2 May 2022 21:17:50 +0800 Subject: [PATCH 124/159] Add MatrixFunction implementation --- main/SS/Formula/CacheAreaEval.cs | 116 ++++++++ main/SS/Formula/Eval/OperandResolver.cs | 39 +++ .../TwoOperandNumericOperation.cs | 43 ++- main/SS/Formula/Functions/MatrixFunction.cs | 264 ++++++++++++++++++ main/SS/Formula/OperationEvaluationContext.cs | 38 +++ main/SS/Formula/PTG/ArrayPtg.cs | 7 +- main/SS/Formula/WorkbookEvaluator.cs | 50 +++- .../main/HSSF/Model/TestFormulaParser.cs | 12 +- testcases/main/HSSF/Record/TestNameRecord.cs | 6 +- testcases/main/SS/Formula/PTG/TestArrayPtg.cs | 12 +- 10 files changed, 566 insertions(+), 21 deletions(-) create mode 100644 main/SS/Formula/CacheAreaEval.cs create mode 100644 main/SS/Formula/Functions/MatrixFunction.cs diff --git a/main/SS/Formula/CacheAreaEval.cs b/main/SS/Formula/CacheAreaEval.cs new file mode 100644 index 000000000..c043e3ab6 --- /dev/null +++ b/main/SS/Formula/CacheAreaEval.cs @@ -0,0 +1,116 @@ +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.PTG; +using NPOI.SS.Util; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula +{ + public class CacheAreaEval:AreaEvalBase + { + ValueEval[] _values; + public CacheAreaEval(AreaI ptg, ValueEval[] values):base(ptg) + { + _values = values; + } + public CacheAreaEval(int firstRow, int firstColumn, int lastRow, int lastColumn, ValueEval[] values): + base(firstRow, firstColumn, lastRow, lastColumn) + { + _values = values; + } + public override ValueEval GetRelativeValue(int relativeRowIndex, int relativeColumnIndex) + { + return GetRelativeValue(-1, relativeRowIndex, relativeColumnIndex); + } + + public override ValueEval GetRelativeValue(int sheetIndex, int relativeRowIndex, int relativeColumnIndex) + { + int oneDimensionalIndex = relativeRowIndex * Width + relativeColumnIndex; + return _values[oneDimensionalIndex]; + } + public override AreaEval Offset(int relFirstRowIx, int relLastRowIx, + int relFirstColIx, int relLastColIx) + { + + AreaI area = new OffsetArea(FirstRow, FirstColumn, + relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); + + int height = area.LastRow - area.FirstRow + 1; + int width = area.LastColumn - area.FirstColumn + 1; + + ValueEval[] newVals = new ValueEval[height * width]; + + int startRow = area.FirstRow - FirstRow; + int startCol = area.FirstColumn - FirstColumn; + + for (int j = 0; j < height; j++) + { + for (int i = 0; i < width; i++) + { + ValueEval temp; + + /* CacheAreaEval is only temporary value representation, does not equal sheet selection + * so any attempts going beyond the selection results in BlankEval + */ + if (startRow + j > LastRow || startCol + i > LastColumn) + { + temp = BlankEval.instance; + } + else + { + temp = _values[(startRow + j) * Width + (startCol + i)]; + } + newVals[j * width + i] = temp; + } + } + + return new CacheAreaEval(area, newVals); + } + public override TwoDEval GetRow(int rowIndex) + { + if (rowIndex >= Height) + { + throw new ArgumentException("Invalid rowIndex " + rowIndex + + ". Allowable range is (0.." + Height + ")."); + } + int absRowIndex = FirstRow + rowIndex; + ValueEval[] values = new ValueEval[Width]; + + for (int i = 0; i < values.Length; i++) + { + values[i] = GetRelativeValue(rowIndex, i); + } + return new CacheAreaEval(absRowIndex, FirstColumn, absRowIndex, LastColumn, values); + } + + public override TwoDEval GetColumn(int columnIndex) + { + if (columnIndex >= Width) + { + throw new ArgumentException("Invalid columnIndex " + columnIndex + + ". Allowable range is (0.." + Width + ")."); + } + int absColIndex = FirstColumn+ columnIndex; + ValueEval[] values = new ValueEval[Height]; + + for (int i = 0; i < values.Length; i++) + { + values[i] = GetRelativeValue(i, columnIndex); + } + + return new CacheAreaEval(FirstRow, absColIndex, LastRow, absColIndex, values); + } + + public override string ToString() + { + CellReference crA = new CellReference(FirstRow, FirstColumn); + CellReference crB = new CellReference(LastRow, LastColumn); + return GetType().Name + "[" + + crA.FormatAsString() + + ':' + + crB.FormatAsString() + + "]"; + } + } +} diff --git a/main/SS/Formula/Eval/OperandResolver.cs b/main/SS/Formula/Eval/OperandResolver.cs index 96c69bb79..b9f6b2697 100644 --- a/main/SS/Formula/Eval/OperandResolver.cs +++ b/main/SS/Formula/Eval/OperandResolver.cs @@ -17,6 +17,7 @@ namespace NPOI.SS.Formula.Eval { + using NPOI.SS.Util; using System; using System.Globalization; @@ -387,5 +388,43 @@ public static String CoerceValueToString(ValueEval ve) } throw new InvalidOperationException("Unexpected eval (" + ve.GetType().Name + ")"); } + /** + * Retrieves a single value from an area evaluation utilizing the 2D indices of the cell + * within its own area reference to index the value in the area evaluation. + * + * @param ae area reference after evaluation + * @param cell the source cell of the formula that contains its 2D indices + * @return a NumberEval, StringEval, BoolEval or BlankEval. or ErrorEval + * Never null. + */ + + public static ValueEval GetElementFromArray(AreaEval ae, IEvaluationCell cell) + { + CellRangeAddress range = cell.ArrayFormulaRange; + int relativeRowIndex = cell.RowIndex - range.FirstRow; + int relativeColIndex = cell.ColumnIndex - range.FirstColumn; + + if (ae.IsColumn) + { + if (ae.IsRow) + { + return ae.GetRelativeValue(0, 0); + } + else if (relativeRowIndex < ae.Height) + { + return ae.GetRelativeValue(relativeRowIndex, 0); + } + } + else if (!ae.IsRow && relativeRowIndex < ae.Height && relativeColIndex < ae.Width) + { + return ae.GetRelativeValue(relativeRowIndex, relativeColIndex); + } + else if (ae.IsRow && relativeColIndex < ae.Width) + { + return ae.GetRelativeValue(0, relativeColIndex); + } + + return ErrorEval.NA; + } } } \ No newline at end of file diff --git a/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs b/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs index 3371f34ed..ad2e4c30b 100644 --- a/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs +++ b/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs @@ -4,7 +4,7 @@ namespace NPOI.SS.Formula.Eval { - public abstract class TwoOperandNumericOperation : Fixed2ArgFunction + public abstract class TwoOperandNumericOperation : Fixed2ArgFunction, ArrayFunction { //public int Type //{ @@ -19,6 +19,15 @@ protected double SingleOperandEvaluate(ValueEval arg, int srcCellRow, int srcCel ValueEval ve = OperandResolver.GetSingleValue(arg, srcCellRow, srcCellCol); return OperandResolver.CoerceValueToDouble(ve); } + public ValueEval EvaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) + { + if (args.Length != 2) + { + return ErrorEval.VALUE_INVALID; + } + Func func = this.Evaluate; + return new ArrayEval(func).Evaluate(srcRowIndex, srcColumnIndex, args[0], args[1]); + } public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { @@ -55,5 +64,37 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva public static NPOI.SS.Formula.Functions.Function MultiplyEval = new MultiplyEval(); public static NPOI.SS.Formula.Functions.Function PowerEval = new PowerEval(); public static NPOI.SS.Formula.Functions.Function SubtractEval = new SubtractEval(); + + private class ArrayEval : MatrixFunction.TwoArrayArg + { + Func _evaluateFunc = null; + public ArrayEval(Func evalFunc) + { + _evaluateFunc = evalFunc; + } + + private MatrixFunction.MutableValueCollector instance = new MatrixFunction.MutableValueCollector(false, true); + + protected override double[] CollectValues(ValueEval arg) + { + return instance.collectValues(arg); + } + + protected override double[,] Evaluate(double[,] d1, double[,] d2) + { + int width = d1.GetLength(1) < d2.GetLength(1) ? d1.GetLength(1) : d2.GetLength(1); + int height = (d1.Length < d2.Length) ? d1.Length : d2.Length; + + double[,] result = new double[height, width]; + + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + result[j, i] = _evaluateFunc(d1[j, i], d2[j, i]); + + } + } + return result; + } + } } } \ No newline at end of file diff --git a/main/SS/Formula/Functions/MatrixFunction.cs b/main/SS/Formula/Functions/MatrixFunction.cs new file mode 100644 index 000000000..b9fb36971 --- /dev/null +++ b/main/SS/Formula/Functions/MatrixFunction.cs @@ -0,0 +1,264 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class MatrixFunction + { + /* retrieves 1D array from 2D array after calculations */ + private static double[] extractDoubleArray(double[,] matrix) + { + int idx = 0; + + if (matrix == null || matrix.Length < 1 || matrix.GetLength(1) < 1) + { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + + double[] vector = new double[matrix.Length * matrix.GetLength(1)]; + + for (int j = 0; j < matrix.Length; j++) + { + for (int i = 0; i < matrix.GetLength(1); i++) + { + vector[idx++] = matrix[j,i]; + } + } + return vector; + } + public static void CheckValues(double[] results) + { + for (int idx = 0; idx < results.Length; idx++) + { + if (Double.IsNaN(results[idx]) || Double.IsInfinity(results[idx])) + { + throw new EvaluationException(ErrorEval.NUM_ERROR); + } + } + } + /* converts 1D array to 2D array for calculations */ + private static double[,] fillDoubleArray(double[] vector, int rows, int cols) + { + int i = 0, j = 0; + + if (rows < 1 || cols < 1 || vector.Length < 1) + { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + + double[,] matrix = new double[rows, cols]; + + for (int idx = 0; idx < vector.Length; idx++) + { + if (j < matrix.Length) + { + if (i == matrix.GetLength(1)) + { + i = 0; + j++; + } + matrix[j, i++] = vector[idx]; + } + } + + return matrix; + } + public abstract class OneArrayArg : Fixed1ArgFunction + { + protected OneArrayArg() + { + //no fields to initialize + } + + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) + { + if (arg0 is AreaEval) + { + double[] result = null; + double[,] resultArray; + int width = 1, height = 1; + + try + { + double[] values = CollectValues(arg0); + double[,] array = fillDoubleArray(values, ((AreaEval)arg0).Height, ((AreaEval)arg0).Width); + resultArray = Evaluate(array); + width = resultArray.GetLength(1); + height = resultArray.Length; + result = extractDoubleArray(resultArray); + + CheckValues(result); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + + ValueEval[] vals = new ValueEval[result.Length]; + + for (int idx = 0; idx < result.Length; idx++) + { + vals[idx] = new NumberEval(result[idx]); + } + + if (result.Length == 1) + { + return vals[0]; + } + else + { + /* find a better solution */ + return new CacheAreaEval(((AreaEval)arg0).FirstRow, ((AreaEval)arg0).FirstColumn, + ((AreaEval)arg0).FirstRow + height - 1, + ((AreaEval)arg0).FirstColumn + width - 1, vals); + } + } + else + { + double[,] result = null; + try + { + double value = NumericFunction.SingleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); + double[,] temp = { { value } }; + result = Evaluate(temp); + NumericFunction.CheckValue(result[0, 0]); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + + return new NumberEval(result[0, 0]); + } + } + + protected abstract double[,] Evaluate(double[,] d1); + protected abstract double[] CollectValues(ValueEval arg); + } + public abstract class TwoArrayArg : Fixed2ArgFunction + { + protected TwoArrayArg() + { + //no fields to initialize + } + + + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) + { + double[] result; + int width = 1, height = 1; + + try + { + double[,] array0, array1, resultArray; + + if (arg0 is AreaEval) + { + try + { + double[] values = CollectValues(arg0); + array0 = fillDoubleArray(values, ((AreaEval)arg0).Height, ((AreaEval)arg0).Width); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + else + { + try + { + double value = NumericFunction.SingleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); + array0 = new double[,] { { value } }; + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + + if (arg1 is AreaEval) + { + try + { + double[] values = CollectValues(arg1); + array1 = fillDoubleArray(values, ((AreaEval)arg1).Height, ((AreaEval)arg1).Width); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + else + { + try + { + double value = NumericFunction.SingleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); + array1 = new double[,] { { value } }; + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + + resultArray = Evaluate(array0, array1); + width = resultArray.GetLength(1); + height = resultArray.Length; + result = extractDoubleArray(resultArray); + CheckValues(result); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + catch (ArgumentException e) + { + return ErrorEval.VALUE_INVALID; + } + + + ValueEval[] vals = new ValueEval[result.Length]; + + for (int idx = 0; idx < result.Length; idx++) + { + vals[idx] = new NumberEval(result[idx]); + } + + if (result.Length == 1) + return vals[0]; + else + { + return new CacheAreaEval(((AreaEval)arg0).FirstRow, ((AreaEval)arg0).FirstColumn, + ((AreaEval)arg0).FirstRow + height - 1, + ((AreaEval)arg0).FirstColumn + width - 1, vals); + } + + } + + protected abstract double[,] Evaluate(double[,] d1, double[,] d2); + protected abstract double[] CollectValues(ValueEval arg); + + } + + + public class MutableValueCollector : MultiOperandNumericFunction + { + public MutableValueCollector(bool isReferenceBoolCounted, bool isBlankCounted) : + base(isReferenceBoolCounted, isBlankCounted) + { + + } + public double[] collectValues(params ValueEval[] operands) + { + return GetNumberArray(operands); + } + protected internal override double Evaluate(double[] values) + { + throw new InvalidOperationException("should not be called"); + } + } + } +} diff --git a/main/SS/Formula/OperationEvaluationContext.cs b/main/SS/Formula/OperationEvaluationContext.cs index 8ae90a105..4fa6d1c01 100644 --- a/main/SS/Formula/OperationEvaluationContext.cs +++ b/main/SS/Formula/OperationEvaluationContext.cs @@ -395,6 +395,44 @@ public ValueEval GetArea3DEval(Area3DPxg aptg) return new LazyAreaEval(aptg.FirstRow, aptg.FirstColumn, aptg.LastRow, aptg.LastColumn, sre); } + public ValueEval GetAreaValueEval(int firstRowIndex, int firstColumnIndex, + int lastRowIndex, int lastColumnIndex, Object[,] tokens) + { + + ValueEval[] values = new ValueEval[tokens.Length * tokens.GetLength(1)]; + + int index = 0; + for (int jdx = 0; jdx < tokens.Length; jdx++) + { + for (int idx = 0; idx < tokens.GetLength(1); idx++) + { + values[index++] = convertObjectEval(tokens[jdx,idx]); + } + } + + return new CacheAreaEval(firstRowIndex, firstColumnIndex, lastRowIndex, + lastColumnIndex, values); + } + private ValueEval convertObjectEval(Object token) + { + if (token == null) + { + throw new ArgumentNullException("Array item cannot be null"); + } + if (token is String) { + return new StringEval((String)token); + } + if (token is Double) { + return new NumberEval(((Double)token)); + } + if (token is Boolean) { + return BoolEval.ValueOf((Boolean)token); + } + if (token is Constant.ErrorConstant) { + return ErrorEval.ValueOf(((Constant.ErrorConstant)token).ErrorCode); + } + throw new ArgumentException("Unexpected constant class (" + token.GetType().Name + ")"); + } public ValueEval GetNameXEval(NameXPtg nameXPtg) { ExternalSheet externSheet = _workbook.GetExternalSheet(nameXPtg.SheetRefIndex); diff --git a/main/SS/Formula/PTG/ArrayPtg.cs b/main/SS/Formula/PTG/ArrayPtg.cs index a6c59f009..d3feaadaf 100644 --- a/main/SS/Formula/PTG/ArrayPtg.cs +++ b/main/SS/Formula/PTG/ArrayPtg.cs @@ -95,19 +95,18 @@ public ArrayPtg(Object[][] values2d) _reserved1Short = 0; _reserved2Byte = 0; } - public Object[][] GetTokenArrayValues() + public Object[,] GetTokenArrayValues() { if (_arrayValues == null) { throw new InvalidOperationException("array values not read yet"); } - Object[][] result = new Object[_nRows][]; + Object[,] result = new Object[_nRows,_nColumns]; for (int r = 0; r < _nRows; r++) { - result[r] = new object[_nColumns]; for (int c = 0; c < _nColumns; c++) { - result[r][c] = _arrayValues[GetValueIndex(c, r)]; + result[r,c] = _arrayValues[GetValueIndex(c, r)]; } } return result; diff --git a/main/SS/Formula/WorkbookEvaluator.cs b/main/SS/Formula/WorkbookEvaluator.cs index 07bf3da1e..8b5e441c9 100644 --- a/main/SS/Formula/WorkbookEvaluator.cs +++ b/main/SS/Formula/WorkbookEvaluator.cs @@ -707,7 +707,15 @@ public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) { throw new InvalidOperationException("evaluation stack not empty"); } - ValueEval result = DereferenceResult(value, ec.RowIndex, ec.ColumnIndex); + ValueEval result; + if (ec.IsSingleValue) + { + result = DereferenceResult(value, ec); + } + else + { + result = value; + } if (dbgEvaluationOutputIndent > 0) { EVAL_LOG.Log(POILogger.INFO, dbgIndentStr + "finshed eval of " @@ -774,6 +782,42 @@ public static ValueEval DereferenceResult(ValueEval evaluationResult, int srcRow return value; } /** + * Dereferences a single value from any AreaEval or RefEval evaluation + * result. If the supplied evaluationResult is just a plain value, it is + * returned as-is. + * + * @return a {@link NumberEval}, {@link StringEval}, {@link BoolEval}, or + * {@link ErrorEval}. Never null. {@link BlankEval} is + * converted to {@link NumberEval#ZERO} + */ + public static ValueEval DereferenceResult(ValueEval evaluationResult, OperationEvaluationContext ec) + { + ValueEval value; + + if (ec == null) + { + throw new ArgumentNullException("OperationEvaluationContext ec is null"); + } + if (ec.GetWorkbook() == null) + { + throw new ArgumentNullException("OperationEvaluationContext ec.getWorkbook() is null"); + } + + IEvaluationSheet evalSheet = ec.GetWorkbook().GetSheet(ec.SheetIndex); + IEvaluationCell evalCell = evalSheet.GetCell(ec.RowIndex, ec.ColumnIndex); + + if (evalCell.IsPartOfArrayFormulaGroup && evaluationResult is AreaEval) + { + value = OperandResolver.GetElementFromArray((AreaEval)evaluationResult, evalCell); + } + else + { + value = DereferenceResult(evaluationResult, ec.RowIndex, ec.ColumnIndex); + } + + return value; + } + /** * returns an appropriate Eval impl instance for the Ptg. The Ptg must be * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg, * StringPtg, BoolPtg
special Note: OperationPtg subtypes cannot be @@ -855,6 +899,10 @@ private ValueEval GetEvalForPtg(Ptg ptg, OperationEvaluationContext ec) AreaPtg aptg = (AreaPtg)ptg; return ec.GetAreaEval(aptg.FirstRow, aptg.FirstColumn, aptg.LastRow, aptg.LastColumn); } + if (ptg is ArrayPtg) { + ArrayPtg aptg = (ArrayPtg)ptg; + return ec.GetAreaValueEval(0, 0, aptg.RowCount - 1, aptg.ColumnCount - 1, aptg.GetTokenArrayValues()); + } if (ptg is UnknownPtg) { diff --git a/testcases/main/HSSF/Model/TestFormulaParser.cs b/testcases/main/HSSF/Model/TestFormulaParser.cs index 69b655a8f..0ec96af3a 100644 --- a/testcases/main/HSSF/Model/TestFormulaParser.cs +++ b/testcases/main/HSSF/Model/TestFormulaParser.cs @@ -1239,9 +1239,9 @@ public void TestParseArray() Assert.AreEqual("{1,2,2,#REF!;FALSE,3,3,2}", ptgs[0].ToFormulaString()); ArrayPtg aptg = (ArrayPtg)ptgs[0]; - Object[][] values = aptg.GetTokenArrayValues(); - Assert.AreEqual(ErrorConstant.ValueOf(FormulaError.REF.Code), values[0][3]); - Assert.AreEqual(false, values[1][0]); + Object[,] values = aptg.GetTokenArrayValues(); + Assert.AreEqual(ErrorConstant.ValueOf(FormulaError.REF.Code), values[0,3]); + Assert.AreEqual(false, values[1,0]); } [Test] public void TestParseStringElementInArray() @@ -1249,7 +1249,7 @@ public void TestParseStringElementInArray() Ptg[] ptgs; ptgs = ParseFormula("MAX({\"5\"},3)"); ConfirmTokenClasses(ptgs, typeof(ArrayPtg), typeof(IntPtg), typeof(FuncVarPtg)); - object element = ((ArrayPtg)ptgs[0]).GetTokenArrayValues()[0][0]; + object element = ((ArrayPtg)ptgs[0]).GetTokenArrayValues()[0,0]; if (element is UnicodeString) { // this would cause ClassCastException below @@ -1290,14 +1290,14 @@ public void TestParseArrayNegativeElement() throw e; } ConfirmTokenClasses(ptgs, typeof(ArrayPtg)); - Object element = ((ArrayPtg)ptgs[0]).GetTokenArrayValues()[0][0]; + Object element = ((ArrayPtg)ptgs[0]).GetTokenArrayValues()[0,0]; Assert.AreEqual(-42.0, (Double)element, 0.0); // Should be able to handle whitespace between unary minus and digits (Excel // accepts this formula after presenting the user with a Confirmation dialog). ptgs = ParseFormula("{- 5}"); - element = ((ArrayPtg)ptgs[0]).GetTokenArrayValues()[0][0]; + element = ((ArrayPtg)ptgs[0]).GetTokenArrayValues()[0,0]; Assert.AreEqual(-5.0, (Double)element, 0.0); } [Test] diff --git a/testcases/main/HSSF/Record/TestNameRecord.cs b/testcases/main/HSSF/Record/TestNameRecord.cs index 1e64d0d4f..332350090 100644 --- a/testcases/main/HSSF/Record/TestNameRecord.cs +++ b/testcases/main/HSSF/Record/TestNameRecord.cs @@ -698,9 +698,9 @@ private void assert_bug50244(NameRecord nr) ArrayPtg arr = (ArrayPtg)ptg[0]; Assert.AreEqual(696, arr.RowCount); Assert.AreEqual(1, arr.ColumnCount); - Object[][] vals = arr.GetTokenArrayValues(); - Assert.AreEqual("1.T20.001", vals[0][0]); - Assert.AreEqual("1.T20.010", vals[vals.Length - 1][0]); + Object[,] vals = arr.GetTokenArrayValues(); + Assert.AreEqual("1.T20.001", vals[0,0]); + Assert.AreEqual("1.T20.010", vals[vals.Length - 1,0]); } [Test] public void TestBug57923() diff --git a/testcases/main/SS/Formula/PTG/TestArrayPtg.cs b/testcases/main/SS/Formula/PTG/TestArrayPtg.cs index c34a410ed..2361da8d9 100644 --- a/testcases/main/SS/Formula/PTG/TestArrayPtg.cs +++ b/testcases/main/SS/Formula/PTG/TestArrayPtg.cs @@ -64,15 +64,15 @@ public void TestReadWriteTokenValueBytes() ArrayPtg ptg = Create(ENCODED_PTG_DATA, ENCODED_CONSTANT_DATA); Assert.AreEqual(3, ptg.ColumnCount); Assert.AreEqual(2, ptg.RowCount); - Object[][] values = ptg.GetTokenArrayValues(); + Object[,] values = ptg.GetTokenArrayValues(); Assert.AreEqual(2, values.Length); - Assert.AreEqual(true, values[0][0]); - Assert.AreEqual("ABCD", values[0][1]); - Assert.AreEqual(0d, values[1][0]); - Assert.AreEqual(false, values[1][1]); - Assert.AreEqual("FG", values[1][2]); + Assert.AreEqual(true, values[0,0]); + Assert.AreEqual("ABCD", values[0,1]); + Assert.AreEqual(0d, values[1,0]); + Assert.AreEqual(false, values[1,1]); + Assert.AreEqual("FG", values[1,2]); byte[] outBuf = new byte[ENCODED_CONSTANT_DATA.Length]; ptg.WriteTokenValueBytes(new LittleEndianByteArrayOutputStream(outBuf, 0)); From e72111cbacad4cd0a18344689bec9a1f46a3e89d Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 2 May 2022 21:18:12 +0800 Subject: [PATCH 125/159] Add excel test files --- .../main/SS/Formula/TestWorkbookEvaluator.cs | 5 ++++- .../spreadsheet/MatrixFormulaEvalTestData.xls | Bin 0 -> 44544 bytes .../spreadsheet/MatrixFormulaEvalTestData.xlsx | Bin 0 -> 14875 bytes 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 testcases/test-data/spreadsheet/MatrixFormulaEvalTestData.xls create mode 100644 testcases/test-data/spreadsheet/MatrixFormulaEvalTestData.xlsx diff --git a/testcases/main/SS/Formula/TestWorkbookEvaluator.cs b/testcases/main/SS/Formula/TestWorkbookEvaluator.cs index 2cd41aa1d..3314ea837 100644 --- a/testcases/main/SS/Formula/TestWorkbookEvaluator.cs +++ b/testcases/main/SS/Formula/TestWorkbookEvaluator.cs @@ -41,7 +41,10 @@ public class TestWorkbookEvaluator private static ValueEval EvaluateFormula(Ptg[] ptgs) { - OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + HSSFWorkbook wb = new HSSFWorkbook(); + wb.CreateSheet().CreateRow(0).CreateCell(0); + IEvaluationWorkbook ewb = HSSFEvaluationWorkbook.Create(wb); + OperationEvaluationContext ec = new OperationEvaluationContext(null, ewb, 0, 0, 0, null); return new WorkbookEvaluator(null, null, null).EvaluateFormula(ec, ptgs); } diff --git a/testcases/test-data/spreadsheet/MatrixFormulaEvalTestData.xls b/testcases/test-data/spreadsheet/MatrixFormulaEvalTestData.xls new file mode 100644 index 0000000000000000000000000000000000000000..b163ba48af646f89725bbf3c6db6da0c616e85d8 GIT binary patch literal 44544 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 literal 0 HcmV?d00001 diff --git a/testcases/test-data/spreadsheet/MatrixFormulaEvalTestData.xlsx b/testcases/test-data/spreadsheet/MatrixFormulaEvalTestData.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8145eceeb56085933b146718f0f467c8fa2d62fd GIT binary patch literal 14875 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 literal 0 HcmV?d00001 From 2370b3b988b8f598e2b587c3fb18a9be4d5ff4e4 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 7 May 2022 12:25:59 +0800 Subject: [PATCH 126/159] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 705a435af..e83506cff 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,12 @@ NPOI [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square&logo=Apache)](License.md) [![traffic](https://api.segment.io/v1/pixel/track?data=ewogICJ3cml0ZUtleSI6ICJBV2NjaWd1UkhKODBuNkJ4WlI4cHRaRzBINzY0RmJObCIsCiAgInVzZXJJZCI6ICJ0b255cXVzIiwKICAiZXZlbnQiOiAiTlBPSSBIb21lcGFnZSIKfQ== )](#) +
+ GitHub contributors +
This project is the .NET version of POI Java project. With NPOI, you can read/write Office 2003/2007 files very easily.
From 05f346aefc0950da16e70977a574963dcb0bec90 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 8 May 2022 03:21:28 +0800 Subject: [PATCH 127/159] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e83506cff..d003380b9 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ NPOI从未加入过中国NCC开源组织,他们在欺骗公众!Dotnetcore.np [Stop using Dotnetcore/NPOI nuget package. It’s obsolete!](https://tonyqus.medium.com/stop-using-dotnetcore-npoi-nuget-package-its-too-obsolete-6d0aeedb3319) -[The real history of Dotnetcore.NPOI](https://tonyqus.medium.com/the-real-history-of-dotnetcore-npoi-999bb5e140c7) +[The real history of Dotnetcore.NPOI](https://tonyqus.medium.com/the-real-history-of-dotnetcore-npoi-999bb5e140c7) [中文版](https://zhuanlan.zhihu.com/p/506975972) Advantage of NPOI ================= From 8252f99e11133dadbbd30016ff0de1aa08aafeb5 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Thu, 12 May 2022 05:08:31 +0800 Subject: [PATCH 128/159] fix compilation issue caused by PR --- OpenXmlFormats/Drawing/Chart/Chart.cs | 6 +++--- OpenXmlFormats/Drawing/SpreadsheetDrawing.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/OpenXmlFormats/Drawing/Chart/Chart.cs b/OpenXmlFormats/Drawing/Chart/Chart.cs index 0224fb62a..d25909499 100644 --- a/OpenXmlFormats/Drawing/Chart/Chart.cs +++ b/OpenXmlFormats/Drawing/Chart/Chart.cs @@ -75,7 +75,7 @@ public static CT_ChartSpace Parse(XmlNode node, XmlNamespaceManager namespaceMan else if (childNode.LocalName == "roundedCorners") ctObj.roundedCorners = CT_Boolean.Parse(childNode, namespaceManager); else if (childNode.LocalName == "AlternateContent") - ctObj.alternateContent = Vml.Spreadsheet.CT_AlternateContent.Parse(childNode, namespaceManager); + ctObj.alternateContent = Vml.CT_AlternateContent.Parse(childNode, namespaceManager); else if (childNode.LocalName == "style") ctObj.style = CT_Style.Parse(childNode, namespaceManager); else if (childNode.LocalName == "clrMapOvr") @@ -201,8 +201,8 @@ public CT_Boolean roundedCorners } } - Vml.Spreadsheet.CT_AlternateContent alternateContentField = null; - public Vml.Spreadsheet.CT_AlternateContent alternateContent + Vml.CT_AlternateContent alternateContentField = null; + public Vml.CT_AlternateContent alternateContent { get { diff --git a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs index 128ee196b..2ff68226c 100644 --- a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs +++ b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs @@ -1807,9 +1807,9 @@ public CT_Marker to set { toField = value; } } - private Vml.Spreadsheet.CT_AlternateContent alternateContentField = null; + private Vml.CT_AlternateContent alternateContentField = null; - public Vml.Spreadsheet.CT_AlternateContent alternateContent + public Vml.CT_AlternateContent alternateContent { get { @@ -1926,7 +1926,7 @@ internal static CT_TwoCellAnchor Parse(XmlNode node, XmlNamespaceManager namespa } else if (childNode.LocalName == "AlternateContent") { - twoCellAnchor.alternateContent = Vml.Spreadsheet.CT_AlternateContent.Parse(childNode, namespaceManager); + twoCellAnchor.alternateContent = Vml.CT_AlternateContent.Parse(childNode, namespaceManager); } else if (childNode.LocalName == "clientData") { From 29214935fbedfbf974a081b9c56e47593def4f37 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Fri, 13 May 2022 11:27:48 +0800 Subject: [PATCH 129/159] implement Mdeterm, Minverse, Transpose, MMult function --- main/NPOI.Core.csproj | 1 + main/SS/Formula/Eval/FunctionEval.cs | 8 +- .../TwoOperandNumericOperation.cs | 2 +- main/SS/Formula/Functions/Matrix/MMult.cs | 31 +++ .../Functions/{ => Matrix}/MatrixFunction.cs | 17 +- main/SS/Formula/Functions/Matrix/Mdeterm.cs | 35 ++++ main/SS/Formula/Functions/Matrix/Minverse.cs | 34 ++++ main/SS/Formula/Functions/Matrix/Transpose.cs | 25 +++ main/SS/Formula/OperationEvaluationContext.cs | 4 +- main/SS/Formula/WorkbookEvaluator.cs | 3 +- ...TestMatrixFormulasFromBinarySpreadsheet.cs | 187 ++++++++++++++++++ 11 files changed, 333 insertions(+), 14 deletions(-) create mode 100644 main/SS/Formula/Functions/Matrix/MMult.cs rename main/SS/Formula/Functions/{ => Matrix}/MatrixFunction.cs (93%) create mode 100644 main/SS/Formula/Functions/Matrix/Mdeterm.cs create mode 100644 main/SS/Formula/Functions/Matrix/Minverse.cs create mode 100644 main/SS/Formula/Functions/Matrix/Transpose.cs create mode 100644 testcases/main/HSSF/UserModel/TestMatrixFormulasFromBinarySpreadsheet.cs diff --git a/main/NPOI.Core.csproj b/main/NPOI.Core.csproj index 5dcd16cbb..c85589362 100644 --- a/main/NPOI.Core.csproj +++ b/main/NPOI.Core.csproj @@ -24,6 +24,7 @@ + diff --git a/main/SS/Formula/Eval/FunctionEval.cs b/main/SS/Formula/Eval/FunctionEval.cs index 76a544fec..62069d33b 100644 --- a/main/SS/Formula/Eval/FunctionEval.cs +++ b/main/SS/Formula/Eval/FunctionEval.cs @@ -179,7 +179,7 @@ private static Function[] ProduceFunctions() retval[80] = new NotImplementedFunction("RELREF"); // RELREF retval[81] = new NotImplementedFunction("ARGUMENT"); // ARGUMENT retval[82] = TextFunction.SEARCH; - retval[83] = new NotImplementedFunction("TRANSPOSE"); // TRANSPOSE + retval[83] = MatrixFunction.TRANSPOSE; // TRANSPOSE retval[84] = new NotImplementedFunction("ERROR"); // ERROR retval[85] = new NotImplementedFunction("STEP"); // STEP retval[86] = new NotImplementedFunction("TYPE"); // TYPE @@ -259,9 +259,9 @@ private static Function[] ProduceFunctions() retval[160] = new NotImplementedFunction("GetCHARTITEM"); // GetCHARTITEM retval[161] = new NotImplementedFunction("DIALOGBOX"); // DIALOGBOX retval[162] = TextFunction.CLEAN; // CLEAN - retval[163] = new NotImplementedFunction("MDETERM"); // MDETERM - retval[164] = new NotImplementedFunction("MINVERSE"); // MINVERSE - retval[165] = new NotImplementedFunction("MMULT"); // MMULT + retval[163] = MatrixFunction.MDETERM; // MDETERM + retval[164] = MatrixFunction.MINVERSE; // MINVERSE + retval[165] = MatrixFunction.MMULT; // MMULT retval[166] = new NotImplementedFunction("FILES"); // FILES retval[167] = new IPMT(); retval[168] = new PPMT(); diff --git a/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs b/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs index ad2e4c30b..d7b2a5881 100644 --- a/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs +++ b/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs @@ -83,7 +83,7 @@ protected override double[] CollectValues(ValueEval arg) protected override double[,] Evaluate(double[,] d1, double[,] d2) { int width = d1.GetLength(1) < d2.GetLength(1) ? d1.GetLength(1) : d2.GetLength(1); - int height = (d1.Length < d2.Length) ? d1.Length : d2.Length; + int height = (d1.GetLength(0) < d2.GetLength(0)) ? d1.GetLength(0) : d2.GetLength(0); double[,] result = new double[height, width]; diff --git a/main/SS/Formula/Functions/Matrix/MMult.cs b/main/SS/Formula/Functions/Matrix/MMult.cs new file mode 100644 index 000000000..c2e9fc335 --- /dev/null +++ b/main/SS/Formula/Functions/Matrix/MMult.cs @@ -0,0 +1,31 @@ +using MathNet.Numerics.LinearAlgebra; +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; +using static NPOI.SS.Formula.Functions.MatrixFunction; + +namespace NPOI.SS.Formula.Functions +{ + public class MMulti: TwoArrayArg + { + private MutableValueCollector instance = new MutableValueCollector(false, false); + protected override double[] CollectValues(ValueEval arg) + { + double[] values = instance.collectValues(arg); + + /* handle case where MMULT is operating on an array that is not completely filled*/ + if (arg is AreaEval && values.Length == 1) + throw new EvaluationException(ErrorEval.VALUE_INVALID); + + return values; + } + protected override double[,] Evaluate(double[,] d1, double[,] d2) + { + var first= Matrix.Build.DenseOfArray(d1); + var second = Matrix.Build.DenseOfArray(d2); + + return (first * second).ToArray(); + } + } +} diff --git a/main/SS/Formula/Functions/MatrixFunction.cs b/main/SS/Formula/Functions/Matrix/MatrixFunction.cs similarity index 93% rename from main/SS/Formula/Functions/MatrixFunction.cs rename to main/SS/Formula/Functions/Matrix/MatrixFunction.cs index b9fb36971..97812f736 100644 --- a/main/SS/Formula/Functions/MatrixFunction.cs +++ b/main/SS/Formula/Functions/Matrix/MatrixFunction.cs @@ -7,19 +7,24 @@ namespace NPOI.SS.Formula.Functions { public class MatrixFunction { + public static Function MDETERM = new Mdeterm(); + public static Function TRANSPOSE = new Transpose(); + public static Function MMULT = new MMulti(); + public static Function MINVERSE = new Minverse(); + /* retrieves 1D array from 2D array after calculations */ private static double[] extractDoubleArray(double[,] matrix) { int idx = 0; - if (matrix == null || matrix.Length < 1 || matrix.GetLength(1) < 1) + if (matrix == null || matrix.GetLength(0) < 1 || matrix.GetLength(1) < 1) { throw new EvaluationException(ErrorEval.VALUE_INVALID); } - double[] vector = new double[matrix.Length * matrix.GetLength(1)]; + double[] vector = new double[matrix.GetLength(0) * matrix.GetLength(1)]; - for (int j = 0; j < matrix.Length; j++) + for (int j = 0; j < matrix.GetLength(0); j++) { for (int i = 0; i < matrix.GetLength(1); i++) { @@ -52,7 +57,7 @@ public static void CheckValues(double[] results) for (int idx = 0; idx < vector.Length; idx++) { - if (j < matrix.Length) + if (j < matrix.GetLength(0)) { if (i == matrix.GetLength(1)) { @@ -86,7 +91,7 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva double[,] array = fillDoubleArray(values, ((AreaEval)arg0).Height, ((AreaEval)arg0).Width); resultArray = Evaluate(array); width = resultArray.GetLength(1); - height = resultArray.Length; + height = resultArray.GetLength(0); result = extractDoubleArray(resultArray); CheckValues(result); @@ -206,7 +211,7 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva resultArray = Evaluate(array0, array1); width = resultArray.GetLength(1); - height = resultArray.Length; + height = resultArray.GetLength(0); result = extractDoubleArray(resultArray); CheckValues(result); } diff --git a/main/SS/Formula/Functions/Matrix/Mdeterm.cs b/main/SS/Formula/Functions/Matrix/Mdeterm.cs new file mode 100644 index 000000000..4ef1d1b10 --- /dev/null +++ b/main/SS/Formula/Functions/Matrix/Mdeterm.cs @@ -0,0 +1,35 @@ +using MathNet.Numerics.LinearAlgebra; +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; +using static NPOI.SS.Formula.Functions.MatrixFunction; + +namespace NPOI.SS.Formula.Functions +{ + public class Mdeterm:OneArrayArg + { + private MutableValueCollector instance = new MutableValueCollector(false, false); + protected override double[] CollectValues(ValueEval arg) + { + double[] values = instance.collectValues(arg); + + /* handle case where MDETERM is operating on an array that that is not completely filled*/ + if (arg is AreaEval && values.Length == 1) + throw new EvaluationException(ErrorEval.VALUE_INVALID); + + return values; + } + protected override double[,] Evaluate(double[,] d1) + { + if (d1.GetLength(0) != d1.GetLength(1)) + { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + var temp = Matrix.Build.DenseOfArray(d1); + var result = new double[1, 1]; + result[0, 0] = temp.LU().Determinant; + return result; + } + } +} diff --git a/main/SS/Formula/Functions/Matrix/Minverse.cs b/main/SS/Formula/Functions/Matrix/Minverse.cs new file mode 100644 index 000000000..fa6763b94 --- /dev/null +++ b/main/SS/Formula/Functions/Matrix/Minverse.cs @@ -0,0 +1,34 @@ +using MathNet.Numerics.LinearAlgebra; +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; +using static NPOI.SS.Formula.Functions.MatrixFunction; + +namespace NPOI.SS.Formula.Functions +{ + public class Minverse : OneArrayArg + { + private MutableValueCollector instance = new MutableValueCollector(false, false); + + protected override double[] CollectValues(ValueEval arg) + { + double[] values = instance.collectValues(arg); + + /* handle case where MDETERM is operating on an array that that is not completely filled*/ + if (arg is AreaEval && values.Length == 1) + throw new EvaluationException(ErrorEval.VALUE_INVALID); + + return values; + } + protected override double[,] Evaluate(double[,] d1) + { + if (d1.GetLength(0) != d1.GetLength(1)) + { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + var temp = Matrix.Build.DenseOfArray(d1); + return temp.Inverse().ToArray(); + } + } +} diff --git a/main/SS/Formula/Functions/Matrix/Transpose.cs b/main/SS/Formula/Functions/Matrix/Transpose.cs new file mode 100644 index 000000000..7ffcff8cc --- /dev/null +++ b/main/SS/Formula/Functions/Matrix/Transpose.cs @@ -0,0 +1,25 @@ +using MathNet.Numerics.LinearAlgebra; +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; +using static NPOI.SS.Formula.Functions.MatrixFunction; + +namespace NPOI.SS.Formula.Functions +{ + public class Transpose : OneArrayArg + { + private MutableValueCollector instance = new MutableValueCollector(false, true); + + protected override double[] CollectValues(ValueEval arg) + { + return instance.collectValues(arg); + } + + protected override double[,] Evaluate(double[,] d1) + { + var temp = Matrix.Build.DenseOfArray(d1); + return temp.Transpose().ToArray(); + } + } +} diff --git a/main/SS/Formula/OperationEvaluationContext.cs b/main/SS/Formula/OperationEvaluationContext.cs index 4fa6d1c01..ba70177de 100644 --- a/main/SS/Formula/OperationEvaluationContext.cs +++ b/main/SS/Formula/OperationEvaluationContext.cs @@ -399,10 +399,10 @@ public ValueEval GetArea3DEval(Area3DPxg aptg) int lastRowIndex, int lastColumnIndex, Object[,] tokens) { - ValueEval[] values = new ValueEval[tokens.Length * tokens.GetLength(1)]; + ValueEval[] values = new ValueEval[tokens.GetLength(0) * tokens.GetLength(1)]; int index = 0; - for (int jdx = 0; jdx < tokens.Length; jdx++) + for (int jdx = 0; jdx < tokens.GetLength(0); jdx++) { for (int idx = 0; idx < tokens.GetLength(1); idx++) { diff --git a/main/SS/Formula/WorkbookEvaluator.cs b/main/SS/Formula/WorkbookEvaluator.cs index 8b5e441c9..6f5f727a2 100644 --- a/main/SS/Formula/WorkbookEvaluator.cs +++ b/main/SS/Formula/WorkbookEvaluator.cs @@ -305,7 +305,8 @@ private ValueEval Evaluate(String formula, CellReference target, CellRangeAddres AdjustRegionRelativeReference(ptgs, target, region); - OperationEvaluationContext ec = new OperationEvaluationContext(this, Workbook, sheetIndex, target.Row, target.Col, new EvaluationTracker(_cache),formulaType.GetAttributes().Get().IsSingleValue); + OperationEvaluationContext ec = new OperationEvaluationContext(this, Workbook, sheetIndex, target.Row, target.Col, new EvaluationTracker(_cache), + formulaType.GetAttributes().Get().IsSingleValue); return EvaluateNameFormula(ptgs, ec); } protected bool AdjustRegionRelativeReference(Ptg[] ptgs, CellReference target, CellRangeAddressBase region) diff --git a/testcases/main/HSSF/UserModel/TestMatrixFormulasFromBinarySpreadsheet.cs b/testcases/main/HSSF/UserModel/TestMatrixFormulasFromBinarySpreadsheet.cs new file mode 100644 index 000000000..3081a048d --- /dev/null +++ b/testcases/main/HSSF/UserModel/TestMatrixFormulasFromBinarySpreadsheet.cs @@ -0,0 +1,187 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.Formula.Eval; +using NPOI.SS.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.HSSF.UserModel +{ + [TestFixture] + public class TestMatrixFormulasFromBinarySpreadsheet + { + private static HSSFWorkbook workbook; + private static ISheet sheet; + private static IFormulaEvaluator evaluator; + + private static class Navigator + { + /** + * Name of the test spreadsheet (found in the standard test data folder) + */ + public const String FILENAME = "MatrixFormulaEvalTestData.xls"; + /** + * Row (zero-based) in the spreadsheet where operations start + */ + public const int START_OPERATORS_ROW_INDEX = 1; + /** + * Column (zero-based) in the spreadsheet where operations start + */ + public const int START_OPERATORS_COL_INDEX = 0; + /** + * Column (zero-based) in the spreadsheet where evaluations start + */ + public const int START_RESULT_COL_INDEX = 7; + /** + * Column separation in the spreadsheet between evaluations and expected results + */ + public const int COL_OFF_EXPECTED_RESULT = 3; + /** + * Row separation in the spreadsheet between operations + */ + public const int ROW_OFF_NEXT_OP = 4; + /** + * Used to indicate when there are no more operations left + */ + public const String END_OF_TESTS = ""; + + } + + + public static List data() + { + // Function "Text" uses custom-formats which are locale specific + // can't set the locale on a per-testrun execution, as some settings have been + // already set, when we would try to change the locale by then + + workbook = HSSFTestDataSamples.OpenSampleWorkbook(Navigator.FILENAME); + sheet = workbook.GetSheetAt(0); + evaluator = new HSSFFormulaEvaluator(workbook); + + List data = new List(); + + processFunctionGroup(data, Navigator.START_OPERATORS_ROW_INDEX, null); + + return data; + } + /** + * @param startRowIndex row index in the spreadsheet where the first function/operator is found + * @param testFocusFunctionName name of a single function/operator to test alone. + * Typically pass null to test all functions + */ + private static void processFunctionGroup(List data, int startRowIndex, String testFocusFunctionName) + { + for (int rowIndex = startRowIndex; true; rowIndex += Navigator.ROW_OFF_NEXT_OP) + { + IRow r = sheet.GetRow(rowIndex); + String targetFunctionName = getTargetFunctionName(r); + Assert.IsNotNull("Test spreadsheet cell empty on row (" + + (rowIndex) + "). Expected function name or '" + + Navigator.END_OF_TESTS + "'", targetFunctionName); + if (targetFunctionName.Equals(Navigator.END_OF_TESTS)) + { + // found end of functions list + break; + } + if (testFocusFunctionName == null || targetFunctionName.Equals(testFocusFunctionName, StringComparison.InvariantCultureIgnoreCase)) + { + data.Add(new Object[] { targetFunctionName, rowIndex }); + } + } + } + [TestCaseSource(nameof(data))] + public void processFunctionRow(String targetFunctionName, int formulasRowIdx) + { + int endColNum = Navigator.START_RESULT_COL_INDEX + Navigator.COL_OFF_EXPECTED_RESULT; + + for (int rowNum = formulasRowIdx; rowNum < formulasRowIdx + Navigator.ROW_OFF_NEXT_OP - 1; rowNum++) + { + for (int colNum = Navigator.START_RESULT_COL_INDEX; colNum < endColNum; colNum++) + { + IRow r = sheet.GetRow(rowNum); + + /* mainly to escape row failures on MDETERM which only returns a scalar */ + if (r == null) + { + continue; + } + + ICell c = sheet.GetRow(rowNum).GetCell(colNum); + + if (c == null || c.CellType != CellType.Formula) + { + continue; + } + + CellValue actValue = evaluator.Evaluate(c); + ICell expValue = sheet.GetRow(rowNum).GetCell(colNum + Navigator.COL_OFF_EXPECTED_RESULT); + + String msg = String.Format("Function '{0}': Formula: {1} @ {2}:{3}" + , targetFunctionName, c.CellFormula, rowNum, colNum); + + Assert.IsNotNull(expValue, msg + " - Bad setup data expected value is null"); + Assert.IsNotNull(actValue, msg + " - actual value was null"); + + CellType cellType = expValue.CellType; + switch (cellType) + { + case CellType.Blank: + Assert.AreEqual(CellType.Blank, actValue.CellType, msg); + break; + case CellType.Boolean: + Assert.AreEqual(CellType.Boolean, actValue.CellType, msg); + Assert.AreEqual(expValue.BooleanCellValue, actValue.BooleanValue, msg); + break; + case CellType.Error: + Assert.AreEqual(CellType.Error, actValue.CellType, msg); + Assert.AreEqual(ErrorEval.GetText(expValue.ErrorCellValue), ErrorEval.GetText(actValue.ErrorValue), msg); + break; + case CellType.Formula: // will never be used, since we will call method after formula evaluation + Assert.Fail("Cannot expect formula as result of formula evaluation: " + msg); + break; + case CellType.Numeric: + Assert.AreEqual(CellType.Numeric, actValue.CellType, msg); + Assert.AreEqual(expValue.NumericCellValue, actValue.NumberValue, msg); + break; + case CellType.String: + Assert.AreEqual(CellType.String, actValue.CellType, msg); + Assert.AreEqual(expValue.RichStringCellValue.String, actValue.StringValue, msg); + break; + default: + Assert.Fail("Unexpected cell type: " + cellType); + break; + } + } + } + } + /** + * @return null if cell is missing, empty or Blank + */ + private static String getTargetFunctionName(IRow r) + { + if (r == null) + { + Assert.Warn("Warning - given null row, can't figure out function name"); + return null; + } + ICell cell = r.GetCell(Navigator.START_OPERATORS_COL_INDEX); + //Assert.Warn(Navigator.START_OPERATORS_COL_INDEX.ToString()); + if (cell == null) + { + Assert.Warn("Warning - Row " + r.RowNum + " has no cell " + Navigator.START_OPERATORS_COL_INDEX + ", can't figure out function name"); + return null; + } + if (cell.CellType == CellType.Blank) + { + return null; + } + if (cell.CellType == CellType.String) + { + return cell.RichStringCellValue.String; + } + throw new AssertionException("Bad cell type for 'function name' column: (" + + cell.CellType + ") row (" + (r.RowNum + 1) + ")"); + } + } +} \ No newline at end of file From 902413fa7461194c54d5f64c20feea6c822d5471 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Fri, 13 May 2022 13:31:00 +0800 Subject: [PATCH 130/159] Update TestXMatchFunction.cs --- testcases/main/SS/Formula/Atp/TestXMatchFunction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs b/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs index 156091880..71214fcff 100644 --- a/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs +++ b/testcases/main/SS/Formula/Atp/TestXMatchFunction.cs @@ -23,7 +23,7 @@ public void TestMicrosoftExample0() [Test] public void TestMicrosoftExample1() { - HSSFWorkbook wb = initNumWorkbook("Gra?"); + HSSFWorkbook wb = initNumWorkbook("Gra"); HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); ICell cell = wb.GetSheetAt(0).GetRow(2).CreateCell(5); Util.Utils.AssertDouble(fe, cell, "XMATCH(E3,C3:C7,1)", 2); From cf3e3adb0607a923fa54777fc85d41a95176bad4 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 14 May 2022 06:52:46 +0800 Subject: [PATCH 131/159] POI Bug 60029 Fix problem with Days360 method for the month of february --- main/SS/Formula/Functions/Days360.cs | 42 ++++++++++++----- .../main/SS/Formula/Functions/TestDays360.cs | 47 ++++++++++++++----- 2 files changed, 64 insertions(+), 25 deletions(-) diff --git a/main/SS/Formula/Functions/Days360.cs b/main/SS/Formula/Functions/Days360.cs index 793b55eb3..bf104a314 100644 --- a/main/SS/Formula/Functions/Days360.cs +++ b/main/SS/Formula/Functions/Days360.cs @@ -49,43 +49,39 @@ public class Days360 : Var2or3ArgFunction public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { - double result; try { double d0 = NumericFunction.SingleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); double d1 = NumericFunction.SingleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); - result = Evaluate(d0, d1, false); + return new NumberEval(Evaluate(d0, d1, false)); } catch (EvaluationException e) { return e.GetErrorEval(); } - return new NumberEval(result); } public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2) { - double result; try { double d0 = NumericFunction.SingleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); double d1 = NumericFunction.SingleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); ValueEval ve = OperandResolver.GetSingleValue(arg2, srcRowIndex, srcColumnIndex); bool? method = OperandResolver.CoerceValueToBoolean(ve, false); - result = Evaluate(d0, d1, method == null ? false : method.Value); + return new NumberEval(Evaluate(d0, d1, method != null && (bool)method)); } catch (EvaluationException e) { return e.GetErrorEval(); } - return new NumberEval(result); } private double Evaluate(double d0, double d1, bool method) { DateTime realStart = GetDate(d0); DateTime realEnd = GetDate(d1); int[] startingDate = GetStartingDate(realStart, method); - int[] endingDate = GetEndingDate(realEnd, realStart, method); + int[] endingDate = GetEndingDate(realEnd, startingDate, method); return (endingDate[0] * 360 + endingDate[1] * 30 + endingDate[2]) - @@ -97,11 +93,12 @@ private DateTime GetDate(double date) } private int[] GetStartingDate(DateTime realStart, bool method) { - DateTime d = realStart; - int dd = Math.Min(30, d.Day); - - if (method == false && IsLastDayOfMonth(d)) dd = 30; - return new int[] { d.Year, d.Month, dd }; + int yyyy = realStart.Year; + int mm = realStart.Month; + int dd = Math.Min(30, realStart.Day); + + if (!method&& IsLastDayOfMonth(realStart)) dd = 30; + return new int[] { yyyy, mm, dd }; } private static int[] GetEndingDate(DateTime realEnd, DateTime realStart, bool method) @@ -129,6 +126,27 @@ private static int[] GetEndingDate(DateTime realEnd, DateTime realStart, bool me return new int[] { yyyy, mm, dd }; } + private static int[] GetEndingDate(DateTime realEnd, int[] startingDate, bool method) + { + int yyyy = realEnd.Year; + int mm = realEnd.Month; + int dd = Math.Min(30, realEnd.Day); + + if (!method && realEnd.Day == 31) + { + if (startingDate[2] < 30) + { + yyyy = realEnd.Year; + mm = realEnd.Month+1; + dd = 1; + } + else + { + dd = 30; + } + } + return new int[] { yyyy, mm, dd }; + } private DateTime GetEndingDateAccordingToStartingDate(double date, DateTime startingDate, bool method) { DateTime endingDate = DateUtil.GetJavaDate(date, false); diff --git a/testcases/main/SS/Formula/Functions/TestDays360.cs b/testcases/main/SS/Formula/Functions/TestDays360.cs index 35cb48fa7..55f3548e7 100644 --- a/testcases/main/SS/Formula/Functions/TestDays360.cs +++ b/testcases/main/SS/Formula/Functions/TestDays360.cs @@ -76,12 +76,6 @@ public void TestBasic() // longer time spans Confirm(562, 2008, 8, 11, 2010, 3, 3); Confirm(916, 2007, 2, 23, 2009, 9, 9); - } - - private static void Confirm(int expResult, int y1, int m1, int d1, int y2, int m2, int d2) - { - Confirm(expResult, MakeDate(y1, m1, d1), MakeDate(y2, m2, d2), false); - Confirm(-expResult, MakeDate(y2, m2, d2), MakeDate(y1, m1, d1), false); // other tests Confirm(1, MakeDate(1993, 2, 28), MakeDate(1993, 3, 1), false); @@ -89,6 +83,17 @@ private static void Confirm(int expResult, int y1, int m1, int d1, int y2, int m Confirm(-2, MakeDate(1993, 2, 28), MakeDate(1993, 2, 28), false); Confirm(3, MakeDate(1993, 2, 28), MakeDate(1993, 3, 1), true); Confirm(2, MakeDate(1996, 2, 29), MakeDate(1996, 3, 1), true); + + // from https://support.office.com/en-us/article/DAYS360-function-B9A509FD-49EF-407E-94DF-0CBDA5718C2A + Confirm(1, MakeDate(2011, 1, 30), MakeDate(2011, 2, 1), false); + Confirm(360, MakeDate(2011, 1, 1), MakeDate(2011, 12, 31), false); + Confirm(30, MakeDate(2011, 1, 1), MakeDate(2011, 2, 1), false); + } + + private static void Confirm(int expResult, int y1, int m1, int d1, int y2, int m2, int d2) + { + Confirm(expResult, MakeDate(y1, m1, d1), MakeDate(y2, m2, d2), false); + Confirm(-expResult, MakeDate(y2, m2, d2), MakeDate(y1, m1, d1), false); } /** * The method parameter only Makes a difference when the second parameter @@ -118,6 +123,24 @@ public void TestMonthBoundaries() // leap year ConfirmMonthBoundary(false, 2012, 2, -1, 1, 2, 3, 4); ConfirmMonthBoundary(true, 2012, 2, 0, 1, 2, 3, 4); + + // bug 60029 + DateTime start = MakeDate(2018, 2, 28); + DateTime end = MakeDate(2018, 3, 31); + Confirm(30, start, end, false); + + // examples from https://support.office.com/en-us/article/DAYS360-function-B9A509FD-49EF-407E-94DF-0CBDA5718C2A + start = MakeDate(2011, 1, 30); + end = MakeDate(2011, 2, 1); + Confirm(1, start, end, false); + + start = MakeDate(2011, 1, 1); + end = MakeDate(2011, 12, 31); + Confirm(360, start, end, false); + + start = MakeDate(2011, 1, 1); + end = MakeDate(2011, 2, 1); + Confirm(30, start, end, false); } @@ -151,13 +174,11 @@ private static void Confirm(int expResult, DateTime firstArg, DateTime secondArg { ve = invokeDays360(Convert(firstArg), Convert(secondArg)); } - if (ve is NumberEval) - { - Assert.IsTrue(ve is NumberEval, "wrong return type (" + ve.GetType().Name + ")"); - NumberEval numberEval = (NumberEval)ve; - String err = String.Format("days360({0},{1},{2}) wrong result", firstArg, secondArg, method); - Assert.AreEqual(expResult, numberEval.NumberValue, err); - } + Assert.IsTrue(ve is NumberEval, "wrong return type (" + ve.GetType().Name + ")"); + + NumberEval numberEval = (NumberEval)ve; + String err = String.Format("days360({0},{1},{2}) wrong result", firstArg, secondArg, method); + Assert.AreEqual(expResult, numberEval.NumberValue, err); } private static ValueEval invokeDays360(params ValueEval[] args) { From e3ba38b95b7dc3228ff732a17532ce30afc65158 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 14 May 2022 07:49:39 +0800 Subject: [PATCH 132/159] POI Bug 64393 Handle MissingArgEval in relational operators --- main/SS/Formula/Atp/IfError.cs | 10 +- .../RelationalOperationEval.cs | 150 +++++++++++++++--- .../TwoOperandNumericOperation.cs | 8 - .../Functions/MultiOperandNumericFunction.cs | 2 +- main/SS/Formula/WorkbookEvaluator.cs | 21 +-- .../SS/Formula/Eval/TestMissingArgEval.cs | 59 +++++++ 6 files changed, 202 insertions(+), 48 deletions(-) diff --git a/main/SS/Formula/Atp/IfError.cs b/main/SS/Formula/Atp/IfError.cs index 0c30ba7bb..140da53ad 100644 --- a/main/SS/Formula/Atp/IfError.cs +++ b/main/SS/Formula/Atp/IfError.cs @@ -57,15 +57,7 @@ private static ValueEval EvaluateInternal(ValueEval arg, ValueEval iferror, int arg = WorkbookEvaluator.DereferenceResult(arg, srcCellRow, srcCellCol); if (arg is ErrorEval) { - //if the 2nd argument is missing, use an empty string as default - if (iferror is MissingArgEval) - { - return new StringEval(string.Empty); - } - else - { - return iferror; - } + return iferror; } else { diff --git a/main/SS/Formula/Eval/RelationalOperationEval/RelationalOperationEval.cs b/main/SS/Formula/Eval/RelationalOperationEval/RelationalOperationEval.cs index c70b75487..5fc3a3d38 100644 --- a/main/SS/Formula/Eval/RelationalOperationEval/RelationalOperationEval.cs +++ b/main/SS/Formula/Eval/RelationalOperationEval/RelationalOperationEval.cs @@ -35,16 +35,16 @@ namespace NPOI.SS.Formula.Eval * @author Amol S. Deshmukh < amolweb at ya hoo Dot com > * */ - public abstract class RelationalOperationEval : Fixed2ArgFunction + public abstract class RelationalOperationEval : Fixed2ArgFunction, ArrayFunction { private static int DoCompare(ValueEval va, ValueEval vb) { - // special cases when one operand is blank - if (va == BlankEval.instance) + // special cases when one operand is blank or missing + if (va == BlankEval.instance|| va is MissingArgEval) { return CompareBlank(vb); } - if (vb == BlankEval.instance) + if (vb == BlankEval.instance || vb is MissingArgEval) { return -CompareBlank(va); } @@ -98,25 +98,30 @@ private static int DoCompare(ValueEval va, ValueEval vb) throw new ArgumentException("Bad operand types (" + va.GetType().Name + "), (" + vb.GetType().Name + ")"); } - private static int CompareBlank(ValueEval v) { - if (v == BlankEval.instance) { - return 0; - } - if (v is BoolEval) { - BoolEval boolEval = (BoolEval) v; - return boolEval.BooleanValue ? -1 : 0; - } - if (v is NumberEval) { - NumberEval ne = (NumberEval) v; + private static int CompareBlank(ValueEval v) + { + if (v == BlankEval.instance|| v is MissingArgEval) + { + return 0; + } + if (v is BoolEval) + { + BoolEval boolEval = (BoolEval)v; + return boolEval.BooleanValue ? -1 : 0; + } + if (v is NumberEval) + { + NumberEval ne = (NumberEval)v; //return ne.NumberValue.CompareTo(0.0); return NumberComparer.Compare(0.0, ne.NumberValue); - } - if (v is StringEval) { - StringEval se = (StringEval) v; - return se.StringValue.Length < 1 ? 0 : -1; - } - throw new ArgumentException("bad value class (" + v.GetType().Name + ")"); - } + } + if (v is StringEval) + { + StringEval se = (StringEval)v; + return se.StringValue.Length < 1 ? 0 : -1; + } + throw new ArgumentException("bad value class (" + v.GetType().Name + ")"); + } public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { @@ -136,7 +141,110 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva bool result = ConvertComparisonResult(cmpResult); return BoolEval.ValueOf(result); } + public ValueEval EvaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) + { + ValueEval arg0 = args[0]; + ValueEval arg1 = args[1]; + + int w1, w2, h1, h2; + int a1FirstCol = 0, a1FirstRow = 0; + if (arg0 is AreaEval) + { + AreaEval ae = (AreaEval)arg0; + w1 = ae.Width; + h1 = ae.Height; + a1FirstCol = ae.FirstColumn; + a1FirstRow = ae.FirstRow; + } + else if (arg0 is RefEval) + { + RefEval ref1 = (RefEval)arg0; + w1 = 1; + h1 = 1; + a1FirstCol = ref1.Column; + a1FirstRow = ref1.Row; + } + else + { + w1 = 1; + h1 = 1; + } + int a2FirstCol = 0, a2FirstRow = 0; + if (arg1 is AreaEval) + { + AreaEval ae = (AreaEval)arg1; + w2 = ae.Width; + h2 = ae.Height; + a2FirstCol = ae.FirstColumn; + a2FirstRow = ae.FirstRow; + } + else if (arg1 is RefEval) + { + RefEval ref1 = (RefEval)arg1; + w2 = 1; + h2 = 1; + a2FirstCol = ref1.Column; + a2FirstRow = ref1.Row; + } + else + { + w2 = 1; + h2 = 1; + } + + int width = Math.Max(w1, w2); + int height = Math.Max(h1, h2); + ValueEval[] vals = new ValueEval[height * width]; + + int idx = 0; + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + ValueEval vA; + try + { + vA = OperandResolver.GetSingleValue(arg0, a1FirstRow + i, a1FirstCol + j); + } + catch (EvaluationException e) + { + vA = e.GetErrorEval(); + } + ValueEval vB; + try + { + vB = OperandResolver.GetSingleValue(arg1, a2FirstRow + i, a2FirstCol + j); + } + catch (EvaluationException e) + { + vB = e.GetErrorEval(); + } + if (vA is ErrorEval) + { + vals[idx++] = vA; + } + else if (vB is ErrorEval) + { + vals[idx++] = vB; + } + else + { + int cmpResult = DoCompare(vA, vB); + bool result = ConvertComparisonResult(cmpResult); + vals[idx++] = BoolEval.ValueOf(result); + } + + } + } + + if (vals.Length == 1) + { + return vals[0]; + } + + return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals); + } public abstract bool ConvertComparisonResult(int cmpResult); diff --git a/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs b/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs index d7b2a5881..7f68604cc 100644 --- a/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs +++ b/main/SS/Formula/Eval/TwoOperandNumeric/TwoOperandNumericOperation.cs @@ -6,14 +6,6 @@ namespace NPOI.SS.Formula.Eval public abstract class TwoOperandNumericOperation : Fixed2ArgFunction, ArrayFunction { - //public int Type - //{ - // get - // { - // // TODO - remove - // throw new Exception("obsolete code should not be called"); - // } - //} protected double SingleOperandEvaluate(ValueEval arg, int srcCellRow, int srcCellCol) { ValueEval ve = OperandResolver.GetSingleValue(arg, srcCellRow, srcCellCol); diff --git a/main/SS/Formula/Functions/MultiOperandNumericFunction.cs b/main/SS/Formula/Functions/MultiOperandNumericFunction.cs index eadb75812..134f5a935 100644 --- a/main/SS/Formula/Functions/MultiOperandNumericFunction.cs +++ b/main/SS/Formula/Functions/MultiOperandNumericFunction.cs @@ -233,7 +233,7 @@ private void CollectValue(ValueEval ve, bool isViaReference, DoubleList temp) return; } throw new InvalidOperationException("Invalid ValueEval type passed for conversion: (" - + ve.GetType() + ")"); + + ve.GetType().Name + ")"); } /** * Returns a double array that contains values for the numeric cells diff --git a/main/SS/Formula/WorkbookEvaluator.cs b/main/SS/Formula/WorkbookEvaluator.cs index 6f5f727a2..60fbe6ce0 100644 --- a/main/SS/Formula/WorkbookEvaluator.cs +++ b/main/SS/Formula/WorkbookEvaluator.cs @@ -632,6 +632,7 @@ public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) { // this is an if statement without a false param (as opposed to MissingArgPtg as the false param) i++; + stack.Push(arg0); stack.Push(BoolEval.FALSE); } } @@ -649,13 +650,6 @@ public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) continue; } } - if (ptg is UnionPtg) - { - ValueEval v2 = stack.Pop(); - ValueEval v1 = stack.Pop(); - stack.Push(new RefListEval(v1, v2)); - continue; - } if (ptg is ControlPtg) { // skip Parentheses, Attr, etc @@ -666,8 +660,17 @@ public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) // can ignore, rest of Tokens for this expression are in OK RPN order continue; } - if (ptg is MemErrPtg) { continue; } - + if (ptg is MemErrPtg) + { + continue; + } + if (ptg is UnionPtg) + { + ValueEval v2 = stack.Pop(); + ValueEval v1 = stack.Pop(); + stack.Push(new RefListEval(v1, v2)); + continue; + } ValueEval opResult; if (ptg is OperationPtg) { diff --git a/testcases/main/SS/Formula/Eval/TestMissingArgEval.cs b/testcases/main/SS/Formula/Eval/TestMissingArgEval.cs index 11fd5a46c..93be65f8e 100644 --- a/testcases/main/SS/Formula/Eval/TestMissingArgEval.cs +++ b/testcases/main/SS/Formula/Eval/TestMissingArgEval.cs @@ -60,6 +60,65 @@ public void TestEvaluateMissingArgs() Assert.AreEqual("abc", fe.Evaluate(cell).StringValue); } [Test] + public void TestCompareMissingArgs() + { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ISheet sheet = wb.CreateSheet("Sheet1"); + ICell cell = sheet.CreateRow(0).CreateCell(0); + + cell.SetCellFormula("iferror(0/0,)<0"); + fe.ClearAllCachedResultValues(); + CellValue cv = fe.Evaluate(cell); + Assert.IsFalse(cv.BooleanValue); + + cell.SetCellFormula("iferror(0/0,)<=0"); + fe.ClearAllCachedResultValues(); + cv = fe.Evaluate(cell); + Assert.IsTrue(cv.BooleanValue); + + cell.SetCellFormula("iferror(0/0,)=0"); + fe.ClearAllCachedResultValues(); + cv = fe.Evaluate(cell); + Assert.IsTrue(cv.BooleanValue); + + cell.SetCellFormula("iferror(0/0,)>=0"); + fe.ClearAllCachedResultValues(); + cv = fe.Evaluate(cell); + Assert.IsTrue(cv.BooleanValue); + + cell.SetCellFormula("iferror(0/0,)>0"); + fe.ClearAllCachedResultValues(); + cv = fe.Evaluate(cell); + Assert.IsFalse(cv.BooleanValue); + + // invert above for code coverage + cell.SetCellFormula("0=iferror(0/0,)"); + fe.ClearAllCachedResultValues(); + cv = fe.Evaluate(cell); + Assert.IsTrue(cv.BooleanValue); + + cell.SetCellFormula("0>iferror(0/0,)"); + fe.ClearAllCachedResultValues(); + cv = fe.Evaluate(cell); + Assert.IsFalse(cv.BooleanValue); + } + [Test] public void TestCountFuncs() { HSSFWorkbook wb = new HSSFWorkbook(); From 4484efbdfa0ac22db103525879e9a38824036ebd Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 14 May 2022 08:01:06 +0800 Subject: [PATCH 133/159] POI Bug 64238 Make LOOKUP functions deal with empty last arg correctly --- main/SS/Formula/Functions/LookupUtils.cs | 9 +++++---- .../LookupFunctionsTestCaseData.xls | Bin 71168 -> 70144 bytes 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/main/SS/Formula/Functions/LookupUtils.cs b/main/SS/Formula/Functions/LookupUtils.cs index eae004b67..1ec6fbcd8 100644 --- a/main/SS/Formula/Functions/LookupUtils.cs +++ b/main/SS/Formula/Functions/LookupUtils.cs @@ -389,12 +389,13 @@ public static AreaEval ResolveTableArrayArg(ValueEval eval) */ public static bool ResolveRangeLookupArg(ValueEval rangeLookupArg, int srcCellRow, int srcCellCol) { - if (rangeLookupArg == null) + ValueEval valEval = OperandResolver.GetSingleValue(rangeLookupArg, srcCellRow, srcCellCol); + if (valEval == MissingArgEval.instance) { - // range_lookup arg not provided - return true; // default Is TRUE + // Tricky: + // forth arg exists but is not supplied: "=VLOOKUP(A1,A2:A4,2,)" + return false; } - ValueEval valEval = OperandResolver.GetSingleValue(rangeLookupArg, srcCellRow, srcCellCol); if (valEval is BlankEval) { // Tricky: diff --git a/testcases/test-data/spreadsheet/LookupFunctionsTestCaseData.xls b/testcases/test-data/spreadsheet/LookupFunctionsTestCaseData.xls index 491ba4a5158c334d5514a4e0fa385457c9bfbcd1..f836ef29baad27f7a12a86375d69d939e093bac2 100644 GIT binary patch delta 19377 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 delta 19911 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 U*{N*n-!e+^j9^z3ZBAbQ4duMSQ2+n{ From cf01064ff1e0067827552ac7462395b6f85dfc8a Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 14 May 2022 08:34:20 +0800 Subject: [PATCH 134/159] POI Bug 65870 Add support for BESSELJ function --- main/SS/Formula/Atp/AnalysisToolPak.cs | 2 +- main/SS/Formula/Functions/BesselJ.cs | 54 ++++++++++++++ .../main/SS/Formula/Functions/TestBesselJ.cs | 72 +++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 main/SS/Formula/Functions/BesselJ.cs create mode 100644 testcases/main/SS/Formula/Functions/TestBesselJ.cs diff --git a/main/SS/Formula/Atp/AnalysisToolPak.cs b/main/SS/Formula/Atp/AnalysisToolPak.cs index 2e9c0d4ba..7f8ddcd01 100644 --- a/main/SS/Formula/Atp/AnalysisToolPak.cs +++ b/main/SS/Formula/Atp/AnalysisToolPak.cs @@ -78,7 +78,7 @@ public override FreeRefFunction FindFunction(String name) r(m, "AVERAGEIFS", AverageIfs.instance); r(m, "BAHTTEXT", null); r(m, "BESSELI", null); - r(m, "BESSELJ", null); + r(m, "BESSELJ", BesselJ.instance); r(m, "BESSELK", null); r(m, "BESSELY", null); r(m, "BIN2DEC", Bin2Dec.instance); diff --git a/main/SS/Formula/Functions/BesselJ.cs b/main/SS/Formula/Functions/BesselJ.cs new file mode 100644 index 000000000..e8aacb648 --- /dev/null +++ b/main/SS/Formula/Functions/BesselJ.cs @@ -0,0 +1,54 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class BesselJ:Fixed2ArgFunction, FreeRefFunction + { + public static FreeRefFunction instance = new BesselJ(); + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2) + { + try + { + double xval = EvaluateValue(arg1, srcRowIndex, srcColumnIndex); + if (double.IsNaN(xval)) + { + return ErrorEval.VALUE_INVALID; + } + Double orderDouble = EvaluateValue(arg2, srcRowIndex, srcColumnIndex); + if (double.IsNaN(orderDouble)) + { + return ErrorEval.VALUE_INVALID; + } + int order = (int)orderDouble; + if (order < 0) + { + return ErrorEval.NUM_ERROR; + } + + var result=MathNet.Numerics.SpecialFunctions.BesselJ(order, xval); + return new NumberEval(result); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length == 2) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0], args[1]); + } + return ErrorEval.VALUE_INVALID; + } + private static Double EvaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) + { + ValueEval veText = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + String strText1 = OperandResolver.CoerceValueToString(veText); + return OperandResolver.ParseDouble(strText1); + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestBesselJ.cs b/testcases/main/SS/Formula/Functions/TestBesselJ.cs new file mode 100644 index 000000000..0ffa03526 --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestBesselJ.cs @@ -0,0 +1,72 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.Formula; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestBesselJ + { + private static OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + + [Test] + public void TestInvalid() + { + confirmInvalidError("A1", "B2"); + } + + [Test] + public void TestNumError() + { + confirmNumError("22.5", "-40"); + } + + //https://support.microsoft.com/en-us/office/besselj-function-839cb181-48de-408b-9d80-bd02982d94f7 + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + IRow row = sheet.CreateRow(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = row.CreateCell(0); + //this tolerance is too high but commons-math3 and excel don't match up as closely as we'd like + double tolerance = 0.000001; + SS.Util.Utils.AssertDouble(fe, cell, "BESSELJ(1.9, 2)", 0.329925829, tolerance); + SS.Util.Utils.AssertDouble(fe, cell, "BESSELJ(1.9, 2.5)", 0.329925829, tolerance); + } + + private static ValueEval invokeValue(String number1, String number2) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1), new StringEval(number2) }; + return BesselJ.instance.Evaluate(args, ec); + } + + private static void confirmValue(String number1, String number2, double expected) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.00000000000001); + } + + private static void confirmInvalidError(String number1, String number2) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.VALUE_INVALID, result); + } + + private static void confirmNumError(String number1, String number2) + { + ValueEval result = invokeValue(number1, number2); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.NUM_ERROR, result); + } + } +} From 56e54420401457fe1a7c575397ced69f30553993 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 14 May 2022 09:17:13 +0800 Subject: [PATCH 135/159] POI Bug 64258 Provide implementation for TDIST function --- main/SS/Formula/Eval/FunctionEval.cs | 2 +- main/SS/Formula/Functions/TDist.cs | 84 +++++++++++++++++++ .../main/SS/Formula/Functions/TestTDist.cs | 84 +++++++++++++++++++ 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 main/SS/Formula/Functions/TDist.cs create mode 100644 testcases/main/SS/Formula/Functions/TestTDist.cs diff --git a/main/SS/Formula/Eval/FunctionEval.cs b/main/SS/Formula/Eval/FunctionEval.cs index 62069d33b..167caae2a 100644 --- a/main/SS/Formula/Eval/FunctionEval.cs +++ b/main/SS/Formula/Eval/FunctionEval.cs @@ -383,7 +383,7 @@ private static Function[] ProduceFunctions() retval[298] = new Odd(); // ODD retval[299] = new NotImplementedFunction("PERMUT"); // PERMUT retval[300] = NumericFunction.POISSON; // POISSON - retval[301] = new NotImplementedFunction("TDIST"); // TDIST + retval[301] = TDist.instance; // TDIST retval[302] = new NotImplementedFunction("WEIBULL"); // WEIBULL retval[303] = new Sumxmy2(); // SUMXMY2 retval[304] = new Sumx2my2(); // SUMX2MY2 diff --git a/main/SS/Formula/Functions/TDist.cs b/main/SS/Formula/Functions/TDist.cs new file mode 100644 index 000000000..e4ab1b1af --- /dev/null +++ b/main/SS/Formula/Functions/TDist.cs @@ -0,0 +1,84 @@ +using MathNet.Numerics.Distributions; +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class TDist : Fixed3ArgFunction, FreeRefFunction + { + public static TDist instance = new TDist(); + static double tdistOneTail(double x, int degreesOfFreedom) + { + return 1 - StudentT.CDF(0, 1, degreesOfFreedom, x); + } + + static double tdistTwoTails(double x, int degreesOfFreedom) + { + return 2 * tdistOneTail(x, degreesOfFreedom); + } + + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2, ValueEval arg3) + { + try + { + Double number1 = evaluateValue(arg1, srcRowIndex, srcColumnIndex); + if (double.IsNaN(number1)) + { + return ErrorEval.VALUE_INVALID; + } + else if (number1 < 0) + { + return ErrorEval.NUM_ERROR; + } + Double number2 = evaluateValue(arg2, srcRowIndex, srcColumnIndex); + if (double.IsNaN(number2)) + { + return ErrorEval.VALUE_INVALID; + } + int degreesOfFreedom = (int)number2; + if (degreesOfFreedom < 1) + { + return ErrorEval.NUM_ERROR; + } + Double number3 = evaluateValue(arg3, srcRowIndex, srcColumnIndex); + if (double.IsNaN(number3)) + { + return ErrorEval.VALUE_INVALID; + } + int tails = (int)number3; + if (!(tails == 1 || tails == 2)) + { + return ErrorEval.NUM_ERROR; + } + + if (tails == 2) + { + return new NumberEval(tdistTwoTails(number1, degreesOfFreedom)); + } + + return new NumberEval(tdistOneTail(number1, degreesOfFreedom)); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length == 3) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0], args[1], args[2]); + } + + return ErrorEval.VALUE_INVALID; + } + private static Double evaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) + { + ValueEval veText = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + String strText1 = OperandResolver.CoerceValueToString(veText); + return OperandResolver.ParseDouble(strText1); + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestTDist.cs b/testcases/main/SS/Formula/Functions/TestTDist.cs new file mode 100644 index 000000000..b9c4cf589 --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestTDist.cs @@ -0,0 +1,84 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.Formula; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + public class TestTDist + { + private static OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + [Test] + public void TestBasic() + { + confirmValue("5.968191467", "8", "1", 0.00016754180265310392); + confirmValue("5.968191467", "8", "2", 0.00033508360530620784); + confirmValue("5.968191467", "8.2", "2.2", 0.00033508360530620784); + confirmValue("5.968191467", "8.9", "2.9", 0.00033508360530620784); + } + + [Test] + public void TestInvalid() + { + confirmInvalidError("A1", "B2", "C2"); + confirmInvalidError("5.968191467", "8", "C2"); + confirmInvalidError("5.968191467", "B2", "2"); + confirmInvalidError("A1", "8", "2"); + } + + [Test] + public void TestNumError() + { + confirmNumError("5.968191467", "8", "0"); + confirmNumError("-5.968191467", "8", "2"); + } + + //https://support.microsoft.com/en-us/office/tdist-function-630a7695-4021-4853-9468-4a1f9dcdd192 + [Test] + public void testMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + SS.Util.Utils.AddRow(sheet, 0, "Data", "Description"); + SS.Util.Utils.AddRow(sheet, 1, 1.959999998, "Value at which to evaluate the distribution"); + SS.Util.Utils.AddRow(sheet, 2, 60, "Degrees of freedom"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(0).CreateCell(100); + SS.Util.Utils.AssertDouble(fe, cell, "TDIST(A2,A3,2)", 0.054644930, 0.01); + SS.Util.Utils.AssertDouble(fe, cell, "TDIST(A2,A3,1)", 0.027322465, 0.01); + + } + private static ValueEval invokeValue(String number1, String number2, String number3) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1), new StringEval(number2), new StringEval(number3) }; + return TDist.instance.Evaluate(args, ec); + } + + private static void confirmValue(String number1, String number2, String number3, double expected) + { + ValueEval result = invokeValue(number1, number2, number3); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.0); + } + + private static void confirmInvalidError(String number1, String number2, String number3) + { + ValueEval result = invokeValue(number1, number2, number3); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.VALUE_INVALID, result); + } + + private static void confirmNumError(String number1, String number2, String number3) + { + ValueEval result = invokeValue(number1, number2, number3); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.NUM_ERROR, result); + } + } + +} From 98a09722a286fed7796821de365991c01685af88 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sat, 14 May 2022 11:17:34 +0800 Subject: [PATCH 136/159] POI Bug 65850 Add support for Normal Distribution functions including NORMDIST/NORM.DIST NORMSDIST/NORM.S.DIST NORMINV/NORM.INV NORMSINV/NORM.S.INV STANDARDIZE --- main/SS/Formula/Atp/AnalysisToolPak.cs | 4 + main/SS/Formula/Eval/FunctionEval.cs | 10 +-- main/SS/Formula/Functions/NormDist.cs | 70 ++++++++++++++++ main/SS/Formula/Functions/NormInv.cs | 68 +++++++++++++++ main/SS/Formula/Functions/NormSDist.cs | 48 +++++++++++ main/SS/Formula/Functions/NormSInv.cs | 48 +++++++++++ main/SS/Formula/Functions/Standardize.cs | 59 +++++++++++++ .../main/SS/Formula/Functions/TestNormDist.cs | 82 ++++++++++++++++++ .../main/SS/Formula/Functions/TestNormInv.cs | 83 +++++++++++++++++++ .../SS/Formula/Functions/TestNormSDist.cs | 59 +++++++++++++ .../main/SS/Formula/Functions/TestNormSInv.cs | 73 ++++++++++++++++ .../SS/Formula/Functions/TestStandardize.cs | 76 +++++++++++++++++ 12 files changed, 675 insertions(+), 5 deletions(-) create mode 100644 main/SS/Formula/Functions/NormDist.cs create mode 100644 main/SS/Formula/Functions/NormInv.cs create mode 100644 main/SS/Formula/Functions/NormSDist.cs create mode 100644 main/SS/Formula/Functions/NormSInv.cs create mode 100644 main/SS/Formula/Functions/Standardize.cs create mode 100644 testcases/main/SS/Formula/Functions/TestNormDist.cs create mode 100644 testcases/main/SS/Formula/Functions/TestNormInv.cs create mode 100644 testcases/main/SS/Formula/Functions/TestNormSDist.cs create mode 100644 testcases/main/SS/Formula/Functions/TestNormSInv.cs create mode 100644 testcases/main/SS/Formula/Functions/TestStandardize.cs diff --git a/main/SS/Formula/Atp/AnalysisToolPak.cs b/main/SS/Formula/Atp/AnalysisToolPak.cs index 7f8ddcd01..449f646f3 100644 --- a/main/SS/Formula/Atp/AnalysisToolPak.cs +++ b/main/SS/Formula/Atp/AnalysisToolPak.cs @@ -154,6 +154,10 @@ public override FreeRefFunction FindFunction(String name) r(m, "MULTINOMIAL", null); r(m, "NETWORKDAYS", NetworkdaysFunction.instance); r(m, "NOMINAL", null); + r(m, "NORM.DIST", NormDist.instance); + r(m, "NORM.S.DIST", NormSDist.instance); + r(m, "NORM.INV", NormInv.instance); + r(m, "NORM.S.INV", NormSInv.instance); r(m, "NUMBERVALUE", NumberValueFunction.instance); r(m, "OCT2BIN", null); r(m, "OCT2DEC", Oct2Dec.instance); diff --git a/main/SS/Formula/Eval/FunctionEval.cs b/main/SS/Formula/Eval/FunctionEval.cs index 167caae2a..5564875f7 100644 --- a/main/SS/Formula/Eval/FunctionEval.cs +++ b/main/SS/Formula/Eval/FunctionEval.cs @@ -375,11 +375,11 @@ private static Function[] ProduceFunctions() retval[290] = new NotImplementedFunction("LOGNORMDIST"); // LOGNORMDIST retval[291] = new NotImplementedFunction("LOGINV"); // LOGINV retval[292] = new NotImplementedFunction("NEGBINOMDIST"); // NEGBINOMDIST - retval[293] = new NotImplementedFunction("NORMDIST"); // NORMDIST - retval[294] = new NotImplementedFunction("NORMSDIST"); // NORMSDIST - retval[295] = new NotImplementedFunction("NORMINV"); // NORMINV - retval[296] = new NotImplementedFunction("NORMSINV"); // NORMSINV - retval[297] = new NotImplementedFunction("STANDARDIZE"); // STANDARDIZE + retval[293] = NormDist.instance; // NORMDIST + retval[294] = NormSDist.instance; // NORMSDIST + retval[295] = NormInv.instance; // NORMINV + retval[296] = NormSInv.instance; // NORMSINV + retval[297] = Standardize.instance; // STANDARDIZE retval[298] = new Odd(); // ODD retval[299] = new NotImplementedFunction("PERMUT"); // PERMUT retval[300] = NumericFunction.POISSON; // POISSON diff --git a/main/SS/Formula/Functions/NormDist.cs b/main/SS/Formula/Functions/NormDist.cs new file mode 100644 index 000000000..11c8e8dd7 --- /dev/null +++ b/main/SS/Formula/Functions/NormDist.cs @@ -0,0 +1,70 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class NormDist : Fixed4ArgFunction, FreeRefFunction + { + public static NormDist instance = new NormDist(); + internal static double probability(double x, double mean, double stdev, bool cumulative) + { + var normalDistribution = new MathNet.Numerics.Distributions.Normal(mean, stdev); + return cumulative ? normalDistribution.CumulativeDistribution(x) : normalDistribution.Density(x); + } + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2, ValueEval arg3, ValueEval arg4) + { + try + { + Double xval = evaluateValue(arg1, srcRowIndex, srcColumnIndex); + if (double.IsNaN(xval)) + { + return ErrorEval.VALUE_INVALID; + } + Double mean = evaluateValue(arg2, srcRowIndex, srcColumnIndex); + if (double.IsNaN(mean)) + { + return ErrorEval.VALUE_INVALID; + } + Double stdev = evaluateValue(arg3, srcRowIndex, srcColumnIndex); + if (double.IsNaN(stdev)) + { + return ErrorEval.VALUE_INVALID; + } + else if (stdev <= 0) + { + return ErrorEval.NUM_ERROR; + } + var cumulative = OperandResolver.CoerceValueToBoolean(arg4, false); + if (cumulative==null) + { + return ErrorEval.VALUE_INVALID; + } + + return new NumberEval(probability( + xval, mean, stdev, (bool)cumulative)); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length == 4) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0], args[1], args[2], args[3]); + } + + return ErrorEval.VALUE_INVALID; + } + + private static Double evaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) + { + ValueEval veText = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + String strText1 = OperandResolver.CoerceValueToString(veText); + return OperandResolver.ParseDouble(strText1); + } + } +} \ No newline at end of file diff --git a/main/SS/Formula/Functions/NormInv.cs b/main/SS/Formula/Functions/NormInv.cs new file mode 100644 index 000000000..2a4b0775e --- /dev/null +++ b/main/SS/Formula/Functions/NormInv.cs @@ -0,0 +1,68 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class NormInv:Fixed3ArgFunction,FreeRefFunction + { + public static NormInv instance = new NormInv(); + internal static double inverse(double probability, double mean, double stdev) + { + var normalDistribution = new MathNet.Numerics.Distributions.Normal(mean, stdev); + return normalDistribution.InverseCumulativeDistribution(probability); + } + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2, ValueEval arg3) + { + try + { + Double probability = evaluateValue(arg1, srcRowIndex, srcColumnIndex); + if (double.IsNaN(probability)) + { + return ErrorEval.VALUE_INVALID; + } + else if (probability <= 0 || probability >= 1) + { + return ErrorEval.NUM_ERROR; + } + Double mean = evaluateValue(arg2, srcRowIndex, srcColumnIndex); + if (double.IsNaN(mean)) + { + return ErrorEval.VALUE_INVALID; + } + Double stdev = evaluateValue(arg3, srcRowIndex, srcColumnIndex); + if (double.IsNaN(stdev)) + { + return ErrorEval.VALUE_INVALID; + } + else if (stdev <= 0) + { + return ErrorEval.NUM_ERROR; + } + + return new NumberEval(inverse( + probability, mean, stdev)); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length == 3) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0], args[1], args[2]); + } + + return ErrorEval.VALUE_INVALID; + } + private static Double evaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) + { + ValueEval veText = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + String strText1 = OperandResolver.CoerceValueToString(veText); + return OperandResolver.ParseDouble(strText1); + } + } +} diff --git a/main/SS/Formula/Functions/NormSDist.cs b/main/SS/Formula/Functions/NormSDist.cs new file mode 100644 index 000000000..a803ddf9b --- /dev/null +++ b/main/SS/Formula/Functions/NormSDist.cs @@ -0,0 +1,48 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class NormSDist : Fixed1ArgFunction, FreeRefFunction + { + public static NormSDist instance = new NormSDist(); + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1) + { + try + { + Double xval = evaluateValue(arg1, srcRowIndex, srcColumnIndex); + if (double.IsNaN(xval)) + { + return ErrorEval.VALUE_INVALID; + } + else if (xval < 0) + { + return ErrorEval.NUM_ERROR; + } + + return new NumberEval(NormDist.probability(xval, 0, 1, true)); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length == 1) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0]); + } + + return ErrorEval.VALUE_INVALID; + } + private static Double evaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) + { + ValueEval veText = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + String strText1 = OperandResolver.CoerceValueToString(veText); + return OperandResolver.ParseDouble(strText1); + } + } +} diff --git a/main/SS/Formula/Functions/NormSInv.cs b/main/SS/Formula/Functions/NormSInv.cs new file mode 100644 index 000000000..1b627bf1d --- /dev/null +++ b/main/SS/Formula/Functions/NormSInv.cs @@ -0,0 +1,48 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class NormSInv:Fixed1ArgFunction, FreeRefFunction + { + public static NormSInv instance = new NormSInv(); + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1) + { + try + { + Double probability = evaluateValue(arg1, srcRowIndex, srcColumnIndex); + if (double.IsNaN(probability)) + { + return ErrorEval.VALUE_INVALID; + } + else if (probability <= 0 || probability >= 1) + { + return ErrorEval.NUM_ERROR; + } + + return new NumberEval(NormInv.inverse(probability, 0, 1)); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length == 1) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0]); + } + + return ErrorEval.VALUE_INVALID; + } + private static Double evaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) + { + ValueEval veText = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + String strText1 = OperandResolver.CoerceValueToString(veText); + return OperandResolver.ParseDouble(strText1); + } + } +} diff --git a/main/SS/Formula/Functions/Standardize.cs b/main/SS/Formula/Functions/Standardize.cs new file mode 100644 index 000000000..201db2613 --- /dev/null +++ b/main/SS/Formula/Functions/Standardize.cs @@ -0,0 +1,59 @@ +using NPOI.SS.Formula.Eval; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NPOI.SS.Formula.Functions +{ + public class Standardize:Fixed3ArgFunction,FreeRefFunction + { + public static Standardize instance = new Standardize(); + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2, ValueEval arg3) + { + try + { + Double xval = evaluateValue(arg1, srcRowIndex, srcColumnIndex); + if (double.IsNaN(xval)) + { + return ErrorEval.VALUE_INVALID; + } + Double mean = evaluateValue(arg2, srcRowIndex, srcColumnIndex); + if (double.IsNaN(mean)) + { + return ErrorEval.VALUE_INVALID; + } + Double stdev = evaluateValue(arg3, srcRowIndex, srcColumnIndex); + if (double.IsNaN(stdev)) + { + return ErrorEval.VALUE_INVALID; + } + else if (stdev <= 0) + { + return ErrorEval.NUM_ERROR; + } + + var result = (xval - mean)/stdev; + return new NumberEval(result); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + } + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length == 3) + { + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0], args[1], args[2]); + } + + return ErrorEval.VALUE_INVALID; + } + private static Double evaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) + { + ValueEval veText = OperandResolver.GetSingleValue(arg, srcRowIndex, srcColumnIndex); + String strText1 = OperandResolver.CoerceValueToString(veText); + return OperandResolver.ParseDouble(strText1); + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestNormDist.cs b/testcases/main/SS/Formula/Functions/TestNormDist.cs new file mode 100644 index 000000000..63db499cf --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestNormDist.cs @@ -0,0 +1,82 @@ +using NPOI.HSSF.UserModel; +using NPOI.SS.Formula; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestNormDist + { + private static OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + [Test] + public void TestBasic() + { + confirmValue("42", "40", "1.5", true, 0.908788780274132); + confirmValue("42", "40", "1.5", false, 0.109340049783996); + } + [Test] + public void TestInvalid() + { + confirmInvalidError("A1", "B2", "C2", false); + } + + [Test] + public void TestNumError() + { + confirmNumError("42", "40", "0", false); + confirmNumError("42", "40", "0", true); + confirmNumError("42", "40", "-0.1", false); + confirmNumError("42", "40", "-0.1", true); + } + + //https://support.microsoft.com/en-us/office/normdist-function-126db625-c53e-4591-9a22-c9ff422d6d58 + //https://support.microsoft.com/en-us/office/norm-dist-function-edb1cc14-a21c-4e53-839d-8082074c9f8d + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + SS.Util.Utils.AddRow(sheet, 0, "Data", "Description"); + SS.Util.Utils.AddRow(sheet, 1, 42, "Value for which you want the distribution"); + SS.Util.Utils.AddRow(sheet, 2, 40, "Arithmetic mean of the distribution"); + SS.Util.Utils.AddRow(sheet, 3, 1.5, "Standard deviation of the distribution"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(0).CreateCell(100); + SS.Util.Utils.AssertDouble(fe, cell, "NORMDIST(A2,A3,A4,TRUE)", 0.908788780274132, 0.00000000000001); + SS.Util.Utils.AssertDouble(fe, cell, "NORM.DIST(A2,A3,A4,TRUE)", 0.908788780274132, 0.00000000000001); + } + + private static ValueEval invokeValue(String number1, String number2, String number3, bool cumulative) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1), new StringEval(number2), new StringEval(number3), BoolEval.ValueOf(cumulative) }; + return NormDist.instance.Evaluate(args, ec); + } + + private static void confirmValue(String number1, String number2, String number3, bool cumulative, double expected) + { + ValueEval result = invokeValue(number1, number2, number3, cumulative); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.00000000000001); + } + + private static void confirmInvalidError(String number1, String number2, String number3, bool cumulative) + { + ValueEval result = invokeValue(number1, number2, number3, cumulative); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.VALUE_INVALID, result); + } + + private static void confirmNumError(String number1, String number2, String number3, bool cumulative) + { + ValueEval result = invokeValue(number1, number2, number3, cumulative); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.NUM_ERROR, result); + } + } +} \ No newline at end of file diff --git a/testcases/main/SS/Formula/Functions/TestNormInv.cs b/testcases/main/SS/Formula/Functions/TestNormInv.cs new file mode 100644 index 000000000..17e2d82cf --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestNormInv.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Text; +using NPOI.HSSF.UserModel; +using NPOI.SS.Formula; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using NUnit.Framework; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestNormInv + { + private static OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + + [Test] + public void TestBasic() + { + confirmValue("0.908789", "40", "1.5", 42.000002); + } + + [Test] + public void TestInvalid() + { + confirmInvalidError("A1", "B2", "C2"); + } + + [Test] + public void TestNumError() + { + confirmNumError("0.5", "40", "0"); + confirmNumError("0.5", "40", "-0.1"); + confirmNumError("-0.5", "40", "0.1"); + confirmNumError("0", "40", "0.1"); + confirmNumError("1", "40", "0.1"); + confirmNumError("1.5", "40", "0.1"); + } + //https://support.microsoft.com/en-us/office/norminv-function-87981ab8-2de0-4cb0-b1aa-e21d4cb879b8 + //https://support.microsoft.com/en-us/office/norm-inv-function-54b30935-fee7-493c-bedb-2278a9db7e13 + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + SS.Util.Utils.AddRow(sheet, 0, "Data", "Description"); + SS.Util.Utils.AddRow(sheet, 1, 0.908789, "Probability corresponding to the normal distribution"); + SS.Util.Utils.AddRow(sheet, 2, 40, "Arithmetic mean of the distribution"); + SS.Util.Utils.AddRow(sheet, 3, 1.5, "Standard deviation of the distribution"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(0).CreateCell(100); + SS.Util.Utils.AssertDouble(fe, cell, "NORMINV(A2,A3,A4)", 42.000002, 0.000001); + SS.Util.Utils.AssertDouble(fe, cell, "NORM.INV(A2,A3,A4)", 42.000002, 0.000001); + } + private static ValueEval invokeValue(String number1, String number2, String number3) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1), new StringEval(number2), new StringEval(number3) }; + return NormInv.instance.Evaluate(args, ec); + } + + private static void confirmValue(String number1, String number2, String number3, double expected) + { + ValueEval result = invokeValue(number1, number2, number3); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.0000001); + } + + private static void confirmInvalidError(String number1, String number2, String number3) + { + ValueEval result = invokeValue(number1, number2, number3); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.VALUE_INVALID, result); + } + + private static void confirmNumError(String number1, String number2, String number3) + { + ValueEval result = invokeValue(number1, number2, number3); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.NUM_ERROR, result); + } + } +} \ No newline at end of file diff --git a/testcases/main/SS/Formula/Functions/TestNormSDist.cs b/testcases/main/SS/Formula/Functions/TestNormSDist.cs new file mode 100644 index 000000000..79500d3f9 --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestNormSDist.cs @@ -0,0 +1,59 @@ +using NPOI.SS.Formula; +using System; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.HSSF.UserModel; +using NPOI.SS.UserModel; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestNormSDist + { + private static OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + [Test] + public void TestBasic() + { + confirmValue("1.333333", 0.908788726); + } + + [Test] + public void TestInvalid() + { + confirmInvalidError("A1"); + } + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + IRow row = sheet.CreateRow(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = row.CreateCell(0); + SS.Util.Utils.AssertDouble(fe, cell, "NORMSDIST(1.333333)", 0.908788726, 0.000001); + SS.Util.Utils.AssertDouble(fe, cell, "NORM.S.DIST(1.333333)", 0.908788726, 0.000001); + } + private static ValueEval invokeValue(String number1) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1) }; + return NormSDist.instance.Evaluate(args, ec); + } + + private static void confirmValue(String number1, double expected) + { + ValueEval result = invokeValue(number1); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.0000001); + } + + private static void confirmInvalidError(String number1) + { + ValueEval result = invokeValue(number1); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.VALUE_INVALID, result); + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestNormSInv.cs b/testcases/main/SS/Formula/Functions/TestNormSInv.cs new file mode 100644 index 000000000..f5e3a1a3e --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestNormSInv.cs @@ -0,0 +1,73 @@ +using NPOI.SS.Formula; +using System; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.HSSF.UserModel; +using NPOI.SS.UserModel; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestNormSInv + { + private static OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + [Test] + public void TestBasic() + { + confirmValue("0.9088", 1.3334); + } + + [Test] + public void TestInvalid() + { + confirmInvalidError("A1"); + } + [Test] + public void TestNumError() + { + confirmNumError("0"); + confirmNumError("-0.5"); + confirmNumError("1"); + confirmNumError("1.5"); + } + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + IRow row = sheet.CreateRow(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = row.CreateCell(0); + SS.Util.Utils.AssertDouble(fe, cell, "NORMSINV(0.9088)", 1.3334, 0.00001); + SS.Util.Utils.AssertDouble(fe, cell, "NORM.S.INV(0.9088)", 1.3334, 0.00001); + } + private static ValueEval invokeValue(String number1) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1) }; + return NormSInv.instance.Evaluate(args, ec); + } + + private static void confirmValue(String number1, double expected) + { + ValueEval result = invokeValue(number1); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.00001); + } + + private static void confirmInvalidError(String number1) + { + ValueEval result = invokeValue(number1); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.VALUE_INVALID, result); + } + private static void confirmNumError(String number1) + { + ValueEval result = invokeValue(number1); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.NUM_ERROR, result); + } + } +} diff --git a/testcases/main/SS/Formula/Functions/TestStandardize.cs b/testcases/main/SS/Formula/Functions/TestStandardize.cs new file mode 100644 index 000000000..337acfbca --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestStandardize.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Text; +using NPOI.HSSF.UserModel; +using NPOI.SS.Formula; +using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using NUnit.Framework; + +namespace TestCases.SS.Formula.Functions +{ + [TestFixture] + public class TestStandardize + { + private static OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); + [Test] + public void TestBasic() + { + confirmValue("42", "40", "1.5", 1.33333333); + } + + [Test] + public void TestInvalid() + { + confirmInvalidError("A1", "B2", "C2"); + } + + [Test] + public void TestNumError() + { + confirmNumError("42", "40", "0"); + confirmNumError("42", "40", "-0.1"); + } + //https://support.microsoft.com/en-us/office/standardize-function-81d66554-2d54-40ec-ba83-6437108ee775 + [Test] + public void TestMicrosoftExample1() + { + HSSFWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet(); + SS.Util.Utils.AddRow(sheet, 0, "Data", "Description"); + SS.Util.Utils.AddRow(sheet, 1, 42, "Value to normalize"); + SS.Util.Utils.AddRow(sheet, 2, 40, "Arithmetic mean of the distribution"); + SS.Util.Utils.AddRow(sheet, 3, 1.5, "Standard deviation of the distribution"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + ICell cell = wb.GetSheetAt(0).GetRow(0).CreateCell(100); + SS.Util.Utils.AssertDouble(fe, cell, "STANDARDIZE(A2,A3,A4)", 1.33333333, 0.000001); + } + private static ValueEval invokeValue(String number1, String number2, String number3) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1), new StringEval(number2), new StringEval(number3) }; + return Standardize.instance.Evaluate(args, ec); + } + + private static void confirmValue(String number1, String number2, String number3, double expected) + { + ValueEval result = invokeValue(number1, number2, number3); + Assert.IsTrue(result is NumberEval); + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, 0.0000001); + } + + private static void confirmInvalidError(String number1, String number2, String number3) + { + ValueEval result = invokeValue(number1, number2, number3); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.VALUE_INVALID, result); + } + + private static void confirmNumError(String number1, String number2, String number3) + { + ValueEval result = invokeValue(number1, number2, number3); + Assert.IsTrue(result is ErrorEval); + Assert.AreEqual(ErrorEval.NUM_ERROR, result); + } + } +} \ No newline at end of file From cd551f1954a24b77e46798d46c255828b46a9fd4 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Sun, 15 May 2022 01:55:16 +0800 Subject: [PATCH 137/159] Update NPOI.csproj and NPOI.OOXML.csproj --- main/NPOI.csproj | 40 ++++++++++++++++++++++++++++++++++++++++ ooxml/NPOI.OOXML.csproj | 8 ++++++++ 2 files changed, 48 insertions(+) diff --git a/main/NPOI.csproj b/main/NPOI.csproj index 29d562d79..a67c865d5 100644 --- a/main/NPOI.csproj +++ b/main/NPOI.csproj @@ -282,21 +282,35 @@ + + + + + + + + + + + + + + @@ -311,8 +325,19 @@ + + + + + + + + + + + @@ -321,7 +346,9 @@ + + @@ -502,7 +529,14 @@ + + + + + + + @@ -1358,6 +1392,12 @@ + + 4.0.0 + + + 4.15.0 + 1.4.1 diff --git a/ooxml/NPOI.OOXML.csproj b/ooxml/NPOI.OOXML.csproj index 20eb93579..561a4a3bc 100644 --- a/ooxml/NPOI.OOXML.csproj +++ b/ooxml/NPOI.OOXML.csproj @@ -199,6 +199,7 @@ Code + @@ -211,6 +212,7 @@ + @@ -222,6 +224,7 @@ + @@ -234,6 +237,7 @@ + @@ -243,6 +247,7 @@ + @@ -279,6 +284,9 @@ + + + From 04cc20c889090cfd72d2297e20929821bebf1c87 Mon Sep 17 00:00:00 2001 From: Collin Alpert Date: Sun, 22 May 2022 20:46:27 +0200 Subject: [PATCH 138/159] Add DateOnly support. --- main/HSSF/UserModel/HSSFCell.cs | 14 ++++++++++++ main/NPOI.Core.csproj | 2 +- main/SS/UserModel/Cell.cs | 11 +++++++++ main/SS/UserModel/DateUtil.cs | 40 +++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/main/HSSF/UserModel/HSSFCell.cs b/main/HSSF/UserModel/HSSFCell.cs index 40f64eb1d..0e2832ec1 100644 --- a/main/HSSF/UserModel/HSSFCell.cs +++ b/main/HSSF/UserModel/HSSFCell.cs @@ -515,6 +515,20 @@ public void SetCellValue(DateTime value) SetCellValue(DateUtil.GetExcelDate(value, this.book.IsDate1904())); } +#if NET6_0_OR_GREATER + /// + /// Set a date value for the cell. Excel treats dates as numeric so you will need to format the cell as + /// a date. + /// + /// the date value to Set this cell to. For formulas we'll Set the + /// precalculated value, for numerics we'll Set its value. For other types we + /// will Change the cell to a numeric cell and Set its value. + public void SetCellValue(DateOnly value) + { + SetCellValue(DateUtil.GetExcelDate(value, this.book.IsDate1904())); + } +#endif + /// /// Set a string value for the cell. Please note that if you are using diff --git a/main/NPOI.Core.csproj b/main/NPOI.Core.csproj index c85589362..4b10b319b 100644 --- a/main/NPOI.Core.csproj +++ b/main/NPOI.Core.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net6.0 false NPOI NPOI diff --git a/main/SS/UserModel/Cell.cs b/main/SS/UserModel/Cell.cs index a17bc330f..b23d5b136 100644 --- a/main/SS/UserModel/Cell.cs +++ b/main/SS/UserModel/Cell.cs @@ -125,6 +125,17 @@ CellType CellType /// void SetCellValue(DateTime value); +#if NET6_0_OR_GREATER + /// + /// Converts the supplied date to its equivalent Excel numeric value and Sets that into the cell. + /// + /// the numeric value to set this cell to. For formulas we'll set the + /// precalculated value, for numerics we'll set its value. For other types we will change + /// the cell to a numerics cell and set its value. + /// + void SetCellValue(DateOnly value); +#endif + /// /// Set a rich string value for the cell. /// diff --git a/main/SS/UserModel/DateUtil.cs b/main/SS/UserModel/DateUtil.cs index a3d62b0f2..5cccbc62e 100644 --- a/main/SS/UserModel/DateUtil.cs +++ b/main/SS/UserModel/DateUtil.cs @@ -239,6 +239,46 @@ public static double GetExcelDate(DateTime date, bool use1904windowing) return value; } +#if NET6_0_OR_GREATER + /// + /// Given a Date, Converts it into a double representing its internal Excel representation, + /// which Is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds. + /// + /// The date. + /// Should 1900 or 1904 date windowing be used? + /// Excel representation of Date (-1 if error - test for error by Checking for less than 0.1) + public static double GetExcelDate(DateOnly date, bool use1904windowing) + { + if ((!use1904windowing && date.Year < 1900) //1900 date system must bigger than 1900 + || (use1904windowing && date.Year < 1904)) //1904 date system must bigger than 1904 + { + return BAD_DATE; + } + + DateOnly startdate; + if (use1904windowing) + { + startdate = new DateOnly(1904, 1, 1); + } + else + { + startdate = new DateOnly(1900, 1, 1); + } + + double value = (date.ToDateTime(TimeOnly.MinValue) - startdate.ToDateTime(TimeOnly.MinValue)).TotalDays + 1; + + if (!use1904windowing && value >= 60) + { + value++; + } + else if (use1904windowing) + { + value--; + } + return value; + } +#endif + /// /// Given an Excel date with using 1900 date windowing, and converts it to a java.util.Date. /// Excel Dates and Times are stored without any timezone From 53990124f462e305f9871880ac1d0cd57e17c697 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 23 May 2022 08:14:54 +0800 Subject: [PATCH 139/159] use MathNet.Numerics signed package instead --- OpenXmlFormats/NPOI.OpenXmlFormats.csproj | 2 +- main/NPOI.Core.csproj | 2 +- main/NPOI.csproj | 4 ++-- ooxml/NPOI.OOXML.csproj | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/OpenXmlFormats/NPOI.OpenXmlFormats.csproj b/OpenXmlFormats/NPOI.OpenXmlFormats.csproj index 78325f4ce..907ea16c7 100644 --- a/OpenXmlFormats/NPOI.OpenXmlFormats.csproj +++ b/OpenXmlFormats/NPOI.OpenXmlFormats.csproj @@ -72,7 +72,7 @@ ..\solution\Lib\.net45\ TRACE;HIDE_UNREACHABLE_CODE true - 1591 + 1591;1587;1570 pdbonly AnyCPU 7.3 diff --git a/main/NPOI.Core.csproj b/main/NPOI.Core.csproj index c85589362..9ac4b8a75 100644 --- a/main/NPOI.Core.csproj +++ b/main/NPOI.Core.csproj @@ -24,7 +24,7 @@ - + diff --git a/main/NPOI.csproj b/main/NPOI.csproj index a67c865d5..96fbe4569 100644 --- a/main/NPOI.csproj +++ b/main/NPOI.csproj @@ -73,7 +73,7 @@ TRACE;HIDE_UNREACHABLE_CODE ..\solution\Lib\.net45\NPOI.XML true - 1591 + 1591;1587;1570 pdbonly AnyCPU 7.3 @@ -1395,7 +1395,7 @@ 4.0.0 - + 4.15.0 diff --git a/ooxml/NPOI.OOXML.csproj b/ooxml/NPOI.OOXML.csproj index 561a4a3bc..0be31dc1b 100644 --- a/ooxml/NPOI.OOXML.csproj +++ b/ooxml/NPOI.OOXML.csproj @@ -73,7 +73,7 @@ TRACE;HIDE_UNREACHABLE_CODE ..\solution\Lib\.net45\NPOI.OOXML.XML true - 1591 + 1591;1587;1570 pdbonly AnyCPU 7.3 From 3f254de04d3a6c083797ff5764e711e66269bd78 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 23 May 2022 08:15:30 +0800 Subject: [PATCH 140/159] fix compile warnings --- OpenXmlFormats/Wordprocessing/FormField.cs | 3 --- main/SS/Formula/Atp/Switch.cs | 4 ++-- main/SS/Formula/Atp/XLookupFunction.cs | 4 ++-- main/SS/Formula/Functions/Matrix/MatrixFunction.cs | 2 +- main/SS/Formula/Functions/NumberValueFunction.cs | 2 +- main/SS/Formula/Functions/WeekNum.cs | 2 +- main/Util/InputStream.cs | 2 +- ooxml/POIFS/Crypt/Agile/AgileEncryptionHeader.cs | 2 +- ooxml/POIFS/Crypt/Dsig/OOXMLURIDereferencer.cs | 4 ++-- ooxml/POIFS/Crypt/Dsig/SignatureConfig.cs | 6 +++--- ooxml/POIXMLFactory.cs | 2 +- ooxml/XSSF/Model/StylesTable.cs | 2 +- ooxml/XSSF/UserModel/BaseXSSFEvaluationWorkbook.cs | 2 +- ooxml/XSSF/UserModel/XSSFWorkbook.cs | 2 +- ooxml/XWPF/Usermodel/XWPFSDTContentCell.cs | 1 - openxml4Net/Exceptions/InvalidFormatException.cs | 2 -- openxml4Net/Exceptions/OpenXML4NetException.cs | 3 --- openxml4Net/NPOI.OpenXml4Net.csproj | 2 +- openxml4Net/OPC/Internal/ContentType.cs | 1 - openxml4Net/OPC/PackagePart.cs | 2 +- openxml4Net/OPC/PackageRelationshipCollection.cs | 2 +- testcases/main/HSSF/UserModel/TestHSSFDateUtil.cs | 4 ++-- testcases/main/HSSF/UserModel/TestHSSFSheet.cs | 3 +-- testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs | 8 ++++---- testcases/main/POIFS/Crypt/TestCipherAlgorithm.cs | 6 +++--- testcases/main/SS/Format/TestCellFormatResult.cs | 2 +- testcases/main/SS/Formula/Functions/TestDec2Hex.cs | 2 +- testcases/main/SS/Formula/TestFormulaShifter.cs | 4 ++-- .../main/SS/UserModel/BaseTestBugzillaIssues.cs | 2 +- testcases/main/SS/UserModel/BaseTestCloneSheet.cs | 4 ++-- testcases/main/SS/UserModel/BaseTestSheet.cs | 2 +- .../SS/UserModel/BaseTestSheetUpdateArrayFormulas.cs | 2 +- testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs | 2 +- testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs | 4 ++-- testcases/ooxml/SS/TestWorkbookFactory.cs | 4 ++-- testcases/ooxml/SS/UserModel/TestFormulaParser.cs | 2 +- testcases/ooxml/XSSF/TestXSSFCloneSheet.cs | 2 +- .../XSSF/UserModel/Extensions/TestXSSFCellFill.cs | 2 +- testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs | 12 ++++++------ testcases/ooxml/XSSF/UserModel/TestXSSFDrawing.cs | 2 +- .../ooxml/XSSF/UserModel/TestXSSFRichTextString.cs | 2 +- .../ooxml/XSSF/UserModel/TestXSSFSheetShiftRows.cs | 2 +- .../Compliance/TestOPCComplianceCoreProperties.cs | 4 ++-- .../OPC/Compliance/TestOPCCompliancePackageModel.cs | 12 ++++++------ .../OPC/Compliance/TestOPCCompliancePartName.cs | 6 +++--- testcases/openxml4net/TestPackage.cs | 2 +- testcases/openxml4net/TestPackagingURIHelper.cs | 2 +- testcases/openxml4net/TestZipPackage.cs | 8 ++++---- 48 files changed, 74 insertions(+), 85 deletions(-) diff --git a/OpenXmlFormats/Wordprocessing/FormField.cs b/OpenXmlFormats/Wordprocessing/FormField.cs index 1377dddb1..e35d2d0a7 100644 --- a/OpenXmlFormats/Wordprocessing/FormField.cs +++ b/OpenXmlFormats/Wordprocessing/FormField.cs @@ -120,9 +120,6 @@ internal void Write(StreamWriter sw, string nodeName) [XmlRoot(Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", IsNullable = true)] public class CT_FldChar { - - private object itemField; - private ST_FldCharType fldCharTypeField; private ST_OnOff fldLockField; diff --git a/main/SS/Formula/Atp/Switch.cs b/main/SS/Formula/Atp/Switch.cs index 917c578ce..747a7a0ca 100644 --- a/main/SS/Formula/Atp/Switch.cs +++ b/main/SS/Formula/Atp/Switch.cs @@ -23,7 +23,7 @@ public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) { expression = OperandResolver.GetSingleValue(args[0], ec.RowIndex, ec.ColumnIndex); } - catch (Exception e) + catch (Exception) { return ErrorEval.NA; } @@ -51,7 +51,7 @@ public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) } } - catch (EvaluationException e) + catch (EvaluationException) { return ErrorEval.NA; } diff --git a/main/SS/Formula/Atp/XLookupFunction.cs b/main/SS/Formula/Atp/XLookupFunction.cs index 8b6d45965..182e5cd9c 100644 --- a/main/SS/Formula/Atp/XLookupFunction.cs +++ b/main/SS/Formula/Atp/XLookupFunction.cs @@ -73,7 +73,7 @@ private ValueEval _evaluate(ValueEval[] args, int srcRowIndex, int srcColumnInde { return e.GetErrorEval(); } - catch (Exception e) + catch (Exception ) { return ErrorEval.VALUE_INVALID; } @@ -91,7 +91,7 @@ private ValueEval _evaluate(ValueEval[] args, int srcRowIndex, int srcColumnInde { return e.GetErrorEval(); } - catch (Exception e) + catch (Exception) { return ErrorEval.VALUE_INVALID; } diff --git a/main/SS/Formula/Functions/Matrix/MatrixFunction.cs b/main/SS/Formula/Functions/Matrix/MatrixFunction.cs index 97812f736..23921c223 100644 --- a/main/SS/Formula/Functions/Matrix/MatrixFunction.cs +++ b/main/SS/Formula/Functions/Matrix/MatrixFunction.cs @@ -219,7 +219,7 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva { return e.GetErrorEval(); } - catch (ArgumentException e) + catch (ArgumentException ) { return ErrorEval.VALUE_INVALID; } diff --git a/main/SS/Formula/Functions/NumberValueFunction.cs b/main/SS/Formula/Functions/NumberValueFunction.cs index e2c0d34bf..736717919 100644 --- a/main/SS/Formula/Functions/NumberValueFunction.cs +++ b/main/SS/Formula/Functions/NumberValueFunction.cs @@ -89,7 +89,7 @@ public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) { return e.GetErrorEval(); } - catch (Exception anyex) + catch (Exception) { return ErrorEval.VALUE_INVALID; //If any of the arguments are not valid, NUMBERVALUE returns the #VALUE! error value. } diff --git a/main/SS/Formula/Functions/WeekNum.cs b/main/SS/Formula/Functions/WeekNum.cs index 70aef3370..a8467196a 100644 --- a/main/SS/Formula/Functions/WeekNum.cs +++ b/main/SS/Formula/Functions/WeekNum.cs @@ -67,7 +67,7 @@ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEva { serialNumCalendar = DateUtil.GetJavaDate(serialNum, false); } - catch (Exception e) + catch (Exception ) { return ErrorEval.NUM_ERROR; } diff --git a/main/Util/InputStream.cs b/main/Util/InputStream.cs index bf05ca68c..b0d2223cb 100644 --- a/main/Util/InputStream.cs +++ b/main/Util/InputStream.cs @@ -156,7 +156,7 @@ public override int Read(byte[] b, int off, int len) b[off + i] = (byte)c; } } - catch (IOException ee) + catch (IOException ) { } return i; diff --git a/ooxml/POIFS/Crypt/Agile/AgileEncryptionHeader.cs b/ooxml/POIFS/Crypt/Agile/AgileEncryptionHeader.cs index 81520f177..1c40de0a5 100644 --- a/ooxml/POIFS/Crypt/Agile/AgileEncryptionHeader.cs +++ b/ooxml/POIFS/Crypt/Agile/AgileEncryptionHeader.cs @@ -42,7 +42,7 @@ protected internal AgileEncryptionHeader(EncryptionDocument ed) throw new NullReferenceException("keyData not Set"); } } - catch (Exception e) + catch (Exception) { throw new EncryptedDocumentException("Unable to parse keyData"); } diff --git a/ooxml/POIFS/Crypt/Dsig/OOXMLURIDereferencer.cs b/ooxml/POIFS/Crypt/Dsig/OOXMLURIDereferencer.cs index 641af01e0..8b5791256 100644 --- a/ooxml/POIFS/Crypt/Dsig/OOXMLURIDereferencer.cs +++ b/ooxml/POIFS/Crypt/Dsig/OOXMLURIDereferencer.cs @@ -102,7 +102,7 @@ public IData dereference(IURIReference uriReference, SignedXml context) throw new NotImplementedException(); } } - catch (IOException e) + catch (IOException) { //throw new URIReferenceException("I/O error: " + e.Message, e); throw new NotImplementedException(); @@ -128,7 +128,7 @@ private PackagePart FindPart(Uri uri) { ppn = PackagingUriHelper.CreatePartName(path); } - catch (InvalidFormatException e) + catch (InvalidFormatException) { //Console.WriteLine(POILogger.WARN, "illegal part name (not expected)", uri); return null; diff --git a/ooxml/POIFS/Crypt/Dsig/SignatureConfig.cs b/ooxml/POIFS/Crypt/Dsig/SignatureConfig.cs index 699db9c26..5c085f330 100644 --- a/ooxml/POIFS/Crypt/Dsig/SignatureConfig.cs +++ b/ooxml/POIFS/Crypt/Dsig/SignatureConfig.cs @@ -38,9 +38,9 @@ public class SignatureConfig { private ThreadLocal opcPackage = new ThreadLocal(); - ///private ThreadLocal signatureFactory = new ThreadLocal(); - ///private ThreadLocal keyInfoFactory = new ThreadLocal(); - ///private ThreadLocal provider = new ThreadLocal(); + //private ThreadLocal signatureFactory = new ThreadLocal(); + //private ThreadLocal keyInfoFactory = new ThreadLocal(); + //private ThreadLocal provider = new ThreadLocal(); private List signatureFacets = new List(); private HashAlgorithm digestAlgo = HashAlgorithm.sha1; diff --git a/ooxml/POIXMLFactory.cs b/ooxml/POIXMLFactory.cs index cad3ee149..f2bce3bc5 100644 --- a/ooxml/POIXMLFactory.cs +++ b/ooxml/POIXMLFactory.cs @@ -63,7 +63,7 @@ public virtual POIXMLDocumentPart CreateDocumentPart(POIXMLDocumentPart parent, { return CreateDocumentPart(cls, PARENT_PART, new Object[] { parent, part }); } - catch (MissingMethodException e) + catch (MissingMethodException) { return CreateDocumentPart(cls, ORPHAN_PART, new Object[] { part }); } diff --git a/ooxml/XSSF/Model/StylesTable.cs b/ooxml/XSSF/Model/StylesTable.cs index 2c20b6d2d..47cc5e62f 100644 --- a/ooxml/XSSF/Model/StylesTable.cs +++ b/ooxml/XSSF/Model/StylesTable.cs @@ -323,7 +323,7 @@ public int PutNumberFormat(String fmt) { return GetNumberFormatId(fmt); } - catch (InvalidOperationException e) + catch (InvalidOperationException ) { throw new InvalidOperationException("Found the format, but couldn't figure out where - should never happen!"); } diff --git a/ooxml/XSSF/UserModel/BaseXSSFEvaluationWorkbook.cs b/ooxml/XSSF/UserModel/BaseXSSFEvaluationWorkbook.cs index cd16bfe4c..ce507904f 100644 --- a/ooxml/XSSF/UserModel/BaseXSSFEvaluationWorkbook.cs +++ b/ooxml/XSSF/UserModel/BaseXSSFEvaluationWorkbook.cs @@ -90,7 +90,7 @@ private int ResolveBookIndex(String bookName) { return Int32.Parse(bookName); } - catch (FormatException e) { } + catch (FormatException ) { } // Look up an External Link Table for this name List tables = _uBook.ExternalLinksTable; diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index 5f4973c0f..12fc10d83 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -730,7 +730,7 @@ private String GetUniqueSheetName(String srcName) uniqueIndex++; baseName = srcName.Substring(0, bracketPos).Trim(); } - catch (FormatException e) + catch (FormatException) { // contents of brackets not numeric } diff --git a/ooxml/XWPF/Usermodel/XWPFSDTContentCell.cs b/ooxml/XWPF/Usermodel/XWPFSDTContentCell.cs index d54fd4980..1074720fd 100644 --- a/ooxml/XWPF/Usermodel/XWPFSDTContentCell.cs +++ b/ooxml/XWPF/Usermodel/XWPFSDTContentCell.cs @@ -167,7 +167,6 @@ private bool IsStartToken(object cursor, String string1) //{ // return true; //} - return false; } diff --git a/openxml4Net/Exceptions/InvalidFormatException.cs b/openxml4Net/Exceptions/InvalidFormatException.cs index 655b1ed91..bcee376ab 100644 --- a/openxml4Net/Exceptions/InvalidFormatException.cs +++ b/openxml4Net/Exceptions/InvalidFormatException.cs @@ -6,8 +6,6 @@ namespace NPOI.OpenXml4Net.Exceptions { public class InvalidFormatException:OpenXml4NetException { - private string p; - private InvalidFormatException ex; public InvalidFormatException(String message):base(message) { diff --git a/openxml4Net/Exceptions/OpenXML4NetException.cs b/openxml4Net/Exceptions/OpenXML4NetException.cs index 8a7a2a4a2..ae8ba25cd 100644 --- a/openxml4Net/Exceptions/OpenXML4NetException.cs +++ b/openxml4Net/Exceptions/OpenXML4NetException.cs @@ -5,9 +5,6 @@ namespace NPOI.OpenXml4Net { public class OpenXml4NetException : Exception { - private Exceptions.InvalidFormatException ex; - - public OpenXml4NetException(String msg) : base(msg) { diff --git a/openxml4Net/NPOI.OpenXml4Net.csproj b/openxml4Net/NPOI.OpenXml4Net.csproj index 3342421de..382ffe597 100644 --- a/openxml4Net/NPOI.OpenXml4Net.csproj +++ b/openxml4Net/NPOI.OpenXml4Net.csproj @@ -73,7 +73,7 @@ TRACE;HIDE_UNREACHABLE_CODE ..\solution\Lib\.net45\NPOI.OpenXml4Net.XML true - 1591 + 1591;1587;1570 pdbonly AnyCPU 7.3 diff --git a/openxml4Net/OPC/Internal/ContentType.cs b/openxml4Net/OPC/Internal/ContentType.cs index 507b265d6..ad5971c24 100644 --- a/openxml4Net/OPC/Internal/ContentType.cs +++ b/openxml4Net/OPC/Internal/ContentType.cs @@ -45,7 +45,6 @@ public class ContentType:IComparable /** * Parameters */ - Hashtable p; private Dictionary parameters; /** diff --git a/openxml4Net/OPC/PackagePart.cs b/openxml4Net/OPC/PackagePart.cs index e769ce02c..75b0463ba 100644 --- a/openxml4Net/OPC/PackagePart.cs +++ b/openxml4Net/OPC/PackagePart.cs @@ -511,7 +511,7 @@ public PackagePart GetRelatedPart(PackageRelationship rel) { target = PackagingUriHelper.ParseUri(t.Substring(0, t.IndexOf('#')), UriKind.Absolute); } - catch (UriFormatException e) + catch (UriFormatException) { throw new InvalidFormatException("Invalid target URI: " + t); } diff --git a/openxml4Net/OPC/PackageRelationshipCollection.cs b/openxml4Net/OPC/PackageRelationshipCollection.cs index 48c236fc1..417e44ba6 100644 --- a/openxml4Net/OPC/PackageRelationshipCollection.cs +++ b/openxml4Net/OPC/PackageRelationshipCollection.cs @@ -353,7 +353,7 @@ private void ParseRelationshipsPart(PackagePart relPart) // Check OPC compliance M4.1 rule bool fCorePropertiesRelationship = false; - ///xmlRelationshipsDoc.ChildNodes.GetEnumerator(); + //xmlRelationshipsDoc.ChildNodes.GetEnumerator(); XPathNavigator xpathnav = xmlRelationshipsDoc.CreateNavigator(); XmlNamespaceManager nsMgr = new XmlNamespaceManager(xpathnav.NameTable); nsMgr.AddNamespace("x", PackageNamespaces.RELATIONSHIPS); diff --git a/testcases/main/HSSF/UserModel/TestHSSFDateUtil.cs b/testcases/main/HSSF/UserModel/TestHSSFDateUtil.cs index 527d242ab..f17393232 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFDateUtil.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFDateUtil.cs @@ -489,7 +489,7 @@ public void AbsoluteDayYearTooLow() HSSFDateUtil.AbsoluteDay(calendar, false); Assert.Fail("Should fail here"); } - catch (ArgumentException e) + catch (ArgumentException ) { // expected here } @@ -500,7 +500,7 @@ public void AbsoluteDayYearTooLow() HSSFDateUtil.AbsoluteDay(calendar, true); Assert.Fail("Should fail here"); } - catch (ArgumentException e) + catch (ArgumentException ) { // expected here } diff --git a/testcases/main/HSSF/UserModel/TestHSSFSheet.cs b/testcases/main/HSSF/UserModel/TestHSSFSheet.cs index 916e7b135..e9ee8f5d3 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFSheet.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFSheet.cs @@ -696,9 +696,8 @@ public void TestTopRow() /** cell with formula becomes null on cloning a sheet*/ [Test] - public new void Test35084() + public void Test35084() { - HSSFWorkbook wb = new HSSFWorkbook(); NPOI.SS.UserModel.ISheet s = wb.CreateSheet("Sheet1"); IRow r = s.CreateRow(0); diff --git a/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs b/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs index 95f9e7eac..1837bb22f 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs @@ -1359,7 +1359,7 @@ public void InvalidInPlaceWrite() wb.Write(); Assert.Fail("Shouldn't work for new files"); } - catch (InvalidOperationException e) { } + catch (InvalidOperationException) { } // Can't work for InputStream opened files wb = new HSSFWorkbook( @@ -1369,7 +1369,7 @@ public void InvalidInPlaceWrite() wb.Write(); Assert.Fail("Shouldn't work for InputStream"); } - catch (InvalidOperationException e) { } + catch (InvalidOperationException) { } // Can't work for OPOIFS OPOIFSFileSystem ofs = new OPOIFSFileSystem( @@ -1380,7 +1380,7 @@ public void InvalidInPlaceWrite() wb.Write(); Assert.Fail("Shouldn't work for OPOIFSFileSystem"); } - catch (InvalidOperationException e) { } + catch (InvalidOperationException) { } // Can't work for Read-Only files NPOIFSFileSystem fs = new NPOIFSFileSystem( @@ -1391,7 +1391,7 @@ public void InvalidInPlaceWrite() wb.Write(); Assert.Fail("Shouldn't work for Read Only"); } - catch (InvalidOperationException e) { } + catch (InvalidOperationException) { } } [Test] diff --git a/testcases/main/POIFS/Crypt/TestCipherAlgorithm.cs b/testcases/main/POIFS/Crypt/TestCipherAlgorithm.cs index 4b9f5a3ba..54588e53b 100644 --- a/testcases/main/POIFS/Crypt/TestCipherAlgorithm.cs +++ b/testcases/main/POIFS/Crypt/TestCipherAlgorithm.cs @@ -26,7 +26,7 @@ public void Test() CipherAlgorithm.FromEcmaId(0); Assert.Fail("Should throw exception"); } - catch (EncryptedDocumentException e) + catch (EncryptedDocumentException) { // expected } @@ -36,7 +36,7 @@ public void Test() CipherAlgorithm.FromXmlId("AES", 1); Assert.Fail("Should throw exception"); } - catch (EncryptedDocumentException e) + catch (EncryptedDocumentException) { // expected } @@ -46,7 +46,7 @@ public void Test() CipherAlgorithm.FromXmlId("RC1", 0x40); Assert.Fail("Should throw exception"); } - catch (EncryptedDocumentException e) + catch (EncryptedDocumentException) { // expected } diff --git a/testcases/main/SS/Format/TestCellFormatResult.cs b/testcases/main/SS/Format/TestCellFormatResult.cs index a9ee94c2f..c94628772 100644 --- a/testcases/main/SS/Format/TestCellFormatResult.cs +++ b/testcases/main/SS/Format/TestCellFormatResult.cs @@ -36,7 +36,7 @@ public void TestNullTextRaisesException() CellFormatResult result = new CellFormatResult(applies, text, textColor); Assert.Fail("Cannot Initialize CellFormatResult with null text parameter"); } - catch (ArgumentException e) + catch (ArgumentException ) { //Expected } diff --git a/testcases/main/SS/Formula/Functions/TestDec2Hex.cs b/testcases/main/SS/Formula/Functions/TestDec2Hex.cs index 8106fdd90..ccbf88dc0 100644 --- a/testcases/main/SS/Formula/Functions/TestDec2Hex.cs +++ b/testcases/main/SS/Formula/Functions/TestDec2Hex.cs @@ -101,7 +101,7 @@ public void TestBasic() Assert.AreEqual("549755813887", maxLong); ConfirmValue("Converts the max supported value to hexadecimal", maxLong, "7FFFFFFFFF"); - String minLong = (-549755813888l).ToString(); + String minLong = (-549755813888L).ToString(); Assert.AreEqual("-549755813888", minLong); ConfirmValue("Converts the min supported value to hexadecimal", minLong, "FF80000000"); diff --git a/testcases/main/SS/Formula/TestFormulaShifter.cs b/testcases/main/SS/Formula/TestFormulaShifter.cs index 2bec1a513..858ef4d1f 100644 --- a/testcases/main/SS/Formula/TestFormulaShifter.cs +++ b/testcases/main/SS/Formula/TestFormulaShifter.cs @@ -271,7 +271,7 @@ public void TestInvalidArgument() FormulaShifter.CreateForRowShift(1, "name", 1, 2, 0, SpreadsheetVersion.EXCEL97); Assert.Fail("Should catch exception here"); } - catch (ArgumentException e) + catch (ArgumentException) { // expected here } @@ -280,7 +280,7 @@ public void TestInvalidArgument() FormulaShifter.CreateForRowShift(1, "name", 2, 1, 2, SpreadsheetVersion.EXCEL97); Assert.Fail("Should catch exception here"); } - catch (ArgumentException e) + catch (ArgumentException) { // expected here } diff --git a/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs b/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs index aff2e911c..18376c24a 100644 --- a/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs +++ b/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs @@ -1111,7 +1111,7 @@ public void Test57973() IFont font = wb.CreateFont(); font.FontName = ("Arial"); font.FontHeightInPoints = ((short)14); - font.Boldweight = (short)FontBoldWeight.Bold;// (Font.BOLDWEIGHT_BOLD); + font.IsBold=true; font.Color = (IndexedColors.Red.Index); str2.ApplyFont(font); diff --git a/testcases/main/SS/UserModel/BaseTestCloneSheet.cs b/testcases/main/SS/UserModel/BaseTestCloneSheet.cs index 17fbb5209..cf0368b69 100644 --- a/testcases/main/SS/UserModel/BaseTestCloneSheet.cs +++ b/testcases/main/SS/UserModel/BaseTestCloneSheet.cs @@ -85,7 +85,7 @@ public void TestCloneSheetIntValid() wb.CloneSheet(2); Assert.Fail("ShouldFail"); } - catch (ArgumentException e) + catch (ArgumentException) { // expected here } @@ -101,7 +101,7 @@ public void TestCloneSheetIntInvalid() wb.CloneSheet(1); Assert.Fail("Should Fail"); } - catch (ArgumentException e) + catch (ArgumentException) { // expected here } diff --git a/testcases/main/SS/UserModel/BaseTestSheet.cs b/testcases/main/SS/UserModel/BaseTestSheet.cs index fda9687ce..2076d8f82 100644 --- a/testcases/main/SS/UserModel/BaseTestSheet.cs +++ b/testcases/main/SS/UserModel/BaseTestSheet.cs @@ -357,7 +357,7 @@ public void AddMergedRegionWithSingleCellShouldFail() sheet.AddMergedRegion(region); Assert.Fail("Should not be able to add a single-cell merged region (" + region.FormatAsString() + ")"); } - catch (ArgumentException e) + catch (ArgumentException) { // expected } diff --git a/testcases/main/SS/UserModel/BaseTestSheetUpdateArrayFormulas.cs b/testcases/main/SS/UserModel/BaseTestSheetUpdateArrayFormulas.cs index 49b2af015..945ee5626 100644 --- a/testcases/main/SS/UserModel/BaseTestSheetUpdateArrayFormulas.cs +++ b/testcases/main/SS/UserModel/BaseTestSheetUpdateArrayFormulas.cs @@ -650,7 +650,7 @@ public void ShouldNotBeAbleToCreateArrayFormulaOnPreexistingMergedRegion() sheet.SetArrayFormula("SUM(A1:A3)", arrayFormula); Assert.Fail("expected exception: should not be able to create an array formula that intersects with a merged region"); } - catch (InvalidOperationException e) + catch (InvalidOperationException) { // expected } diff --git a/testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs b/testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs index 491ba8ca7..acbc8fae9 100644 --- a/testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs +++ b/testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs @@ -37,7 +37,7 @@ public class TestSecureTempZip * Test case for #59841 - this is an example on how to use encrypted temp files, * which are streamed into POI opposed to having everything in memory */ - [Test] + [Ignore] public void ProtectedTempZip() { FileInfo tmpFile = TempFile.CreateTempFile("protectedXlsx", ".zip"); diff --git a/testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs b/testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs index 007e343b7..dd16b50cd 100644 --- a/testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs +++ b/testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs @@ -242,7 +242,7 @@ public void TestSignSpreadsheet() throw new NotImplementedException(); } - [Test] + [Ignore] public void TestSignEnvelopingDocument() { String testFile = "hello-world-unsigned.xlsx"; //OPCPackage pkg = OPCPackage.Open(copy(testdata.GetFile(testFile)), PackageAccess.READ_WRITE); @@ -476,7 +476,7 @@ public void TestSignSpreadsheet() throw new NotImplementedException(); } - [Test] + [Ignore] public void TestNonSha1() { String testFile = "hello-world-unsigned.xlsx"; InitKeyPair("Test", "CN=Test"); diff --git a/testcases/ooxml/SS/TestWorkbookFactory.cs b/testcases/ooxml/SS/TestWorkbookFactory.cs index b50dff5ac..ca24f721c 100644 --- a/testcases/ooxml/SS/TestWorkbookFactory.cs +++ b/testcases/ooxml/SS/TestWorkbookFactory.cs @@ -359,7 +359,7 @@ public void TestEmptyInputStream() WorkbookFactory.Create(emptyStream,true); Assert.Fail("Shouldn't be able to create for an empty stream"); } - catch (EmptyFileException e) + catch (EmptyFileException ) { } } @@ -376,7 +376,7 @@ public void TestEmptyFile() WorkbookFactory.Create(emptyFile.FullName); Assert.Fail("Shouldn't be able to create for an empty file"); } - catch (EmptyFileException e) + catch (EmptyFileException ) { } emptyFile.Delete(); diff --git a/testcases/ooxml/SS/UserModel/TestFormulaParser.cs b/testcases/ooxml/SS/UserModel/TestFormulaParser.cs index 5cd608b9b..6fcd9de95 100644 --- a/testcases/ooxml/SS/UserModel/TestFormulaParser.cs +++ b/testcases/ooxml/SS/UserModel/TestFormulaParser.cs @@ -38,7 +38,7 @@ public void TestHSSFFailsForOver65536() FormulaParser.Parse("Sheet1!1:65537", workbook, FormulaType.Cell, 0); Assert.Fail("Expected exception"); } - catch (FormulaParseException expected) + catch (FormulaParseException) { } } diff --git a/testcases/ooxml/XSSF/TestXSSFCloneSheet.cs b/testcases/ooxml/XSSF/TestXSSFCloneSheet.cs index 8889f9dae..aad263620 100644 --- a/testcases/ooxml/XSSF/TestXSSFCloneSheet.cs +++ b/testcases/ooxml/XSSF/TestXSSFCloneSheet.cs @@ -54,7 +54,7 @@ public TestXSSFCloneSheet() try { wb.CloneSheet(0, VALID_SHEET_NAME); Assert.Fail("Should fail"); - } catch (ArgumentException e) { + } catch (ArgumentException) { // expected here } Assert.AreEqual(1, wb.NumberOfSheets); diff --git a/testcases/ooxml/XSSF/UserModel/Extensions/TestXSSFCellFill.cs b/testcases/ooxml/XSSF/UserModel/Extensions/TestXSSFCellFill.cs index f5ce083d0..4c9a9db5b 100644 --- a/testcases/ooxml/XSSF/UserModel/Extensions/TestXSSFCellFill.cs +++ b/testcases/ooxml/XSSF/UserModel/Extensions/TestXSSFCellFill.cs @@ -76,7 +76,7 @@ public void TestColorFromTheme() XSSFCell cellWithThemeColor = (XSSFCell)wb.GetSheetAt(0).GetRow(10).GetCell(0); //color RGB will be extracted from theme XSSFColor foregroundColor = (XSSFColor)((XSSFCellStyle)cellWithThemeColor.CellStyle).FillForegroundColorColor; - byte[] rgb = foregroundColor.GetRgb(); + byte[] rgb = foregroundColor.RGB; byte[] rgbWithTint = foregroundColor.GetRgbWithTint(); // Dk2 Assert.AreEqual(rgb[0], 31); diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs index 24b78653c..8d3137542 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs @@ -1628,7 +1628,7 @@ public void Test55692() WorkbookFactory.Create(inpA); Assert.Fail("Should've raised a EncryptedDocumentException error"); } - catch (EncryptedDocumentException e) { } + catch (EncryptedDocumentException ) { } // Via a POIFSFileSystem POIFSFileSystem fsP = new POIFSFileSystem(inpB); @@ -1637,7 +1637,7 @@ public void Test55692() WorkbookFactory.Create(fsP); Assert.Fail("Should've raised a EncryptedDocumentException error"); } - catch (EncryptedDocumentException e) { } + catch (EncryptedDocumentException) { } // Via a NPOIFSFileSystem NPOIFSFileSystem fsNP = new NPOIFSFileSystem(inpC); @@ -1646,7 +1646,7 @@ public void Test55692() WorkbookFactory.Create(fsNP); Assert.Fail("Should've raised a EncryptedDocumentException error"); } - catch (EncryptedDocumentException e) { } + catch (EncryptedDocumentException) { } } [Test] public void Bug53282() @@ -2151,7 +2151,7 @@ public void Bug56800_xlsb() new XSSFWorkbook(pkg).Close(); Assert.Fail(".xlsb files not supported"); } - catch (XLSBUnsupportedException e) + catch (XLSBUnsupportedException) { // Good, detected and warned } @@ -2162,7 +2162,7 @@ public void Bug56800_xlsb() WorkbookFactory.Create(pkg).Close(); Assert.Fail(".xlsb files not supported"); } - catch (XLSBUnsupportedException e) + catch (XLSBUnsupportedException) { // Good, detected and warned } @@ -2174,7 +2174,7 @@ public void Bug56800_xlsb() WorkbookFactory.Create(xlsbFile.FullName).Close(); Assert.Fail(".xlsb files not supported"); } - catch (XLSBUnsupportedException e) + catch (XLSBUnsupportedException) { // Good, detected and warned } diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFDrawing.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFDrawing.cs index c27ee9ee1..f4d7dc14d 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFDrawing.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFDrawing.cs @@ -800,7 +800,7 @@ public void TestBug56835CellComment() Drawing.CreateCellComment(anchor); Assert.Fail("Should fail if we try to add the same comment for the same cell"); } - catch (ArgumentException e) + catch (ArgumentException ) { // expected } diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFRichTextString.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFRichTextString.cs index 1170580f7..b9295ed96 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFRichTextString.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFRichTextString.cs @@ -127,7 +127,7 @@ public void TestApplyFontWithStyles() rt.ApplyFont(0, 10, (short)1); Assert.Fail("Fails without styles in the table"); } - catch (ArgumentOutOfRangeException e) + catch (ArgumentOutOfRangeException ) { // expected } diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFSheetShiftRows.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFSheetShiftRows.cs index 2fa9c4c43..212e0f3f7 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFSheetShiftRows.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFSheetShiftRows.cs @@ -352,7 +352,7 @@ public void TestRemoveSheetAndAdjustActiveSheet() wb.RemoveSheetAt(0); Assert.Fail("Should catch exception as no more sheets are there"); } - catch (ArgumentException e) + catch (ArgumentException) { // expected } diff --git a/testcases/openxml4net/OPC/Compliance/TestOPCComplianceCoreProperties.cs b/testcases/openxml4net/OPC/Compliance/TestOPCComplianceCoreProperties.cs index 2a2cd4284..6f6d68326 100644 --- a/testcases/openxml4net/OPC/Compliance/TestOPCComplianceCoreProperties.cs +++ b/testcases/openxml4net/OPC/Compliance/TestOPCComplianceCoreProperties.cs @@ -103,7 +103,7 @@ public void TestOnlyOneCorePropertiesPart() ExtractInvalidFormatMessage("OnlyOneCorePropertiesPartFAIL.docx"); Assert.Fail("M4.1 should be being relaxed"); } - catch (AssertionException e) { } + catch (AssertionException) { } // We will use the first core properties, and ignore the others Stream is1 = OpenXml4NetTestDataSamples.OpenSampleStream("MultipleCoreProperties.docx"); @@ -144,7 +144,7 @@ public void TestOnlyOneCorePropertiesPart_AddRelationship() // no longer fail on compliance error //fail("expected OPC compliance exception was not thrown"); } - catch (InvalidFormatException e) + catch (InvalidFormatException) { throw; } diff --git a/testcases/openxml4net/OPC/Compliance/TestOPCCompliancePackageModel.cs b/testcases/openxml4net/OPC/Compliance/TestOPCCompliancePackageModel.cs index 70735a963..7b6faf0e2 100644 --- a/testcases/openxml4net/OPC/Compliance/TestOPCCompliancePackageModel.cs +++ b/testcases/openxml4net/OPC/Compliance/TestOPCCompliancePackageModel.cs @@ -51,7 +51,7 @@ public void TestPartNameDerivationAdditionAssert_Failure() pkg.CreatePart(name, ContentTypes.XML); pkg.CreatePart(nameDerived, ContentTypes.EXTENSION_GIF); } - catch (InvalidOperationException e) + catch (InvalidOperationException) { pkg.Revert(); return; @@ -74,7 +74,7 @@ public void TestPartNameDerivationReadingAssert_Failure() { OPCPackage.Open(POIDataSamples.GetOpenXML4JInstance().OpenResourceAsStream(filename)); } - catch (InvalidFormatException e) + catch (InvalidFormatException) { return; } @@ -108,7 +108,7 @@ public void TestAddPackageAlreadyAddFailure() { pkg.CreatePart(name2, ContentTypes.XML); } - catch (PartAlreadyExistsException e) + catch (PartAlreadyExistsException) { return; } @@ -138,7 +138,7 @@ public void TestAddPackageAlreadyAddAssert_Failure2() { pkg.CreatePart(partName, ContentTypes.XML); } - catch (InvalidOperationException e) + catch (InvalidOperationException) { return; } @@ -163,7 +163,7 @@ public void TestAddRelationshipRelationshipsPartAssert_Failure() name1 = PackagingUriHelper .CreatePartName("/test/_rels/document.xml.rels"); } - catch (InvalidFormatException e) + catch (InvalidFormatException) { Assert.Fail("This exception should never happen !"); } @@ -173,7 +173,7 @@ public void TestAddRelationshipRelationshipsPartAssert_Failure() pkg.AddRelationship(name1, TargetMode.Internal, PackageRelationshipTypes.CORE_DOCUMENT); } - catch (InvalidOperationException e) + catch (InvalidOperationException) { return; } diff --git a/testcases/openxml4net/OPC/Compliance/TestOPCCompliancePartName.cs b/testcases/openxml4net/OPC/Compliance/TestOPCCompliancePartName.cs index a0c8fce58..62071b007 100644 --- a/testcases/openxml4net/OPC/Compliance/TestOPCCompliancePartName.cs +++ b/testcases/openxml4net/OPC/Compliance/TestOPCCompliancePartName.cs @@ -128,7 +128,7 @@ public void TestEmptyPartNameFailure() PackagingUriHelper.CreatePartName(new Uri("",UriKind.Relative)); Assert.Fail("A part name shall not be empty. [M1.1]"); } - catch (InvalidFormatException e) + catch (InvalidFormatException) { // Normal behaviour } @@ -210,7 +210,7 @@ public void TestPartNameStartsWithAForwardSlashFailure() PackagingUriHelper.CreatePartName(new Uri("document.xml", UriKind.RelativeOrAbsolute)); Assert.Fail("A part name shall start with a forward slash ('/') character. [M1.4]"); } - catch (InvalidFormatException e) + catch (InvalidFormatException ) { // Normal behaviour } @@ -227,7 +227,7 @@ public void TestPartNameEndsWithAForwardSlashFailure() PackagingUriHelper.CreatePartName(new Uri("/document.xml/", UriKind.Relative)); Assert.Fail("A part name shall not have a forward slash as the last character. [M1.5]"); } - catch (InvalidFormatException e) + catch (InvalidFormatException) { // Normal behaviour } diff --git a/testcases/openxml4net/TestPackage.cs b/testcases/openxml4net/TestPackage.cs index a8b0553e5..3c902ab4c 100644 --- a/testcases/openxml4net/TestPackage.cs +++ b/testcases/openxml4net/TestPackage.cs @@ -1031,7 +1031,7 @@ public void TestCorruptFile() { pkg = OPCPackage.Open(file, PackageAccess.READ); } - catch (Exception e) + catch (Exception) { //System.out.println(e.GetClass().getName()); //System.out.println(e.GetMessage()); diff --git a/testcases/openxml4net/TestPackagingURIHelper.cs b/testcases/openxml4net/TestPackagingURIHelper.cs index 2416be2c5..ad22fce07 100644 --- a/testcases/openxml4net/TestPackagingURIHelper.cs +++ b/testcases/openxml4net/TestPackagingURIHelper.cs @@ -132,7 +132,7 @@ public void TestCreateUriFromString() { Uri Uri = PackagingUriHelper.ToUri(s); } - catch (UriFormatException e) + catch (UriFormatException) { Assert.Fail("Failed to create Uri from " + s); } diff --git a/testcases/openxml4net/TestZipPackage.cs b/testcases/openxml4net/TestZipPackage.cs index e7835a851..8dfec2006 100644 --- a/testcases/openxml4net/TestZipPackage.cs +++ b/testcases/openxml4net/TestZipPackage.cs @@ -266,10 +266,10 @@ public void TestTidyStreamOnInvalidFile() pkg.GetParts(); Assert.Fail("Shouldn't work"); } - catch (ODFNotOfficeXmlFileException e) + catch (ODFNotOfficeXmlFileException) { } - catch (NotOfficeXmlFileException ne) { } + catch (NotOfficeXmlFileException) { } pkg.Close(); Assert.IsNotNull(pkg.ZipArchive); @@ -285,10 +285,10 @@ public void TestTidyStreamOnInvalidFile() pkg.GetParts(); Assert.Fail("Shouldn't work"); } - catch (ODFNotOfficeXmlFileException e) + catch (ODFNotOfficeXmlFileException) { } - catch (NotOfficeXmlFileException ne) { } + catch (NotOfficeXmlFileException) { } pkg.Close(); Assert.IsNotNull(pkg.ZipArchive); From 2e61e6b80a720d4139771fc5d8a67a3c79f42b2a Mon Sep 17 00:00:00 2001 From: Collin Alpert Date: Mon, 23 May 2022 09:10:55 +0200 Subject: [PATCH 141/159] Added methods to other implementations. --- ooxml/NPOI.OOXML.Core.csproj | 2 +- ooxml/XSSF/Streaming/SXSSFCell.cs | 19 +++++++++++++++++++ ooxml/XSSF/UserModel/XSSFCell.cs | 20 ++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/ooxml/NPOI.OOXML.Core.csproj b/ooxml/NPOI.OOXML.Core.csproj index 46de29584..bc51cd2a6 100644 --- a/ooxml/NPOI.OOXML.Core.csproj +++ b/ooxml/NPOI.OOXML.Core.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net6.0 false NPOI.OOXML NPOI.OOXML diff --git a/ooxml/XSSF/Streaming/SXSSFCell.cs b/ooxml/XSSF/Streaming/SXSSFCell.cs index d8c0c57ac..06e9c21db 100644 --- a/ooxml/XSSF/Streaming/SXSSFCell.cs +++ b/ooxml/XSSF/Streaming/SXSSFCell.cs @@ -825,6 +825,25 @@ public void SetCellValue(DateTime value) SetCellValue((DateTime?)value); } +#if NET6_0_OR_GREATER + public void SetCellValue(DateOnly value) + { + bool date1904 = ((SXSSFWorkbook)Sheet.Workbook).XssfWorkbook.IsDate1904(); + SetCellValue(DateUtil.GetExcelDate(value, date1904)); + } + + public void SetCellValue(DateOnly? value) + { + if (!value.HasValue) + { + SetCellType(CellType.Blank); + return; + } + + SetCellValue(value.Value); + } +#endif + public void SetBlank() { SetCellType(CellType.Blank); diff --git a/ooxml/XSSF/UserModel/XSSFCell.cs b/ooxml/XSSF/UserModel/XSSFCell.cs index 4976b9c09..826d7a647 100644 --- a/ooxml/XSSF/UserModel/XSSFCell.cs +++ b/ooxml/XSSF/UserModel/XSSFCell.cs @@ -824,6 +824,26 @@ public void SetCellValue(DateTime value) bool date1904 = Sheet.Workbook.IsDate1904(); SetCellValue(DateUtil.GetExcelDate(value, date1904)); } + +#if NET6_0_OR_GREATER + public void SetCellValue(DateOnly value) + { + bool date1904 = Sheet.Workbook.IsDate1904(); + SetCellValue(DateUtil.GetExcelDate(value, date1904)); + } + + public void SetCellValue(DateOnly? value) + { + if (value == null) + { + SetCellType(CellType.Blank); + return; + } + + SetCellValue(value.Value); + } +#endif + /// /// Returns the error message, such as #VALUE! /// From dddffa3c4fede5a29f8360040504a5d269419c1e Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 30 May 2022 16:56:44 +0800 Subject: [PATCH 142/159] make GetCTFill public --- ooxml/XSSF/UserModel/Extensions/XSSFCellFill.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ooxml/XSSF/UserModel/Extensions/XSSFCellFill.cs b/ooxml/XSSF/UserModel/Extensions/XSSFCellFill.cs index b8fc5c450..b21365972 100644 --- a/ooxml/XSSF/UserModel/Extensions/XSSFCellFill.cs +++ b/ooxml/XSSF/UserModel/Extensions/XSSFCellFill.cs @@ -160,7 +160,7 @@ private CT_PatternFill EnsureCTPatternFill() * @return CT_Fill */ - internal CT_Fill GetCTFill() + public CT_Fill GetCTFill() { return _fill; } From 5fdb42c33ae438e1627410445f20fb31d4481bb5 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 30 May 2022 17:27:12 +0800 Subject: [PATCH 143/159] Update CT_GradientStop #843 --- OpenXmlFormats/Spreadsheet/BaseTypes.cs | 35 ++++++++++++++++++-- OpenXmlFormats/Spreadsheet/Styles/CT_Fill.cs | 9 +++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/BaseTypes.cs b/OpenXmlFormats/Spreadsheet/BaseTypes.cs index ebe7a34e3..97b874554 100644 --- a/OpenXmlFormats/Spreadsheet/BaseTypes.cs +++ b/OpenXmlFormats/Spreadsheet/BaseTypes.cs @@ -41,15 +41,29 @@ public byte[] ToBytes() public class CT_GradientStop { - private int positionField; + private double positionField; private CT_Color colorField; + public double position + { + get { + return positionField; + } + set { positionField = value; } + } + + public CT_Color AddNewColor() + { + this.colorField = new CT_Color(); + return colorField; + } + public static CT_GradientStop Parse(XmlNode node, XmlNamespaceManager namespaceManager) { if (node == null) return null; CT_GradientStop ctObj = new CT_GradientStop(); - ctObj.positionField = XmlHelper.ReadInt(node.Attributes["position"]); + ctObj.positionField = XmlHelper.ReadDouble(node.Attributes["position"]); foreach (XmlNode childNode in node.ChildNodes) { if (childNode.LocalName == "color") @@ -133,7 +147,22 @@ internal void Write(StreamWriter sw, string nodeName) } sw.Write(string.Format("", nodeName)); } - + public CT_GradientStop AddNewStop() + { + var newstop = new CT_GradientStop(); + if (this.stop == null) + this.stop = new List(); + this.stop.Add(newstop); + return newstop; + } + public CT_GradientStop GetStopArray(int index) + { + if (this.stop == null) + { + return null; + } + return this.stop[index]; + } [XmlElement] public List stop { diff --git a/OpenXmlFormats/Spreadsheet/Styles/CT_Fill.cs b/OpenXmlFormats/Spreadsheet/Styles/CT_Fill.cs index 7cc220b61..cd44f227f 100644 --- a/OpenXmlFormats/Spreadsheet/Styles/CT_Fill.cs +++ b/OpenXmlFormats/Spreadsheet/Styles/CT_Fill.cs @@ -40,6 +40,15 @@ public CT_PatternFill AddNewPatternFill() this.patternFillField = new CT_PatternFill(); return GetPatternFill(); } + public CT_GradientFill AddNewGradientFill() + { + this.gradientFillField = new CT_GradientFill(); + return this.gradientFillField; + } + public void UnsetPatternFill() + { + this.patternFillField = null; + } public bool IsSetPatternFill() { return this.patternFillField != null; From de7777ca80775d1cd7b08ec63fba85bcbb09e378 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 6 Jun 2022 07:49:00 +0800 Subject: [PATCH 144/159] fix test cases --- testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs | 2 +- testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs b/testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs index acbc8fae9..eb774cdd1 100644 --- a/testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs +++ b/testcases/ooxml/POIFS/Crypt/TestSecureTempZip.cs @@ -37,7 +37,7 @@ public class TestSecureTempZip * Test case for #59841 - this is an example on how to use encrypted temp files, * which are streamed into POI opposed to having everything in memory */ - [Ignore] + [Ignore("not implemented")] public void ProtectedTempZip() { FileInfo tmpFile = TempFile.CreateTempFile("protectedXlsx", ".zip"); diff --git a/testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs b/testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs index dd16b50cd..76b912420 100644 --- a/testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs +++ b/testcases/ooxml/POIFS/Crypt/TestSignatureInfo.cs @@ -242,7 +242,7 @@ public void TestSignSpreadsheet() throw new NotImplementedException(); } - [Ignore] + [Ignore("not implemented")] public void TestSignEnvelopingDocument() { String testFile = "hello-world-unsigned.xlsx"; //OPCPackage pkg = OPCPackage.Open(copy(testdata.GetFile(testFile)), PackageAccess.READ_WRITE); @@ -476,7 +476,7 @@ public void TestSignSpreadsheet() throw new NotImplementedException(); } - [Ignore] + [Ignore("not implemented")] public void TestNonSha1() { String testFile = "hello-world-unsigned.xlsx"; InitKeyPair("Test", "CN=Test"); From b5b21c522f19d08eac17018d684eaf925c898110 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 6 Jun 2022 07:50:33 +0800 Subject: [PATCH 145/159] fix removing formula issue Add RemoveFormula method and fix #829 --- main/HSSF/UserModel/HSSFCell.cs | 72 +++++++++++++++++++++++++++++++ main/SS/UserModel/Cell.cs | 9 +++- ooxml/XSSF/Streaming/SXSSFCell.cs | 33 +++++++++++++- ooxml/XSSF/UserModel/XSSFCell.cs | 43 ++++++++++++++++-- ooxml/XSSF/UserModel/XSSFSheet.cs | 46 ++++++++++++++++++++ 5 files changed, 198 insertions(+), 5 deletions(-) diff --git a/main/HSSF/UserModel/HSSFCell.cs b/main/HSSF/UserModel/HSSFCell.cs index 0e2832ec1..2a3eaea87 100644 --- a/main/HSSF/UserModel/HSSFCell.cs +++ b/main/HSSF/UserModel/HSSFCell.cs @@ -32,6 +32,7 @@ namespace NPOI.HSSF.UserModel using System.Globalization; using System.Collections.Generic; using NPOI.Util; + using NPOI.SS.Formula.Eval; /// /// High level representation of a cell in a row of a spReadsheet. @@ -665,7 +666,78 @@ public String CellFormula SetCellFormula(value); } } + /// + /// Called when this an array formula in this cell is deleted. + /// + /// a customized exception message for the case if deletion of the cell is impossible. If null, a default message will be generated + internal void TryToDeleteArrayFormula(String message) + { + if (!IsPartOfArrayFormulaGroup) + return; + + CellRangeAddress arrayFormulaRange = ArrayFormulaRange; + if (arrayFormulaRange.NumberOfCells > 1) + { + if (message == null) + { + message = "Cell " + new CellReference(this).FormatAsString() + " is part of a multi-cell array formula. " + + "You cannot change part of an array."; + } + throw new InvalidOperationException(message); + } + //un-register the single-cell array formula from the parent sheet through public interface + Row.Sheet.RemoveArrayFormula(this); + } + public void RemoveFormula() + { + if (CellType != CellType.Formula) + { + return; + } + + if (IsPartOfArrayFormulaGroup) + { + TryToDeleteArrayFormula(null); + return; + } + NotifyFormulaChanging(); + switch (CachedFormulaResultType) + { + case CellType.Numeric: + double numericValue = ((FormulaRecordAggregate)_record).FormulaRecord.Value; + _record = new NumberRecord(); + ((NumberRecord)_record).Value = numericValue; + cellType = CellType.Numeric; + break; + case CellType.String: + _record = new NumberRecord(); + ((NumberRecord)_record).Value = 0; + cellType = CellType.String; + break; + case CellType.Boolean: + bool booleanValue = ((FormulaRecordAggregate)_record).FormulaRecord.CachedBooleanValue; + _record = new BoolErrRecord(); + ((BoolErrRecord)_record).SetValue(booleanValue); + cellType = CellType.Boolean; + break; + case CellType.Error: + byte errorValue = (byte)((FormulaRecordAggregate)_record).FormulaRecord.CachedErrorValue; + _record = new BoolErrRecord(); + try + { + ((BoolErrRecord)_record).SetValue(errorValue); + } + catch (ArgumentException) + { + ((BoolErrRecord)_record).SetValue((byte)ErrorEval.REF_INVALID.ErrorCode); + } + cellType = CellType.Error; + break; + default: + throw new InvalidOperationException(); + } + } public void SetCellFormula(String formula) { if (IsPartOfArrayFormulaGroup) diff --git a/main/SS/UserModel/Cell.cs b/main/SS/UserModel/Cell.cs index b23d5b136..4a3db1654 100644 --- a/main/SS/UserModel/Cell.cs +++ b/main/SS/UserModel/Cell.cs @@ -168,7 +168,14 @@ CellType CellType /// /// if the cell type returned by GetCellType() is not CELL_TYPE_FORMULA String CellFormula { get; set; } - + /// + /// Removes formula, if any. + /// + /// If cell was blank, leaves it as is. + /// If it is a part of an array formula group, blanks the cell. + /// If has a regular formula, removes the formula preserving the "cached" value. + /// + void RemoveFormula(); /// /// Sets formula for this cell. /// diff --git a/ooxml/XSSF/Streaming/SXSSFCell.cs b/ooxml/XSSF/Streaming/SXSSFCell.cs index 06e9c21db..e26f2849a 100644 --- a/ooxml/XSSF/Streaming/SXSSFCell.cs +++ b/ooxml/XSSF/Streaming/SXSSFCell.cs @@ -131,7 +131,38 @@ public string CellFormula ((FormulaValue)_value).Value = value; } } - + public void RemoveFormula() + { + if (CellType != CellType.Formula) + { + return; + } + switch (CachedFormulaResultType) + { + case CellType.Numeric: + double numericValue = ((NumericFormulaValue)_value).PreEvaluatedValue; + _value = new NumericValue(); + ((NumericValue)_value).Value = numericValue; + break; + case CellType.String: + String stringValue = ((StringFormulaValue)_value).PreEvaluatedValue; + _value = new PlainStringValue(); + ((PlainStringValue)_value).Value = stringValue; + break; + case CellType.Boolean: + bool booleanValue = ((BooleanFormulaValue)_value).PreEvaluatedValue; + _value = new BooleanValue(); + ((BooleanValue)_value).Value = booleanValue; + break; + case CellType.Error: + byte errorValue = (byte)((ErrorFormulaValue)_value).PreEvaluatedValue; + _value = new ErrorValue(); + ((ErrorValue)_value).Value = errorValue; + break; + default: + throw new InvalidOperationException(); + } + } public ICellStyle CellStyle { get diff --git a/ooxml/XSSF/UserModel/XSSFCell.cs b/ooxml/XSSF/UserModel/XSSFCell.cs index 826d7a647..5bee6acb9 100644 --- a/ooxml/XSSF/UserModel/XSSFCell.cs +++ b/ooxml/XSSF/UserModel/XSSFCell.cs @@ -529,6 +529,24 @@ public String CellFormula } } + public void RemoveFormula() + { + if (CellType == CellType.Blank) + return; + + if (IsPartOfArrayFormulaGroup) + { + TryToDeleteArrayFormula(null); + return; + } + ((XSSFWorkbook)_row.Sheet.Workbook).OnDeleteFormula(this); + if (_cell.IsSetF()) + { + ((XSSFSheet)_row.Sheet).OnDeleteFormula(this, null); + _cell.unsetF(); + } + } + /** * package/hierarchy use only - reuse an existing evaluation workbook if available for caching * @@ -614,15 +632,34 @@ internal void SetCellArrayFormula(String formula, CellRangeAddress range) cellFormula.t = (ST_CellFormulaType.array); cellFormula.@ref = (range.FormatAsString()); } + /// + /// Called when this an array formula in this cell is deleted. + /// + /// a customized exception message for the case if deletion of the cell is impossible. If null, a default message will be generated + internal void TryToDeleteArrayFormula(String message) + { + if (!IsPartOfArrayFormulaGroup) + return; + CellRangeAddress arrayFormulaRange = ArrayFormulaRange; + if (arrayFormulaRange.NumberOfCells > 1) + { + if (message == null) + { + message = "Cell " + new CellReference(this).FormatAsString() + " is part of a multi-cell array formula. " + + "You cannot change part of an array."; + } + throw new InvalidOperationException(message); + } + //un-register the single-cell array formula from the parent sheet through public interface + Row.Sheet.RemoveArrayFormula(this); + } private void SetFormula(String formula, FormulaType formulaType) { XSSFWorkbook wb = (XSSFWorkbook)_row.Sheet.Workbook; if (formula == null) { - ((XSSFWorkbook)wb).OnDeleteFormula(this); - if (_cell.IsSetF()) - _cell.unsetF(); + RemoveFormula(); return; } diff --git a/ooxml/XSSF/UserModel/XSSFSheet.cs b/ooxml/XSSF/UserModel/XSSFSheet.cs index e99e78f6e..994ae32d1 100644 --- a/ooxml/XSSF/UserModel/XSSFSheet.cs +++ b/ooxml/XSSF/UserModel/XSSFSheet.cs @@ -5425,7 +5425,53 @@ private ISet GetErrorTypes(CT_IgnoredError err) } return result; } + /** + * when a cell with a 'master' shared formula is removed, the next cell in the range becomes the master + * @param cell The cell that is removed + * @param evalWb BaseXSSFEvaluationWorkbook in use, if one exists + */ + internal void OnDeleteFormula(XSSFCell cell, XSSFEvaluationWorkbook evalWb) + { + CT_CellFormula f = cell.GetCTCell().f; + if (f != null && f.t == ST_CellFormulaType.shared && f.isSetRef() && f.Value != null) + { + bool breakit = false; + CellRangeAddress ref1 = CellRangeAddress.ValueOf(f.@ref); + if (ref1.NumberOfCells > 1) + { + for (int i = cell.RowIndex; i <= ref1.LastRow; i++) + { + XSSFRow row = (XSSFRow)GetRow(i); + if (row != null) + { + for (int j = cell.ColumnIndex; j <= ref1.LastColumn; j++) + { + XSSFCell nextCell = (XSSFCell)row.GetCell(j); + if (nextCell != null && nextCell != cell && nextCell.CellType == CellType.Formula) + { + CT_CellFormula nextF = nextCell.GetCTCell().f; + if (nextF.t == ST_CellFormulaType.shared && nextF.si == f.si) + { + nextF.Value = nextCell.GetCellFormula(evalWb); + CellRangeAddress nextRef = new CellRangeAddress( + nextCell.RowIndex, ref1.LastRow, + nextCell.ColumnIndex, ref1.LastColumn); + nextF.@ref=nextRef.FormatAsString(); + + sharedFormulas[(int)nextF.si]= nextF; + breakit = true; + break; + } + } + } + if (breakit) + break; + } + } + } + } + } } } From 88a5e973811e966eb82294ebc908a14cb685cc62 Mon Sep 17 00:00:00 2001 From: Tony Qu Date: Mon, 13 Jun 2022 06:24:05 +0800 Subject: [PATCH 146/159] use Uri.OriginalString instead fix #851 --- ooxml/POIXMLDocumentPart.cs | 2 +- ooxml/POIXMLFactory.cs | 2 +- ooxml/Util/PackageHelper.cs | 2 +- ooxml/XSSF/Model/ExternalLinksTable.cs | 2 +- ooxml/XSSF/UserModel/XSSFHyperlink.cs | 2 +- ooxml/XSSF/UserModel/XSSFWorkbook.cs | 2 +- ooxml/XWPF/Usermodel/XWPFDocument.cs | 2 +- testcases/ooxml/XSSF/UserModel/TestXSSFHyperlink.cs | 2 +- testcases/openxml4net/TestPackage.cs | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ooxml/POIXMLDocumentPart.cs b/ooxml/POIXMLDocumentPart.cs index 043f2816a..aebff9d42 100644 --- a/ooxml/POIXMLDocumentPart.cs +++ b/ooxml/POIXMLDocumentPart.cs @@ -319,7 +319,7 @@ public PackageRelationship GetPackageRelationship() String partName = GetPackagePart().PartName.Name; foreach (PackageRelationship rel in pkg.Relationships) { - if (rel.TargetUri.ToString().Equals(partName)) + if (rel.TargetUri.OriginalString.Equals(partName)) { return rel; } diff --git a/ooxml/POIXMLFactory.cs b/ooxml/POIXMLFactory.cs index f2bce3bc5..0beebce0d 100644 --- a/ooxml/POIXMLFactory.cs +++ b/ooxml/POIXMLFactory.cs @@ -138,7 +138,7 @@ protected PackageRelationship GetPackageRelationship(POIXMLDocumentPart parent, String partName = part.PartName.Name; foreach (PackageRelationship pr in parent.GetPackagePart().Relationships) { - String packName = pr.TargetUri.ToString();// toASCIIString(); + String packName = pr.TargetUri.OriginalString;// toASCIIString(); if (packName.Equals(partName, StringComparison.CurrentCultureIgnoreCase)) { return pr; diff --git a/ooxml/Util/PackageHelper.cs b/ooxml/Util/PackageHelper.cs index 1d1a13b28..9c3acc602 100644 --- a/ooxml/Util/PackageHelper.cs +++ b/ooxml/Util/PackageHelper.cs @@ -101,7 +101,7 @@ public string CreateTempFile() foreach (PackageRelationship rel in rels) { PackagePart p; if(rel.TargetMode == TargetMode.External){ - part_tgt.AddExternalRelationship(rel.TargetUri.ToString(), rel.RelationshipType, rel.Id); + part_tgt.AddExternalRelationship(rel.TargetUri.OriginalString, rel.RelationshipType, rel.Id); //external relations don't have associated namespace parts continue; } diff --git a/ooxml/XSSF/Model/ExternalLinksTable.cs b/ooxml/XSSF/Model/ExternalLinksTable.cs index d04744d41..990b871d9 100644 --- a/ooxml/XSSF/Model/ExternalLinksTable.cs +++ b/ooxml/XSSF/Model/ExternalLinksTable.cs @@ -107,7 +107,7 @@ public virtual String LinkedFileName PackageRelationship rel = GetPackagePart().GetRelationship(rId); if (rel != null && rel.TargetMode == TargetMode.External) { - return rel.TargetUri.ToString(); + return rel.TargetUri.OriginalString; } else { diff --git a/ooxml/XSSF/UserModel/XSSFHyperlink.cs b/ooxml/XSSF/UserModel/XSSFHyperlink.cs index 97c0db10f..63a57ee5c 100644 --- a/ooxml/XSSF/UserModel/XSSFHyperlink.cs +++ b/ooxml/XSSF/UserModel/XSSFHyperlink.cs @@ -83,7 +83,7 @@ public XSSFHyperlink(CT_Hyperlink ctHyperlink, PackageRelationship hyperlinkRel) else { Uri target = _externalRel.TargetUri; - _location = target.ToString(); + _location = target.OriginalString; if (ctHyperlink.location != null) { // URI fragment diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index 12fc10d83..6ded36602 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -625,7 +625,7 @@ public ISheet CloneSheet(int sheetNum, String newName) if (pr.TargetMode == TargetMode.External) { clonedSheet.GetPackagePart().AddExternalRelationship - (pr.TargetUri.ToString(), pr.RelationshipType, null); + (pr.TargetUri.OriginalString, pr.RelationshipType, null); } } } diff --git a/ooxml/XWPF/Usermodel/XWPFDocument.cs b/ooxml/XWPF/Usermodel/XWPFDocument.cs index 01f80717e..97a678f08 100644 --- a/ooxml/XWPF/Usermodel/XWPFDocument.cs +++ b/ooxml/XWPF/Usermodel/XWPFDocument.cs @@ -214,7 +214,7 @@ private void InitHyperlinks() while (relIter.MoveNext()) { PackageRelationship rel = relIter.Current; - hyperlinks.Add(new XWPFHyperlink(rel.Id, rel.TargetUri.ToString())); + hyperlinks.Add(new XWPFHyperlink(rel.Id, rel.TargetUri.OriginalString)); } } catch (InvalidDataException e) diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFHyperlink.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFHyperlink.cs index b884c58a5..69fabc2a7 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFHyperlink.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFHyperlink.cs @@ -245,7 +245,7 @@ private static void doTestHyperlinkContents(XSSFSheet sheet) sheet.GetRow(16).GetCell(2).Hyperlink.Type); Assert.AreEqual(null, sheet.GetRow(16).GetCell(2).Hyperlink.Label); - Assert.AreEqual("mailto:dev@poi.apache.org?subject=XSSF Hyperlinks", + Assert.AreEqual("mailto:dev@poi.apache.org?subject=XSSF%20Hyperlinks", sheet.GetRow(16).GetCell(2).Hyperlink.Address); } [Test] diff --git a/testcases/openxml4net/TestPackage.cs b/testcases/openxml4net/TestPackage.cs index 3c902ab4c..e1103da17 100644 --- a/testcases/openxml4net/TestPackage.cs +++ b/testcases/openxml4net/TestPackage.cs @@ -247,8 +247,8 @@ public void TestCreatePackageWithCoreDocument() Assert.AreEqual(1, rels.Size); rel = rels.GetRelationship(0); Assert.IsNotNull(rel); - Assert.Warn(" 'Sheet1!A1' and rel.TargetUri.Fragment should be equal."); - //Assert.AreEqual("Sheet1!A1", rel.TargetUri.Fragment); + //Assert.Warn(" 'Sheet1!A1' and rel.TargetUri.Fragment should be equal."); + Assert.AreEqual("/xl/workbook.xml#Sheet1!A1", rel.TargetUri.OriginalString); assertMSCompatibility(pkg); } From e9cf27790e23937c68e803cc21a8ede107c301c9 Mon Sep 17 00:00:00 2001 From: Emilien Duperthuy Date: Mon, 13 Jun 2022 08:47:14 +0200 Subject: [PATCH 147/159] fix IndexOutOfRangeException thrown in FormulaCellCacheEntrySet While inserting or removing an item from the cache entry set, the item hash code is used to determine the first potential cache index. But this logic could end up being a negative value, hence throwing an IndexOutOfRangeException when trying to access the array with that index. --- main/SS/Formula/FormulaCellCacheEntrySet.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/SS/Formula/FormulaCellCacheEntrySet.cs b/main/SS/Formula/FormulaCellCacheEntrySet.cs index e8fd3357f..c1ebfcb9f 100644 --- a/main/SS/Formula/FormulaCellCacheEntrySet.cs +++ b/main/SS/Formula/FormulaCellCacheEntrySet.cs @@ -92,7 +92,7 @@ public void Add(CellCacheEntry cce) private static bool AddInternal(CellCacheEntry[] arr, CellCacheEntry cce) { - int startIx = cce.GetHashCode() % arr.Length; + int startIx = Math.Abs(cce.GetHashCode() % arr.Length); for (int i = startIx; i < arr.Length; i++) { @@ -156,7 +156,7 @@ public bool Remove(CellCacheEntry cce) // else - usual case // delete single element (without re-Hashing) - int startIx = cce.GetHashCode() % arr.Length; + int startIx = Math.Abs(cce.GetHashCode() % arr.Length); // note - can't exit loops upon finding null because of potential previous deletes for (int i = startIx; i < arr.Length; i++) From 972cfb9f06da40ae4bc12344ab9b249fe38da9ed Mon Sep 17 00:00:00 2001 From: EmilienDup <50311090+EmilienDup@users.noreply.github.com> Date: Fri, 17 Jun 2022 17:34:49 +0200 Subject: [PATCH 148/159] Update OperationEvaluationContext.cs Fix Overloaded ctr to conform to https://github.com/apache/poi/blob/4b2913afd36232bd826883c20c4169098054a805/poi/src/main/java/org/apache/poi/ss/formula/OperationEvaluationContext.java#L66 --- main/SS/Formula/OperationEvaluationContext.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/main/SS/Formula/OperationEvaluationContext.cs b/main/SS/Formula/OperationEvaluationContext.cs index ba70177de..5736ca6db 100644 --- a/main/SS/Formula/OperationEvaluationContext.cs +++ b/main/SS/Formula/OperationEvaluationContext.cs @@ -28,16 +28,13 @@ public class OperationEvaluationContext private bool _isSingleValue; private WorkbookEvaluator _bookEvaluator; private bool _isInArrayContext; + public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, IEvaluationWorkbook workbook, int sheetIndex, int srcRowNum, - int srcColNum, EvaluationTracker tracker) + int srcColNum, EvaluationTracker tracker) + : this(bookEvaluator, workbook, sheetIndex, srcRowNum, srcColNum, tracker, isSingleValue: true) { - _bookEvaluator = bookEvaluator; - _workbook = workbook; - _sheetIndex = sheetIndex; - _rowIndex = srcRowNum; - _columnIndex = srcColNum; - _tracker = tracker; } + public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, IEvaluationWorkbook workbook, int sheetIndex, int srcRowNum, int srcColNum, EvaluationTracker tracker, bool isSingleValue) { @@ -578,4 +575,4 @@ private ValueEval GetExternalNameXEval(ExternalName externName, String workbookN } } } -} \ No newline at end of file +} From 2803d88df4d17251f8a27a94f55fada2feb58a49 Mon Sep 17 00:00:00 2001 From: EmilienDup <50311090+EmilienDup@users.noreply.github.com> Date: Fri, 17 Jun 2022 17:38:48 +0200 Subject: [PATCH 149/159] Update WorkbookEvaluator.cs Add conditions in `EvaluateFormula` method Following: https://github.com/apache/poi/blob/4b2913afd36232bd826883c20c4169098054a805/poi/src/main/java/org/apache/poi/ss/formula/WorkbookEvaluator.java#L438 --- main/SS/Formula/WorkbookEvaluator.cs | 78 +++++++++++++++------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/main/SS/Formula/WorkbookEvaluator.cs b/main/SS/Formula/WorkbookEvaluator.cs index 60fbe6ce0..c0ce65fa9 100644 --- a/main/SS/Formula/WorkbookEvaluator.cs +++ b/main/SS/Formula/WorkbookEvaluator.cs @@ -547,6 +547,9 @@ public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) + "): " + Arrays.ToString(ptgs).Replace("\\Qorg.apache.poi.ss.formula.ptg.\\E", "")); dbgEvaluationOutputIndent++; } + + IEvaluationSheet evalSheet = ec.GetWorkbook().GetSheet(ec.SheetIndex); + IEvaluationCell evaluationCell = evalSheet.GetCell(ec.RowIndex, ec.ColumnIndex); Stack stack = new Stack(); for (int i = 0, iSize = ptgs.Length; i < iSize; i++) @@ -600,45 +603,48 @@ public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) } if (attrPtg.IsOptimizedIf) { - ValueEval arg0 = stack.Pop(); - bool evaluatedPredicate; - try + if (!evaluationCell.IsPartOfArrayFormulaGroup) { - evaluatedPredicate = IfFunc.EvaluateFirstArg(arg0, ec.RowIndex, ec.ColumnIndex); - } - catch (EvaluationException e) - { - stack.Push(e.GetErrorEval()); - int dist = attrPtg.Data; - i += CountTokensToBeSkipped(ptgs, i, dist); - attrPtg = (AttrPtg)ptgs[i]; - dist = attrPtg.Data + 1; - i += CountTokensToBeSkipped(ptgs, i, dist); - continue; - } - if (evaluatedPredicate) - { - // nothing to skip - true param folows - } - else - { - int dist = attrPtg.Data; - i += CountTokensToBeSkipped(ptgs, i, dist); - Ptg nextPtg = ptgs[i + 1]; - if (ptgs[i] is AttrPtg && nextPtg is FuncVarPtg && - // in order to verify that there is no third param, we need to check - // if we really have the IF next or some other FuncVarPtg as third param, e.g. ROW()/COLUMN()! - ((FuncVarPtg)nextPtg).FunctionIndex == FunctionMetadataRegistry.FUNCTION_INDEX_IF) - { - // this is an if statement without a false param (as opposed to MissingArgPtg as the false param) - i++; - stack.Push(arg0); - stack.Push(BoolEval.FALSE); - } + ValueEval arg0 = stack.Pop(); + bool evaluatedPredicate; + try + { + evaluatedPredicate = IfFunc.EvaluateFirstArg(arg0, ec.RowIndex, ec.ColumnIndex); + } + catch (EvaluationException e) + { + stack.Push(e.GetErrorEval()); + int dist = attrPtg.Data; + i += CountTokensToBeSkipped(ptgs, i, dist); + attrPtg = (AttrPtg)ptgs[i]; + dist = attrPtg.Data + 1; + i += CountTokensToBeSkipped(ptgs, i, dist); + continue; + } + if (evaluatedPredicate) + { + // nothing to skip - true param folows + } + else + { + int dist = attrPtg.Data; + i += CountTokensToBeSkipped(ptgs, i, dist); + Ptg nextPtg = ptgs[i + 1]; + if (ptgs[i] is AttrPtg && nextPtg is FuncVarPtg && + // in order to verify that there is no third param, we need to check + // if we really have the IF next or some other FuncVarPtg as third param, e.g. ROW()/COLUMN()! + ((FuncVarPtg)nextPtg).FunctionIndex == FunctionMetadataRegistry.FUNCTION_INDEX_IF) + { + // this is an if statement without a false param (as opposed to MissingArgPtg as the false param) + i++; + stack.Push(arg0); + stack.Push(BoolEval.FALSE); + } + } } continue; } - if (attrPtg.IsSkip) + if (attrPtg.IsSkip && !evaluationCell.IsPartOfArrayFormulaGroup) { int dist = attrPtg.Data + 1; i += CountTokensToBeSkipped(ptgs, i, dist); @@ -1053,4 +1059,4 @@ public bool DebugEvaluationOutputForNextEval set { dbgEvaluationOutputForNextEval = value; } } } -} \ No newline at end of file +} From 2ffd3dbea157ec88b0a48d6f7821b932d47978a3 Mon Sep 17 00:00:00 2001 From: Maksym Koshovyi Date: Wed, 29 Jun 2022 18:28:18 +0300 Subject: [PATCH 150/159] Update DateUtil.cs --- main/SS/UserModel/DateUtil.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main/SS/UserModel/DateUtil.cs b/main/SS/UserModel/DateUtil.cs index 5cccbc62e..a9b0a3580 100644 --- a/main/SS/UserModel/DateUtil.cs +++ b/main/SS/UserModel/DateUtil.cs @@ -883,7 +883,9 @@ public static bool IsCellInternalDateFormatted(ICell cell) public static bool IsValidExcelDate(double value) { //return true; - return value > -Double.Epsilon; + + const int maxDate = 2958465; // 31/12/9999 + return value > -Double.Epsilon && value <= maxDate; } } From 28197d5ca6441a23d4d935f7925c3a05d38742e8 Mon Sep 17 00:00:00 2001 From: Artem Kolosov Date: Wed, 8 Jun 2022 10:25:33 +0700 Subject: [PATCH 151/159] Upstream fix: Complete CT_CustomFilters and CT_CustomFilter --- OpenXmlFormats/Spreadsheet/AutoFilter.cs | 122 +++++++++++++++++++---- ooxml/XSSF/Model/CommentsTable.cs | 2 +- 2 files changed, 101 insertions(+), 23 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/AutoFilter.cs b/OpenXmlFormats/Spreadsheet/AutoFilter.cs index 648d2fe82..298141f0b 100644 --- a/OpenXmlFormats/Spreadsheet/AutoFilter.cs +++ b/OpenXmlFormats/Spreadsheet/AutoFilter.cs @@ -140,23 +140,14 @@ public class CT_FilterColumn private bool showButtonField; + private CT_CustomFilters customFiltersField; + public CT_FilterColumn() { this.hiddenButtonField = false; this.showButtonField = true; } - //[XmlAttribute] - //public object Item - //{ - // get - // { - // return this.itemField; - // } - // set - // { - // this.itemField = value; - // } - //} + [XmlAttribute] public uint colId { @@ -196,6 +187,19 @@ public bool showButton } } + [XmlAttribute] + public CT_CustomFilters customFilters + { + get + { + return this.customFiltersField; + } + set + { + this.customFiltersField = value; + } + } + public static CT_FilterColumn Parse(XmlNode node, XmlNamespaceManager namespaceManager) { if (node == null) @@ -204,12 +208,12 @@ public static CT_FilterColumn Parse(XmlNode node, XmlNamespaceManager namespaceM ctObj.colId = XmlHelper.ReadUInt(node.Attributes["colId"]); ctObj.hiddenButton = XmlHelper.ReadBool(node.Attributes["hiddenButton"]); ctObj.showButton = XmlHelper.ReadBool(node.Attributes["showButton"]); - //TODO: implement http://www.schemacentral.com/sc/ooxml/t-ssml_CT_FilterColumn.html - //foreach (XmlNode childNode in node.ChildNodes) - //{ - // if (childNode.LocalName == "Item") - // ctObj.Item = new Object(); - //} + + foreach (XmlNode childNode in node.ChildNodes) + { + if (childNode.LocalName == "customFilters") + ctObj.customFilters = CT_CustomFilters.Parse(childNode, namespaceManager); + } return ctObj; } @@ -219,10 +223,23 @@ internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); XmlHelper.WriteAttribute(sw, "colId", this.colId, true); - XmlHelper.WriteAttribute(sw, "hiddenButton", this.hiddenButton); - XmlHelper.WriteAttribute(sw, "showButton", this.showButton); - sw.Write(">"); - sw.Write(string.Format("", nodeName)); + XmlHelper.WriteAttribute(sw, "hiddenButton", this.hiddenButton, false); + XmlHelper.WriteAttribute(sw, "showButton", this.showButton, false); + + if (this.customFilters == null || this.customFilters.customFilter.Count == 0) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.customFilters != null && this.customFilters.customFilter.Count > 0) + { + if (this.customFilters != null) + this.customFilters.Write(sw, "customFilters"); + } + sw.Write(string.Format("", nodeName)); + } } } @@ -317,6 +334,46 @@ public bool and this.andField = value; } } + + public static CT_CustomFilters Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + if (node == null) + return null; + CT_CustomFilters ctObj = new CT_CustomFilters(); + if (node.Attributes["and"] != null) + ctObj.and = XmlHelper.ReadBool(node.Attributes["and"]); + + ctObj.customFilterField = new List(); + foreach (XmlNode childNode in node.ChildNodes) + { + if (childNode.LocalName == "customFilter") + ctObj.customFilter.Add(CT_CustomFilter.Parse(childNode, namespaceManager)); + } + return ctObj; + } + + internal void Write(StreamWriter sw, string nodeName) + { + sw.Write(string.Format("<{0}", nodeName)); + XmlHelper.WriteAttribute(sw, "and", this.and, false); + + if (this.customFilter == null || this.customFilter.Count == 0) + { + sw.Write("/>"); + } + else + { + sw.Write(">"); + if (this.customFilter != null && this.customFilter.Count > 0) + { + foreach (CT_CustomFilter x in this.customFilter) + { + x.Write(sw, "customFilter"); + } + } + sw.Write(string.Format("", nodeName)); + } + } } public class CT_CustomFilter @@ -355,6 +412,27 @@ public string val this.valField = value; } } + + public static CT_CustomFilter Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + if (node == null) + return null; + CT_CustomFilter ctObj = new CT_CustomFilter(); + if (node.Attributes["operator"] != null + && Enum.TryParse(XmlHelper.ReadString(node.Attributes["operator"]), out ST_FilterOperator _operator)) + ctObj.@operator = _operator; + if (node.Attributes["val"] != null) + ctObj.val = XmlHelper.ReadString(node.Attributes["val"]); + return ctObj; + } + + internal void Write(StreamWriter sw, string nodeName) + { + sw.Write(string.Format("<{0}", nodeName)); + XmlHelper.WriteAttribute(sw, "operator", this.@operator.ToString(), true); + XmlHelper.WriteAttribute(sw, "val", this.val, true); + sw.Write("/>"); + } } public enum ST_FilterOperator diff --git a/ooxml/XSSF/Model/CommentsTable.cs b/ooxml/XSSF/Model/CommentsTable.cs index f17903552..de1fd6640 100644 --- a/ooxml/XSSF/Model/CommentsTable.cs +++ b/ooxml/XSSF/Model/CommentsTable.cs @@ -24,7 +24,7 @@ namespace NPOI.XSSF.Model using NPOI.SS.UserModel; using NPOI.SS.Util; using NPOI.XSSF.UserModel; - using OpenXmlFormats.Spreadsheet; + using NPOI.OpenXmlFormats.Spreadsheet; public class CommentsTable : POIXMLDocumentPart { From 8b5ac6af3036a6aba8079419b16e08d452687026 Mon Sep 17 00:00:00 2001 From: Artem Kolosov Date: Thu, 9 Jun 2022 15:31:42 +0700 Subject: [PATCH 152/159] Upstream fix: added proper namespaces to pivot tables Some namespaces were missing in pivot table related objects. --- .../PivotTable/CT_PivotCacheDefinition.cs | 11 +++++++---- .../Spreadsheet/PivotTable/CT_PivotCacheRecords.cs | 3 +++ .../PivotTable/CT_PivotTableDefinition.cs | 7 +++++-- OpenXmlFormats/Spreadsheet/Workbook/CT_Workbook.cs | 13 +++++++++---- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs index a85703c0f..3091e1d14 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheDefinition.cs @@ -89,6 +89,9 @@ internal void Write(StreamWriter sw) sw.Write(""); } } @@ -3529,7 +3532,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "st", this.st, false); XmlHelper.WriteAttribute(sw, "b", this.b, false); - if (this.tpls == null && (this.x == null || this.x.Count == 0)) + if ((this.tpls == null || this.tpls.tpl.Count == 0) && (this.x == null || this.x.Count == 0)) { sw.Write("/>"); } @@ -4451,7 +4454,7 @@ public static CT_Number Parse(XmlNode node, XmlNamespaceManager namespaceManager internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "v", this.v); + XmlHelper.WriteAttribute(sw, "v", this.v, true); XmlHelper.WriteAttribute(sw, "u", this.u, false); XmlHelper.WriteAttribute(sw, "f", this.f, false); XmlHelper.WriteAttribute(sw, "c", this.c, false); @@ -4821,7 +4824,7 @@ public static CT_String Parse(XmlNode node, XmlNamespaceManager namespaceManager internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "v", this.v); + XmlHelper.WriteAttribute(sw, "v", this.v, true); XmlHelper.WriteAttribute(sw, "u", this.u, false); XmlHelper.WriteAttribute(sw, "f", this.f, false); XmlHelper.WriteAttribute(sw, "c", this.c, false); diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs index 64c2628f9..1c45a507e 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotCacheRecords.cs @@ -40,6 +40,9 @@ internal void Write(StreamWriter sw) sw.Write(""); if (this.extLst != null) diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs index 27e24c149..162946620 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs @@ -193,6 +193,9 @@ internal void Write(StreamWriter sw) sw.Write(""); } @@ -7048,7 +7051,7 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "evalOrder", this.evalOrder, false); XmlHelper.WriteAttribute(sw, "id", this.id, true); XmlHelper.WriteAttribute(sw, "iMeasureHier", this.iMeasureHier, false); - XmlHelper.WriteAttribute(sw, "iMeasureFld", this.iMeasureFld, false); + XmlHelper.WriteAttribute(sw, "iMeasureFld", this.iMeasureFld, true); XmlHelper.WriteAttribute(sw, "name", this.name, false); XmlHelper.WriteAttribute(sw, "description", this.description, false); XmlHelper.WriteAttribute(sw, "stringValue1", this.stringValue1, false); diff --git a/OpenXmlFormats/Spreadsheet/Workbook/CT_Workbook.cs b/OpenXmlFormats/Spreadsheet/Workbook/CT_Workbook.cs index bae040809..55f7720ef 100644 --- a/OpenXmlFormats/Spreadsheet/Workbook/CT_Workbook.cs +++ b/OpenXmlFormats/Spreadsheet/Workbook/CT_Workbook.cs @@ -113,10 +113,15 @@ public static CT_Workbook Parse(XmlNode node, XmlNamespaceManager namespaceManag internal void Write(StreamWriter sw) { sw.Write(""); - sw.Write(""); + sw.Write(""); if (this.fileVersion != null) this.fileVersion.Write(sw, "fileVersion"); if (this.fileSharing != null) From bf129fc8a26f0a793bd7c2d0d73c150363f6e964 Mon Sep 17 00:00:00 2001 From: Artem Koloskov Date: Fri, 17 Jun 2022 12:32:06 +0700 Subject: [PATCH 153/159] Upstream-fix: Pivot area serialization fix Pivot area property was serializing wrong, omiting "field" property when it has value of 0, which causes excel to repair the pivot tables that contain this object. Added WriteAttribute reload for string values, to accept a default string in method call. --- .../Spreadsheet/PivotTable/CT_PivotTableDefinition.cs | 6 +++--- OpenXmlFormats/Spreadsheet/Sheet.cs | 4 ++-- openxml4Net/Util/XmlHelper.cs | 6 +++++- testcases/main/NPOI.TestCases.csproj | 3 +++ testcases/ooxml/NPOI.OOXML.TestCases.csproj | 3 +++ testcases/openxml4net/NPOI.OOXML4Net.Testcases.csproj | 3 +++ 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs index 162946620..bd118404a 100644 --- a/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs +++ b/OpenXmlFormats/Spreadsheet/PivotTable/CT_PivotTableDefinition.cs @@ -5080,9 +5080,9 @@ public static CT_Format Parse(XmlNode node, XmlNamespaceManager namespaceManager internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "action", this.action.ToString()); + XmlHelper.WriteAttribute(sw, "action", this.action.ToString(), false, ST_FormatAction.formatting.ToString()); XmlHelper.WriteAttribute(sw, "dxfId", this.dxfId); - if (this.pivotArea == null && this.extLst == null) + if (this.pivotArea == null && (this.extLst == null || this.extLst.ext.Count == 0)) { sw.Write("/>"); } @@ -5091,7 +5091,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(">"); if (this.pivotArea != null) this.pivotArea.Write(sw, "pivotArea"); - if (this.extLst != null) + if (this.extLst != null && this.extLst.ext.Count != 0) this.extLst.Write(sw, "extLst"); sw.Write(string.Format("", nodeName)); } diff --git a/OpenXmlFormats/Spreadsheet/Sheet.cs b/OpenXmlFormats/Spreadsheet/Sheet.cs index 0463c3579..8e4b8f0b8 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet.cs @@ -1673,7 +1673,7 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(">"); if (this.references != null) this.references.Write(sw, "references"); - if (this.extLst != null) + if (this.extLst != null && this.extLst.ext.Count != 0) this.extLst.Write(sw, "extLst"); sw.Write(string.Format("", nodeName)); } @@ -2059,7 +2059,7 @@ public static CT_PivotAreaReference Parse(XmlNode node, XmlNamespaceManager name internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); - XmlHelper.WriteAttribute(sw, "field", this.field); + XmlHelper.WriteAttribute(sw, "field", this.field, true); XmlHelper.WriteAttribute(sw, "count", this.count); XmlHelper.WriteAttribute(sw, "selected", this.selected, false, true); XmlHelper.WriteAttribute(sw, "byPosition", this.byPosition, false); diff --git a/openxml4Net/Util/XmlHelper.cs b/openxml4Net/Util/XmlHelper.cs index 0249cb2e0..c18f12cc8 100644 --- a/openxml4Net/Util/XmlHelper.cs +++ b/openxml4Net/Util/XmlHelper.cs @@ -356,7 +356,11 @@ public static void WriteAttribute(StreamWriter sw, string attributeName, string } public static void WriteAttribute(StreamWriter sw, string attributeName, string value, bool writeIfBlank) { - if (string.IsNullOrEmpty(value) && !writeIfBlank) + WriteAttribute(sw, attributeName, value, writeIfBlank, string.Empty); + } + public static void WriteAttribute(StreamWriter sw, string attributeName, string value, bool writeIfBlank, string defaultValue) + { + if ((string.IsNullOrEmpty(value) || defaultValue.Equals(value)) && !writeIfBlank) return; sw.Write(string.Format(" {0}=\"{1}\"", attributeName, value == null ? string.Empty : EncodeXml(value))); } diff --git a/testcases/main/NPOI.TestCases.csproj b/testcases/main/NPOI.TestCases.csproj index f67583a1c..fb70238e5 100644 --- a/testcases/main/NPOI.TestCases.csproj +++ b/testcases/main/NPOI.TestCases.csproj @@ -719,6 +719,9 @@ 3.13.1 + + 4.2.1 + 1.3.3 diff --git a/testcases/ooxml/NPOI.OOXML.TestCases.csproj b/testcases/ooxml/NPOI.OOXML.TestCases.csproj index de4f84064..29cc244cf 100644 --- a/testcases/ooxml/NPOI.OOXML.TestCases.csproj +++ b/testcases/ooxml/NPOI.OOXML.TestCases.csproj @@ -310,6 +310,9 @@ 3.13.1 + + 4.2.1 + 1.8.9 diff --git a/testcases/openxml4net/NPOI.OOXML4Net.Testcases.csproj b/testcases/openxml4net/NPOI.OOXML4Net.Testcases.csproj index 68d931e2d..5e87f75b7 100644 --- a/testcases/openxml4net/NPOI.OOXML4Net.Testcases.csproj +++ b/testcases/openxml4net/NPOI.OOXML4Net.Testcases.csproj @@ -162,6 +162,9 @@ 3.13.1 + + 4.2.1 +