Skip to content

Commit

Permalink
#946. #1167, #1058 LoadFromCollection, support for sortorder attribut…
Browse files Browse the repository at this point in the history
…e on nested classes
  • Loading branch information
swmal committed Nov 21, 2023
1 parent 4514d04 commit d03d1b3
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 6 deletions.
4 changes: 3 additions & 1 deletion src/EPPlus/LoadFunctions/LoadFromCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ public LoadFromCollection(ExcelRangeBase range, IEnumerable<T> items, LoadFromCo
ShowTotal = tableAttr.ShowTotal;
}
var classSortOrderAttr = type.GetFirstAttributeOfType<EPPlusTableColumnSortOrderAttribute>();
if(classSortOrderAttr != null && classSortOrderAttr.Properties != null && classSortOrderAttr.Properties.Length > 0)
if (classSortOrderAttr != null && classSortOrderAttr.Properties != null && classSortOrderAttr.Properties.Length > 0)
{
SortOrderProperties = classSortOrderAttr.Properties.ToList();
var scanner = new NestedColumnsSortorderScanner(type, parameters.BindingFlags);
SortOrderProperties = scanner.GetSortOrder();
}
LoadFromCollectionColumns<T> cols;
if (parameters.Members == null)
Expand Down
117 changes: 117 additions & 0 deletions src/EPPlus/LoadFunctions/NestedColumnsSortorderScanner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*************************************************************************************************
Required Notice: Copyright (C) EPPlus Software AB.
This software is licensed under PolyForm Noncommercial License 1.0.0
and may only be used for noncommercial purposes
https://polyformproject.org/licenses/noncommercial/1.0.0/
A commercial license to use this software can be purchased at https://epplussoftware.com
*************************************************************************************************
Date Author Change
*************************************************************************************************
10/21/2023 EPPlus Software AB EPPlus 7.0.2
*************************************************************************************************/
using OfficeOpenXml.Attributes;
using OfficeOpenXml.Core.CellStore;
using OfficeOpenXml.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace OfficeOpenXml.LoadFunctions
{
internal class NestedColumnsSortorderScanner
{
public NestedColumnsSortorderScanner(Type outerType, BindingFlags bindingFlags)
{
_sortOrder = new List<string>();
_bindingFlags = bindingFlags;
ReadTypes(outerType);
}

private readonly List<string> _sortOrder = new List<string>();
private readonly BindingFlags _bindingFlags;

private Type GetMemberType(MemberInfo member, string path)
{
if (member.MemberType == MemberTypes.Property)
{
return ((PropertyInfo)member).PropertyType;
}
else if (member.MemberType == MemberTypes.Field)
{
return ((FieldInfo)member).FieldType;
}
else if (member.MemberType == MemberTypes.Method)
{
return ((MethodInfo)member).ReturnType;
}
else
{
throw new InvalidCastException($"Member {path} must be either Property, Field or Method but is {member.MemberType} which is not supported.");
}
}

private string GetMemberName(MemberInfo member)
{
if (member.MemberType == MemberTypes.Property)
{
return ((PropertyInfo)member).Name;
}
else if (member.MemberType == MemberTypes.Field)
{
return ((FieldInfo)member).Name;
}
else if (member.MemberType == MemberTypes.Method)
{
return ((MethodInfo)member).Name;
}
else
{
throw new InvalidCastException($"Member {member.Name} must be either Property, Field or Method but is {member.MemberType} which is not supported.");
}
}

private void ReadTypes(Type type, string path = null)
{
if (type.HasPropertyOfType<EPPlusTableColumnSortOrderAttribute>())
{
var sortOrderAttribute = type.GetFirstAttributeOfType<EPPlusTableColumnSortOrderAttribute>();
if (_sortOrder.Count == 0)
{
_sortOrder.AddRange(sortOrderAttribute.Properties);
}
else
{
var pathIndex = _sortOrder.IndexOf(path);
var offset = 1;
foreach (var prop in sortOrderAttribute.Properties)
{
var fullPath = $"{path}.{prop}";
_sortOrder.Insert(pathIndex + offset++, fullPath);
}
//_sortOrder.Remove(path);
}
foreach (var member in type.GetProperties(_bindingFlags))
{
if (member.MemberType != MemberTypes.Property) continue;
var memberName = GetMemberName(member);
var memberPath = string.IsNullOrEmpty(path) ? member.Name : $"{path}.{memberName}";
var isNested = member.HasPropertyOfType<EpplusNestedTableColumnAttribute>();
if (isNested)
{
var memberType = GetMemberType(member, memberPath);
ReadTypes(memberType, memberPath);
}
}

}
}

public List<string> GetSortOrder()
{
return _sortOrder;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public void ShouldSetHeaderPrefixOnComplexClassProperty_WithTableColumnAttribute
{
var ws = package.Workbook.Worksheets.Add("test");
ws.Cells["A1"].LoadFromCollection(items);
var cv = ws.Cells["F1"].Value;
var cv = ws.Cells["G1"].Value;
Assert.AreEqual("Collateral Owner Email", cv);
}
}
Expand All @@ -190,7 +190,7 @@ public void ShouldSetHeaderPrefixOnComplexClassProperty_WithoutTableColumnAttrib
{
var ws = package.Workbook.Worksheets.Add("test");
ws.Cells["A1"].LoadFromCollection(items);
var cv = ws.Cells["G1"].Value;
var cv = ws.Cells["F1"].Value;
Assert.AreEqual("Collateral Owner Name", cv);
}
}
Expand All @@ -209,15 +209,15 @@ public void ShouldLoadFromComplexInheritence()
[TestMethod]
public void LoadComplexTest2()
{
using(var package = new ExcelPackage())
using (var package = new ExcelPackage())
{
var items = ExcelItems.GetItems1();
var sheet = package.Workbook.Worksheets.Add("test");
sheet.Cells["A1"].LoadFromCollection(items);
Assert.AreEqual("Product Family", sheet.Cells["A1"].Value);
Assert.AreEqual("PCH Die Name", sheet.Cells["B1"].Value);
Assert.AreEqual("Collateral Owner Email", sheet.Cells["F1"].Value);
Assert.AreEqual("Mission Control Lead Email", sheet.Cells["I1"].Value);
Assert.AreEqual("Collateral Owner Email", sheet.Cells["G1"].Value);
Assert.AreEqual("Mission Control Lead Email", sheet.Cells["J1"].Value);
Assert.AreEqual("Created (GMT)", sheet.Cells["L1"].Value);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OfficeOpenXml;
using OfficeOpenXml.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EPPlusTest.LoadFunctions
{
[TestClass]
public class LoadFromCollectionSortOrderAttributeTests
{
[EPPlusTableColumnSortOrder(Properties = new string[] { nameof(Name), nameof(Obj), nameof(Id) })]
[EpplusTable]
public class Outer
{
[EpplusTableColumn]
public int Id { get; set; }

[EpplusNestedTableColumn]
public Inner Obj { get; set; }

[EpplusTableColumn]
public string Name { get; set; }
}

[EPPlusTableColumnSortOrder(Properties = new string[] { nameof(Name), nameof(Id) })]
public class Inner
{
[EpplusTableColumn]
public int Id { get; set; }

[EpplusTableColumn]
public string Name { get; set; }
}

[EPPlusTableColumnSortOrder(Properties = new string[] { nameof(Name), nameof(Obj), nameof(Id) })]
[EpplusTable]
public class Outer2
{
[EpplusTableColumn]
public int Id { get; set; }

[EpplusNestedTableColumn]
public Inner2 Obj { get; set; }

[EpplusTableColumn]
public string Name { get; set; }
}

[EPPlusTableColumnSortOrder(Properties = new string[] { nameof(Name), nameof(Id), nameof(Email) })]
public class Inner2
{
[EpplusTableColumn]
public int Id { get; set; }

[EpplusTableColumn]
public string Name { get; set; }

[EpplusNestedTableColumn]
public InnerEmail Email { get; set; }
}

[EPPlusTableColumnSortOrder(Properties = new string[] { nameof(Email), nameof(Id) })]
public class InnerEmail
{
[EpplusTableColumn]
public int Id { get; set; }

[EpplusTableColumn]
public string Email { get; set; }
}

[TestMethod]
public void SortBySortorderAttribute1()
{
var outer = new Outer
{
Id = 1,
Obj = new Inner
{
Id = 2,
Name = "Inner"
},
Name = "Outer"
};
var items = new List<Outer> { outer };
using (var package = new ExcelPackage())
{
var sheet = package.Workbook.Worksheets.Add("Sheet 1");
sheet.Cells["A1"].LoadFromCollection(items);

Assert.AreEqual("Outer", sheet.Cells["A1"].Value);
Assert.AreEqual("Inner", sheet.Cells["B1"].Value);
Assert.AreEqual(2, sheet.Cells["C1"].Value);
Assert.AreEqual(1, sheet.Cells["D1"].Value);
}
}

[TestMethod]
public void SortBySortorderAttribute2()
{
var outer = new Outer2
{
Id = 1,
Obj = new Inner2
{
Id = 2,
Name = "Inner",
Email = new InnerEmail
{
Id = 3,
Email = "[email protected]"
}
},
Name = "Outer"
};
var items = new List<Outer2> { outer };
using (var package = new ExcelPackage())
{
var sheet = package.Workbook.Worksheets.Add("Sheet 1");
sheet.Cells["A1"].LoadFromCollection(items);

Assert.AreEqual("Outer", sheet.Cells["A1"].Value);
Assert.AreEqual("Inner", sheet.Cells["B1"].Value);
Assert.AreEqual(2, sheet.Cells["C1"].Value);
Assert.AreEqual("[email protected]", sheet.Cells["D1"].Value);
Assert.AreEqual(3, sheet.Cells["E1"].Value);
Assert.AreEqual(1, sheet.Cells["F1"].Value);
}
}
}
}

0 comments on commit d03d1b3

Please sign in to comment.