Skip to content

Commit

Permalink
Merge pull request #1329 from artemkoloskov/xssf-column
Browse files Browse the repository at this point in the history
XSSFColumn class implementation
  • Loading branch information
tonyqus authored Nov 8, 2024
2 parents da4b372 + 3a1724e commit ce6a2d6
Show file tree
Hide file tree
Showing 51 changed files with 5,503 additions and 862 deletions.
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

0 comments on commit ce6a2d6

Please sign in to comment.