Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XSSFColumn class implementation #1329

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8a04e66
Make XSSFCell.ColumnIndex Internally settable.
Nov 17, 2022
b2dee1f
Implement column shifting in FormulaShifter class.
Nov 22, 2022
2221299
Add copyColumnWidth flag to CellCopyPolicy class.
Nov 22, 2022
8df0997
Add IColumn Interface
Nov 7, 2022
f632a60
Implement CellUtil.CopyCell overload that accepts IColoumn as argument.
Nov 22, 2022
b64889a
Add UpdateColumnFormulas and ShiftFormula methods to XSSFRowColShift…
Nov 22, 2022
542230d
Implement XSSFColumn class.
Nov 22, 2022
73ce1ba
Add ColumShifter and XSSFColumnShifter calsses to mange column shifting.
Nov 22, 2022
aafdcc7
Make XSSFRow.RemoveCell method check for possibility for cell to be i…
Nov 22, 2022
b972a28
Add unit tests for XSSFSheet column functionalty and for XSSFColumn i…
Nov 22, 2022
9ad741c
Makes CT_Cols.Parse method break up CT_Col with non-equal min and max…
Nov 30, 2022
d0b4439
Remove nullable from come of the bool properties of IColumn
Nov 30, 2022
1834cf7
Remove XSSFRow.CheckColumnAndRemove private method
Dec 1, 2022
bf86e56
Change default values of new CT_Col.collapsedField and CT_Col.collaps…
Dec 1, 2022
34873ec
Change the way XSSFColumn accesses cells in it
Dec 1, 2022
37f3dc1
Refactor XSSFSheet methods that used ColumnHelper class
Dec 2, 2022
7fb5b75
Update unit tests to support the new column paradigm
Dec 2, 2022
e2ae1d0
Add XSSFSheet.UngroupColumn tests.
Dec 2, 2022
1275c92
apply editorconfig to new files of the branch #upstr
Jun 27, 2023
c8bd934
upstr: Fix TestSetColumnGroupCollapsed test to correctly anticipate t…
Jun 27, 2023
6ef2b69
upstr: Fix CT_Col.Parse method to parse values of customWidth, width …
Jun 27, 2023
e85bdc8
upstr: Implement combining CT_Col objects by their adjacency and simi…
Jun 27, 2023
b49dd97
upstr: Obsolete ColumnHelper class
Jun 27, 2023
68931fc
upstr: comment and clean up for XSSFColumn
Jun 27, 2023
3bb1df7
upstr: update tests for XSSFSheet.ShiftRows and XSSFSheet.ShiftColumns
Jun 30, 2023
79a4395
upstr: fix bug in ColumnShifter with wrong addresses created to check…
Jun 30, 2023
c56a1b9
upstr: fix bug in RowShifter.ShiftMergedRegions with lastCol variable
Jun 30, 2023
203d7a3
upstr: fix bug in XSSFSheet.AddMergedRegion which returned incorrect …
Jun 30, 2023
7997433
upstr: fix potentioal Sequence contained no elements exception in Row…
Jun 30, 2023
3e6ab4b
upstr: fix the way ColumnShifter and RowShifter determine overwritten…
Jun 30, 2023
7d6ee42
upstr: modify shiftcolumn and shifrrow tests for clarity.
Jun 30, 2023
a77954b
upstr: refactor XSSFSheet RemoveOverwrittenColumns to use RemoveColumn
Jun 30, 2023
fa007c5
upstr: fix XSSFSheet.RemoveOverwrittenColumns
Jul 3, 2023
50a563d
upstr: enable invariant culture in tests where it is necessary
Jul 3, 2023
eadb46b
upstr: make XSSFSheet.DestroyColumn mehtods public
Jul 4, 2023
cba8a93
upstr: set invariant culture for two tests in TestNormSInv
Jul 4, 2023
be4de3a
upstr: fix bug with column shifting
Jul 4, 2023
90bea62
Upstream feature: prevent XSSFSheet from intializing with maximum num…
Jan 11, 2024
b186b30
Upstream feature: fix the way last column is decided in `CT_Cols.Brea…
Jan 17, 2024
5119eae
upstream fix: Add IEnumerable<IRow> implementation to ISheet and its …
Feb 8, 2024
6f7bd88
upstream-fix: revert to original column width calculation in GetColum…
Feb 9, 2024
fe02e5c
Merge branch 'nissl-lab:master' into xssfcolumn-to-upstream
artem-iron Feb 9, 2024
e2cb672
merge from fork
artemkoloskov May 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 75 additions & 10 deletions OpenXmlFormats/Spreadsheet/Sheet/CT_Col.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public class CT_Col

private byte outlineLevelField;

private bool collapsedField = true;
private bool collapsedSpecifiedField = true;
private bool collapsedField = false;
private bool collapsedSpecifiedField = false;

[XmlAttribute]
public uint min
Expand Down Expand Up @@ -279,17 +279,17 @@ public static CT_Col Parse(XmlNode node, XmlNamespaceManager namespaceManager)
CT_Col ctObj = new CT_Col();
ctObj.min = XmlHelper.ReadUInt(node.Attributes["min"]);
ctObj.max = XmlHelper.ReadUInt(node.Attributes["max"]);
ctObj.width = XmlHelper.ReadDouble(node.Attributes["width"]);
ctObj.widthField = XmlHelper.ReadDouble(node.Attributes["width"]);
if (node.Attributes["style"] != null)
ctObj.style = XmlHelper.ReadUInt(node.Attributes["style"]);
else
ctObj.style = null;
ctObj.hidden = XmlHelper.ReadBool(node.Attributes["hidden"]);
ctObj.bestFit = XmlHelper.ReadBool(node.Attributes["bestFit"]);
ctObj.outlineLevel = XmlHelper.ReadByte(node.Attributes["outlineLevel"]);
ctObj.customWidth = XmlHelper.ReadBool(node.Attributes["customWidth"]);
ctObj.customWidthField = XmlHelper.ReadBool(node.Attributes["customWidth"]);
ctObj.phonetic = XmlHelper.ReadBool(node.Attributes["phonetic"]);
ctObj.collapsed = XmlHelper.ReadBool(node.Attributes["collapsed"]);
ctObj.collapsedField = XmlHelper.ReadBool(node.Attributes["collapsed"]);
return ctObj;
}

Expand Down Expand Up @@ -347,14 +347,79 @@ public CT_Col Copy()
return col;
}

/// <summary>
/// Checks if <paramref name="col"/> is adjacent or intersecting with
/// current column and can be combined with it. If that is the case -
/// will modify current <see cref="CT_Col"/> to have <see cref="min"/>
/// and <see cref="max"/> of both columns.
/// </summary>
/// <param name="col"><see cref="CT_Col"/> to combine with</param>
/// <exception cref="InvalidOperationException">Thrown if
/// <paramref name="col"/> cannot be combined with current
/// <see cref="CT_Col"/></exception>
public void CombineWith(CT_Col col)
{
if(!IsAdjacentAndCanBeCombined(col))
{
throw new InvalidOperationException($"Columns [{this}] and " +
$"[{col}] could not be combined");
}

min = Math.Min(min, col.min);
max = Math.Max(max, col.max);
}

/// <summary>
/// Checks if <paramref name="col"/> is adjacent or intersecting with
/// current column and can be combined with it.
/// </summary>
/// <param name="col"> <see cref="CT_Col"/> to check</param>
/// <returns><c>true</c> if <paramref name="col"/> is adjacent or
/// intersecting and have equal properties with current
/// <see cref="CT_Col"/>; <c>false</c> otherwise</returns>
public bool IsAdjacentAndCanBeCombined(CT_Col col)
{
return IsAdjacentOrIntersecting(col) && HasEqualProperties(col);
}

private bool HasEqualProperties(CT_Col col)
{
return bestFitField == col.bestFitField
&& collapsedField == col.collapsedField
&& collapsedSpecifiedField == col.collapsedSpecifiedField
&& customWidthField == col.customWidthField
&& hiddenField == col.hiddenField
&& outlineLevelField == col.outlineLevelField
&& phoneticField == col.phoneticField
&& styleField == col.styleField
&& widthField == col.widthField
&& widthSpecifiedField == col.widthSpecifiedField;
}

private bool IsAdjacentOrIntersecting(CT_Col col)
{
return IsAdjacent(col) || IsIntersecting(col);
}

private bool IsAdjacent(CT_Col col)
{
return min == col.max + 1 || max == col.min - 1;
}

private bool IsIntersecting(CT_Col col)
{
return (min >= col.min && min <= col.max) || (max <= col.max && max >= col.min) ||
(col.min >= min && col.min <= max) || (col.max <= max && col.max >= min);
}

public override bool Equals(object obj)
{
if (obj == null)
return false;
if (!(obj is CT_Col))
if (obj is not CT_Col col)
{
return false;
CT_Col col = obj as CT_Col;
return col.min == this.min && col.max == this.max;
}

return col.min == min && col.max == max && HasEqualProperties(col);
}

public override int GetHashCode()
Expand Down
108 changes: 94 additions & 14 deletions OpenXmlFormats/Spreadsheet/Sheet/CT_Cols.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System;
using NPOI.SS;
using System;
using System.Collections.Generic;

using System.Text;
using System.Xml.Serialization;
using System.Xml;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace NPOI.OpenXmlFormats.Spreadsheet
{
Expand Down Expand Up @@ -53,10 +52,14 @@ public void RemoveCol(int index)
}
public void RemoveCols(IList<CT_Col> toRemove)
{
if (colField == null) return;
if (colField == null)
{
return;
}

foreach (CT_Col c in toRemove)
{
colField.Remove(c);
_ = colField.Remove(c);
}
}
public int sizeOfColArray()
Expand All @@ -68,7 +71,6 @@ public CT_Col GetColArray(int index)
return colField[index];
}


public List<CT_Col> GetColList()
{
return colField;
Expand All @@ -86,37 +88,115 @@ public List<CT_Col> col
}
}

public static CT_Cols Parse(XmlNode node, XmlNamespaceManager namespaceManager)
public static CT_Cols Parse(XmlNode node, XmlNamespaceManager namespaceManager, int lastColumn)
{
if (node == null)
{
return null;
CT_Cols ctObj = new CT_Cols();
ctObj.col = new List<CT_Col>();
}

CT_Cols ctObj = new CT_Cols
{
col = new List<CT_Col>()
};
foreach (XmlNode childNode in node.ChildNodes)
{
if (childNode.LocalName == "col")
ctObj.col.Add(CT_Col.Parse(childNode, namespaceManager));
{
CT_Col ctCol = CT_Col.Parse(childNode, namespaceManager);

if (ctCol.min != ctCol.max)
{
BreakUpCtCol(ctObj, ctCol, lastColumn);
}
else
{
ctObj.col.Add(ctCol);
}
}
}

return ctObj;
}

/// <summary>
/// For ease of use of columns in NPOI break up <see cref="CT_Col"/>s
/// that span over multiple physical columns into individual
/// <see cref="CT_Col"/>s for each physical column.
/// </summary>
/// <param name="ctObj"></param>
/// <param name="ctCol"></param>
private static void BreakUpCtCol(CT_Cols ctObj, CT_Col ctCol, int lastColumn)
{
int max = ctCol.max >= SpreadsheetVersion.EXCEL2007.LastColumnIndex - 1
? lastColumn
: (int)ctCol.max;

for (int i = (int)ctCol.min; i <= max; i++)
{
CT_Col breakOffCtCol = ctCol.Copy();
breakOffCtCol.min = (uint)i;
breakOffCtCol.max = (uint)i;

ctObj.col.Add(breakOffCtCol);
}
}

internal void Write(StreamWriter sw, string nodeName)
{
List<CT_Col> combinedCols = CombineCols(col);

sw.Write(string.Format("<{0}", nodeName));
sw.Write(">");
if (this.col != null)

if (combinedCols != null)
{
foreach (CT_Col x in this.col)
foreach (CT_Col x in combinedCols)
{
x.Write(sw, "col");
}
}

sw.Write(string.Format("</{0}>", nodeName));
}

/// <summary>
/// Broken up by the <see cref="BreakUpCtCol(CT_Cols, CT_Col)"/> method
/// <see cref="CT_Col"/>s are combined into <see cref="CT_Col"/> spans
/// </summary>
private static List<CT_Col> CombineCols(List<CT_Col> cols)
{
List<CT_Col> combinedCols = new List<CT_Col>();

cols.Sort((c1, c2) => c1.min.CompareTo(c2.min));

CT_Col lastCol = null;

foreach (CT_Col col in cols)
{
if (lastCol == null)
{
lastCol = col;
continue;
}

if (col.IsAdjacentAndCanBeCombined(lastCol))
{
lastCol.CombineWith(col);
continue;
}

combinedCols.Add(lastCol);
lastCol = col;
}

if (lastCol != null)
{
combinedCols.Add(lastCol);
}

return combinedCols;
}

public void SetColArray(CT_Col[] colArray)
{
Expand Down
33 changes: 32 additions & 1 deletion OpenXmlFormats/Spreadsheet/Sheet/CT_Row.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Xml;
using NPOI.OpenXml4Net.Util;
using System.IO;
using NPOI.SS.Util;

namespace NPOI.OpenXmlFormats.Spreadsheet
{
Expand Down Expand Up @@ -45,6 +46,8 @@ public class CT_Row
private bool phField;
private double dyDescentField; //x14ac:dyDescent

private int lastCellField = -1;

public static CT_Row Parse(XmlNode node, XmlNamespaceManager namespaceManager)
{
if (node == null)
Expand All @@ -68,9 +71,26 @@ public static CT_Row Parse(XmlNode node, XmlNamespaceManager namespaceManager)
foreach (XmlNode childNode in node.ChildNodes)
{
if (childNode.LocalName == "extLst")
{
ctObj.extLst = CT_ExtensionList.Parse(childNode, namespaceManager);
}
else if (childNode.LocalName == "c")
ctObj.c.Add(CT_Cell.Parse(childNode, namespaceManager));
{
CT_Cell cell = CT_Cell.Parse(childNode, namespaceManager);
ctObj.c.Add(cell);

if (cell.r == null)
{
continue;
}

var cellNum = new CellReference(cell.r).Col;

if (cellNum > ctObj.lastCellField)
{
ctObj.lastCellField = cellNum;
}
}
}
return ctObj;
}
Expand Down Expand Up @@ -383,6 +403,17 @@ public bool ph
this.phField = value;
}
}

/// <summary>
/// An index of the last non-empty cell in the row
/// </summary>
public int lastCell
{
get
{
return this.lastCellField;
}
}
}

}
Loading
Loading