Skip to content

Commit

Permalink
Initial version for #946, #1167 and #1058
Browse files Browse the repository at this point in the history
  • Loading branch information
swmal committed Nov 20, 2023
1 parent a7c1806 commit e705ed8
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/EPPlus/LoadFunctions/LoadFromCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public LoadFromCollection(ExcelRangeBase range, IEnumerable<T> items, LoadFromCo
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
105 changes: 105 additions & 0 deletions src/EPPlus/LoadFunctions/NestedColumnsSortorderScanner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
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
@@ -0,0 +1,64 @@
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; }
}

[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);
}
}
}
}

0 comments on commit e705ed8

Please sign in to comment.