diff --git a/ClosedXML/ClosedXML.csproj b/ClosedXML/ClosedXML.csproj index 8b9ecc4..84c5f14 100644 --- a/ClosedXML/ClosedXML.csproj +++ b/ClosedXML/ClosedXML.csproj @@ -49,6 +49,10 @@ True ..\packages\DocumentFormat.OpenXml.2.5\lib\DocumentFormat.OpenXml.dll + + ..\packages\FastMember.Signed.1.1.0\lib\net40\FastMember.Signed.dll + True + diff --git a/ClosedXML/Excel/CalcEngine/CalcEngine.cs b/ClosedXML/Excel/CalcEngine/CalcEngine.cs index 9ba60b2..a392dc1 100644 --- a/ClosedXML/Excel/CalcEngine/CalcEngine.cs +++ b/ClosedXML/Excel/CalcEngine/CalcEngine.cs @@ -11,7 +11,7 @@ namespace ClosedXML.Excel.CalcEngine { /// - /// CalcEngine parses strings and returns Expression objects that can + /// CalcEngine parses strings and returns Expression objects that can /// be evaluated. /// /// @@ -171,7 +171,7 @@ /// /// This method is useful when the engine needs to create objects dynamically. /// For example, a spreadsheet calc engine would use this method to dynamically create cell - /// range objects based on identifiers that cannot be enumerated at design time + /// range objects based on identifiers that cannot be enumerated at design time /// (such as "AB12", "A1:AB12", etc.) /// public virtual object GetExternalObject(string identifier) { @@ -375,15 +375,6 @@ break; } - // look for bindings - if (DataContext != null) { - var list = new List(); - for (var t = _token; t != null; t = GetMember()) { - list.Add(new BindingInfo((string)t.Value, GetParameters())); - } - x = new BindingExpression(this, list, _ci); - break; - } Throw("Unexpected identifier"); break; @@ -423,7 +414,7 @@ #region ** parser void GetToken() { - // eat white space + // eat white space while (_ptr < _len && _expr[_ptr] <= ' ') { _ptr++; } @@ -613,7 +604,7 @@ } List GetParameters() // e.g. myfun(a, b, c+2) { - // check whether next token is a (, + // check whether next token is a (, // restore state and bail if it's not var pos = _ptr; var tk = _token; @@ -650,7 +641,7 @@ return parms; } Token GetMember() { - // check whether next token is a MEMBER token ('.'), + // check whether next token is a MEMBER token ('.'), // restore state and bail if it's not var pos = _ptr; var tk = _token; diff --git a/ClosedXML/Excel/CalcEngine/Expression.cs b/ClosedXML/Excel/CalcEngine/Expression.cs index 1b97924..286bedd 100644 --- a/ClosedXML/Excel/CalcEngine/Expression.cs +++ b/ClosedXML/Excel/CalcEngine/Expression.cs @@ -1,12 +1,8 @@ using System; -using System.Text; using System.Threading; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; -using System.Reflection; -using ClosedXML.Excel.CalcEngine; namespace ClosedXML.Excel.CalcEngine { @@ -373,106 +369,6 @@ } } /// - /// Expression based on an object's properties. - /// - class BindingExpression : Expression - { - CalcEngine _ce; - CultureInfo _ci; - List _bindingPath; - - // ** ctor - internal BindingExpression(CalcEngine engine, List bindingPath, CultureInfo ci) - { - _ce = engine; - _bindingPath = bindingPath; - _ci = ci; - } - - // ** object model - override public object Evaluate() - { - return GetValue(_ce.DataContext); - } - - // ** implementation - object GetValue(object obj) - { - const BindingFlags bf = - BindingFlags.IgnoreCase | - BindingFlags.Instance | - BindingFlags.Public | - BindingFlags.Static; - - if (obj != null) - { - foreach (var bi in _bindingPath) - { - // get property - if (bi.PropertyInfo == null) - { - bi.PropertyInfo = obj.GetType().GetProperty(bi.Name, bf); - } - - // get object - try - { - obj = bi.PropertyInfo.GetValue(obj, null); - } - catch - { - // REVIEW: is this needed? - System.Diagnostics.Debug.Assert(false, "shouldn't happen!"); - bi.PropertyInfo = obj.GetType().GetProperty(bi.Name, bf); - bi.PropertyInfoItem = null; - obj = bi.PropertyInfo.GetValue(obj, null); - } - - // handle indexers (lists and dictionaries) - if (bi.Parms != null && bi.Parms.Count > 0) - { - // get indexer property (always called "Item") - if (bi.PropertyInfoItem == null) - { - bi.PropertyInfoItem = obj.GetType().GetProperty("Item", bf); - } - - // get indexer parameters - var pip = bi.PropertyInfoItem.GetIndexParameters(); - var list = new List(); - for (int i = 0; i < pip.Length; i++) - { - var pv = bi.Parms[i].Evaluate(); - pv = Convert.ChangeType(pv, pip[i].ParameterType, _ci); - list.Add(pv); - } - - // get value - obj = bi.PropertyInfoItem.GetValue(obj, list.ToArray()); - } - } - } - - // all done - return obj; - } - } - /// - /// Helper used for building BindingExpression objects. - /// - class BindingInfo - { - public BindingInfo(string member, List parms) - { - Name = member; - Parms = parms; - } - public string Name { get; set; } - public PropertyInfo PropertyInfo { get; set; } - public PropertyInfo PropertyInfoItem { get; set; } - public List Parms { get; set; } - } - /// /// Expression that represents an external object. /// class XObjectExpression : diff --git a/ClosedXML/Excel/Cells/XLCell.cs b/ClosedXML/Excel/Cells/XLCell.cs index 7a131ed..4b1b6a3 100644 --- a/ClosedXML/Excel/Cells/XLCell.cs +++ b/ClosedXML/Excel/Cells/XLCell.cs @@ -11,6 +11,7 @@ namespace ClosedXML.Excel { using Attributes; + using FastMember; internal class XLCell : IXLCell, IXLStylized { @@ -244,19 +245,7 @@ _cellValue = dtTest.ToOADate().ToInvariantString(); } - else if ( - value is sbyte - || value is byte - || value is short - || value is ushort - || value is int - || value is uint - || value is long - || value is ulong - || value is float - || value is double - || value is decimal - ) + else if (value.GetType().IsNumber()) { if ((value is double || value is float) && (Double.IsNaN((Double)Convert.ChangeType(value, typeof(Double))) || Double.IsInfinity((Double)Convert.ChangeType(value, typeof(Double))))) @@ -513,16 +502,21 @@ } else { - BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; + const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; var memberCache = new Dictionary>(); + var accessorCache = new Dictionary(); IEnumerable members = null; + TypeAccessor accessor = null; bool isPlainObject = itemType == typeof(object); if (!isPlainObject) + { members = itemType.GetFields(bindingFlags).Cast() .Concat(itemType.GetProperties(bindingFlags)) .Where(mi => !XLColumnAttribute.IgnoreMember(mi)) .OrderBy(mi => XLColumnAttribute.GetOrder(mi)); + accessor = TypeAccessor.Create(itemType); + } foreach (T m in data) { @@ -532,12 +526,20 @@ // This is very inefficient and we prefer type of T to be a concrete class or struct var type = m.GetType(); if (!memberCache.ContainsKey(type)) - memberCache.Add(type, type.GetFields(bindingFlags).Cast() + { + var _accessor = TypeAccessor.Create(type); + + var _members = type.GetFields(bindingFlags).Cast() .Concat(type.GetProperties(bindingFlags)) .Where(mi => !XLColumnAttribute.IgnoreMember(mi)) - .OrderBy(mi => XLColumnAttribute.GetOrder(mi))); + .OrderBy(mi => XLColumnAttribute.GetOrder(mi)); + + memberCache.Add(type, _members); + accessorCache.Add(type, _accessor); + } members = memberCache[type]; + accessor = accessorCache[type]; } var co = Address.ColumnNumber; @@ -627,14 +629,7 @@ foreach (var mi in members) { - var fi = mi as FieldInfo; - var pi = mi as PropertyInfo; - - if (fi != null) - SetValue(fi.GetValue(m), ro, co); - else if (pi != null && mi as IEnumerable == null) - SetValue(pi.GetValue(m, null), ro, co); - + SetValue(accessor[m, mi.Name], ro, co); co++; } } @@ -711,9 +706,32 @@ var maxCo = 0; var isDataTable = false; var isDataReader = false; + + const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; + var memberCache = new Dictionary>(); + var accessorCache = new Dictionary(); + IEnumerable members = null; + TypeAccessor accessor = null; + foreach (var m in data) { var itemType = m.GetType(); + if (!memberCache.ContainsKey(itemType)) + { + var _accessor = TypeAccessor.Create(itemType); + + var _members = itemType.GetFields(bindingFlags).Cast() + .Concat(itemType.GetProperties(bindingFlags)) + .Where(mi => !XLColumnAttribute.IgnoreMember(mi)) + .OrderBy(mi => XLColumnAttribute.GetOrder(mi)); + + memberCache.Add(itemType, _members); + accessorCache.Add(itemType, _accessor); + } + + members = memberCache[itemType]; + accessor = accessorCache[itemType]; + var co = Address.ColumnNumber; if (itemType.IsPrimitive || itemType == typeof(String) || itemType == typeof(DateTime) || itemType.IsNumber()) @@ -757,18 +775,9 @@ } else { - var fieldInfo = itemType.GetFields(); - foreach (var info in fieldInfo) + foreach (var mi in members) { - SetValue(info.GetValue(m), ro, co); - co++; - } - - var propertyInfo = itemType.GetProperties(); - foreach (var info in propertyInfo) - { - if ((info as IEnumerable) == null) - SetValue(info.GetValue(m, null), ro, co); + SetValue(accessor[m, mi.Name], ro, co); co++; } } diff --git a/ClosedXML/Excel/IXLTheme.cs b/ClosedXML/Excel/IXLTheme.cs index b4daa78..b4e1ccb 100644 --- a/ClosedXML/Excel/IXLTheme.cs +++ b/ClosedXML/Excel/IXLTheme.cs @@ -1,4 +1,6 @@ - +using ClosedXML.Excel; +using System.Drawing; + namespace ClosedXML.Excel { public interface IXLTheme @@ -15,5 +17,7 @@ XLColor Accent6 { get; set; } XLColor Hyperlink { get; set; } XLColor FollowedHyperlink { get; set; } + + XLColor ResolveThemeColor(XLThemeColor themeColor); } } diff --git a/ClosedXML/Excel/Style/Dictionary.cs b/ClosedXML/Excel/Style/Dictionary.cs deleted file mode 100644 index b866775..0000000 --- a/ClosedXML/Excel/Style/Dictionary.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace ClosedXML.Excel.Style -{ - class Dictionary - { - } -} diff --git a/ClosedXML/Excel/XLTheme.cs b/ClosedXML/Excel/XLTheme.cs index 288b063..3c5e839 100644 --- a/ClosedXML/Excel/XLTheme.cs +++ b/ClosedXML/Excel/XLTheme.cs @@ -1,7 +1,9 @@ - +using FastMember; +using System.Linq; + namespace ClosedXML.Excel { - internal class XLTheme: IXLTheme + internal class XLTheme : IXLTheme { public XLColor Background1 { get; set; } public XLColor Text1 { get; set; } @@ -15,5 +17,17 @@ public XLColor Accent6 { get; set; } public XLColor Hyperlink { get; set; } public XLColor FollowedHyperlink { get; set; } + + private TypeAccessor accessor = TypeAccessor.Create(typeof(XLTheme)); + + public XLColor ResolveThemeColor(XLThemeColor themeColor) + { + var tc = themeColor.ToString(); + var members = accessor.GetMembers(); + if (members.Any(m => m.Name.Equals(tc))) + return accessor[this, tc] as XLColor; + else + return null; + } } } diff --git a/ClosedXML/Extensions.cs b/ClosedXML/Extensions.cs index 67db6c4..b2e1cd0 100644 --- a/ClosedXML/Extensions.cs +++ b/ClosedXML/Extensions.cs @@ -319,24 +319,6 @@ } } - public static class ObjectExtensions - { - public static bool IsNumber(this object value) - { - return value is sbyte - || value is byte - || value is short - || value is ushort - || value is int - || value is uint - || value is long - || value is ulong - || value is float - || value is double - || value is decimal; - } - } - public static class TypeExtensions { public static bool IsNumber(this Type type) @@ -354,4 +336,22 @@ || type == typeof(decimal); } } -} \ No newline at end of file + + public static class ObjectExtensions + { + public static bool IsNumber(this object value) + { + return value is sbyte + || value is byte + || value is short + || value is ushort + || value is int + || value is uint + || value is long + || value is ulong + || value is float + || value is double + || value is decimal; + } + } +} diff --git a/ClosedXML/packages.config b/ClosedXML/packages.config index dfe0d07..682875d 100644 --- a/ClosedXML/packages.config +++ b/ClosedXML/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj b/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj index b828af4..f2acadf 100644 --- a/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj +++ b/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj @@ -45,6 +45,10 @@ ..\packages\DocumentFormat.OpenXml.1.0\lib\DocumentFormat.OpenXml.dll True + + ..\packages\FastMember.Signed.1.1.0\lib\net35\FastMember.Signed.dll + True + diff --git a/ClosedXML_Net3.5/packages.config b/ClosedXML_Net3.5/packages.config index db6cf16..6908883 100644 --- a/ClosedXML_Net3.5/packages.config +++ b/ClosedXML_Net3.5/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/ClosedXML_Tests/Excel/Misc/StylesTests.cs b/ClosedXML_Tests/Excel/Misc/StylesTests.cs index a00a47e..d60e69f 100644 --- a/ClosedXML_Tests/Excel/Misc/StylesTests.cs +++ b/ClosedXML_Tests/Excel/Misc/StylesTests.cs @@ -63,5 +63,19 @@ Assert.AreEqual(XLBorderStyleValues.Thick, range.LastColumn().Cell(2).Style.Border.RightBorder); Assert.AreEqual(XLBorderStyleValues.Double, range.LastColumn().Cell(3).Style.Border.RightBorder); } + + [Test] + public void ResolveThemeColors() + { + using (var wb = new XLWorkbook()) + { + string color; + color = wb.Theme.ResolveThemeColor(XLThemeColor.Accent1).Color.ToHex(); + Assert.AreEqual("FF4F81BD", color); + + color = wb.Theme.ResolveThemeColor(XLThemeColor.Background1).Color.ToHex(); + Assert.AreEqual("FFFFFFFF", color); + } + } } -} \ No newline at end of file +}