diff --git a/ClosedXML/ClosedXML.csproj b/ClosedXML/ClosedXML.csproj index e2302de..704523f 100644 --- a/ClosedXML/ClosedXML.csproj +++ b/ClosedXML/ClosedXML.csproj @@ -71,6 +71,17 @@ + + + + + + + + + + + @@ -103,6 +114,21 @@ + + + + + + + + + + + + + + + diff --git a/ClosedXML/Excel/Caching/IXLRepository.cs b/ClosedXML/Excel/Caching/IXLRepository.cs new file mode 100644 index 0000000..dd3574b --- /dev/null +++ b/ClosedXML/Excel/Caching/IXLRepository.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + /// + /// Base interface for an abstract repository. + /// + internal interface IXLRepository + { + /// + /// Clear the repository; + /// + void Clear(); + } + + internal interface IXLRepository : IXLRepository, IEnumerable + where Tkey : struct, IEquatable + where Tvalue : class + { + /// + /// Put the into the repository under the specified + /// if there is no such key present. + /// + /// Key to identify the value. + /// Value to put into the repository if key does not exist. + /// Value stored in the repository under the specified . If key already existed + /// returned value may differ from the input one. + Tvalue Store(Tkey key, Tvalue value); + } +} diff --git a/ClosedXML/Excel/Caching/XLAlignmentRepository.cs b/ClosedXML/Excel/Caching/XLAlignmentRepository.cs new file mode 100644 index 0000000..fbd768f --- /dev/null +++ b/ClosedXML/Excel/Caching/XLAlignmentRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + internal sealed class XLAlignmentRepository : XLRepositoryBase + { + #region constructors + public XLAlignmentRepository(Func createNew) : base(createNew) + { + } + + public XLAlignmentRepository(Func createNew, IEqualityComparer comparer) : base(createNew, comparer) + { + } + #endregion + } +} diff --git a/ClosedXML/Excel/Caching/XLBorderRepository.cs b/ClosedXML/Excel/Caching/XLBorderRepository.cs new file mode 100644 index 0000000..f9ea36f --- /dev/null +++ b/ClosedXML/Excel/Caching/XLBorderRepository.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + internal sealed class XLBorderRepository : XLRepositoryBase + { + #region constructors + public XLBorderRepository(Func createNew) : base(createNew) + { + } + + public XLBorderRepository(Func createNew, IEqualityComparer comparer) : base(createNew, comparer) + { + } + + + #endregion + } +} diff --git a/ClosedXML/Excel/Caching/XLColorRepository.cs b/ClosedXML/Excel/Caching/XLColorRepository.cs new file mode 100644 index 0000000..1e42837 --- /dev/null +++ b/ClosedXML/Excel/Caching/XLColorRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + internal sealed class XLColorRepository : XLRepositoryBase + { + #region constructors + public XLColorRepository(Func createNew) : base(createNew) + { + } + + public XLColorRepository(Func createNew, IEqualityComparer comparer) : base(createNew, comparer) + { + } + #endregion + } +} diff --git a/ClosedXML/Excel/Caching/XLFillRepository.cs b/ClosedXML/Excel/Caching/XLFillRepository.cs new file mode 100644 index 0000000..b9439bb --- /dev/null +++ b/ClosedXML/Excel/Caching/XLFillRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + internal sealed class XLFillRepository : XLRepositoryBase + { + #region constructors + public XLFillRepository(Func createNew) : base(createNew) + { + } + + public XLFillRepository(Func createNew, IEqualityComparer comparer) : base(createNew, comparer) + { + } + #endregion + } +} diff --git a/ClosedXML/Excel/Caching/XLFontRepository.cs b/ClosedXML/Excel/Caching/XLFontRepository.cs new file mode 100644 index 0000000..e10f541 --- /dev/null +++ b/ClosedXML/Excel/Caching/XLFontRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + internal sealed class XLFontRepository : XLRepositoryBase + { + #region constructors + public XLFontRepository(Func createNew) : base(createNew) + { + } + + public XLFontRepository(Func createNew, IEqualityComparer comparer) : base(createNew, comparer) + { + } + #endregion + } +} diff --git a/ClosedXML/Excel/Caching/XLNumberFormatRepository.cs b/ClosedXML/Excel/Caching/XLNumberFormatRepository.cs new file mode 100644 index 0000000..1d73a98 --- /dev/null +++ b/ClosedXML/Excel/Caching/XLNumberFormatRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + internal sealed class XLNumberFormatRepository : XLRepositoryBase + { + #region constructors + public XLNumberFormatRepository(Func createNew) : base(createNew) + { + } + + public XLNumberFormatRepository(Func createNew, IEqualityComparer comparer) : base(createNew, comparer) + { + } + #endregion + } +} diff --git a/ClosedXML/Excel/Caching/XLProtectionRepository.cs b/ClosedXML/Excel/Caching/XLProtectionRepository.cs new file mode 100644 index 0000000..0b42f3f --- /dev/null +++ b/ClosedXML/Excel/Caching/XLProtectionRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + internal sealed class XLProtectionRepository : XLRepositoryBase + { + #region constructors + public XLProtectionRepository(Func createNew) : base(createNew) + { + } + + public XLProtectionRepository(Func createNew, IEqualityComparer comparer) : base(createNew, comparer) + { + } + #endregion + } +} diff --git a/ClosedXML/Excel/Caching/XLRepositoryBase.cs b/ClosedXML/Excel/Caching/XLRepositoryBase.cs new file mode 100644 index 0000000..cdf3487 --- /dev/null +++ b/ClosedXML/Excel/Caching/XLRepositoryBase.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Collections; + +namespace ClosedXML.Excel.Caching +{ + internal abstract class XLRepositoryBase : IXLRepository + { + public abstract void Clear(); + } + + internal abstract class XLRepositoryBase : XLRepositoryBase, IXLRepository + where Tkey : struct, IEquatable + where Tvalue : class + { + const int CONCURRENCY_LEVEL = 4; + const int INITIAL_CAPACITY = 1000; + + private readonly ConcurrentDictionary _storage; + private readonly Func _createNew; + public XLRepositoryBase(Func createNew) : this(createNew, EqualityComparer.Default) + { + } + + public XLRepositoryBase(Func createNew, IEqualityComparer comparer) + { + _storage = new ConcurrentDictionary(CONCURRENCY_LEVEL, INITIAL_CAPACITY, comparer); + _createNew = createNew; + } + + public bool ContainsKey(Tkey key) + { + WeakReference cachedReference; + if (_storage.TryGetValue(key, out cachedReference)) + { + var storedValue = cachedReference.Target as Tvalue; + return (storedValue != null); + } + return false; + } + + public Tvalue Store(Tkey key, Tvalue value) + { + if (value == null) + return null; + + if (!_storage.ContainsKey(key)) + { + _storage.TryAdd(key, new WeakReference(value)); + return value; + } + else + { + var cachedReference = _storage[key]; + var storedValue = cachedReference.Target as Tvalue; + if (storedValue == null) + { + _storage.TryAdd(key, new WeakReference(value)); + return value; + } + return storedValue; + } + } + + public Tvalue GetOrCreate(Tkey key) + { + WeakReference cachedReference; + if (_storage.TryGetValue(key, out cachedReference)) + { + var storedValue = cachedReference.Target as Tvalue; + if (storedValue != null) + { + return storedValue; + } + else + { + WeakReference _; + _storage.TryRemove(key, out _); + } + } + + var value = _createNew(key); + return Store(key, value); + } + + public override void Clear() + { + _storage.Clear(); + } + + /// + /// Enumerate items in repository removing "dead" entries. + /// + public IEnumerator GetEnumerator() + { + return _storage + .Select(pair => + { + var val = pair.Value.Target as Tvalue; + if (val == null) + { + WeakReference _; + _storage.TryRemove(pair.Key, out _); + } + return val; + }) + .Where(val => val != null) + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/ClosedXML/Excel/Caching/XLStyleRepository.cs b/ClosedXML/Excel/Caching/XLStyleRepository.cs new file mode 100644 index 0000000..26aa586 --- /dev/null +++ b/ClosedXML/Excel/Caching/XLStyleRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + internal sealed class XLStyleRepository : XLRepositoryBase + { + #region constructors + public XLStyleRepository(Func createNew) : base(createNew) + { + } + + public XLStyleRepository(Func createNew, IEqualityComparer comparer) : base(createNew, comparer) + { + } + #endregion + } +} diff --git a/ClosedXML/Excel/Caching/XLWorkbookElementRepositoryBase.cs b/ClosedXML/Excel/Caching/XLWorkbookElementRepositoryBase.cs new file mode 100644 index 0000000..d06b8e2 --- /dev/null +++ b/ClosedXML/Excel/Caching/XLWorkbookElementRepositoryBase.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace ClosedXML.Excel.Caching +{ + /// + /// Base repository for elements. + /// + internal abstract class XLWorkbookElementRepositoryBase : XLRepositoryBase + where Tkey : struct, IEquatable + where Tvalue : class + { + public XLWorkbook Workbook { get; private set; } + + public XLWorkbookElementRepositoryBase(XLWorkbook workbook, Func createNew) : this(workbook, createNew, EqualityComparer.Default) + { + } + + public XLWorkbookElementRepositoryBase(XLWorkbook workbook, Func createNew, IEqualityComparer сomparer) : base(createNew, сomparer) + { + Workbook = workbook; + } + } +} diff --git a/ClosedXML/Excel/Style/Colors/XLColor_Internal.cs b/ClosedXML/Excel/Style/Colors/XLColor_Internal.cs index a5aeafc..3919bf8 100644 --- a/ClosedXML/Excel/Style/Colors/XLColor_Internal.cs +++ b/ClosedXML/Excel/Style/Colors/XLColor_Internal.cs @@ -5,64 +5,54 @@ { public partial class XLColor { - private XLColor(XLColor defaultColor) - { - var dColor = defaultColor; - if (dColor._colorType == XLColorType.Color) - _color = dColor._color; - else if (dColor._colorType == XLColorType.Theme) - { - _themeColor = dColor._themeColor; - _themeTint = dColor._themeTint; - } - else - { - _indexed = dColor._indexed; - } + internal XLColorKey Key { get; private set; } - HasValue = true; - _hashCode = dColor._hashCode; - _colorType = dColor._colorType; + private XLColor(XLColor defaultColor) : this(defaultColor.Key) + { } - private XLColor() + private XLColor() : this(new XLColorKey()) { HasValue = false; - _hashCode = 0; } - private XLColor(Color color) + private XLColor(Color color) : this(new XLColorKey { - _color = color; - _hashCode = 13 ^ color.ToArgb(); - HasValue = true; - _colorType = XLColorType.Color; + Color = color, + ColorType = XLColorType.Color + }) + { } - private XLColor(Int32 index) + private XLColor(Int32 index) : this(new XLColorKey { - _indexed = index; - _hashCode = 11 ^ _indexed; - HasValue = true; - _colorType = XLColorType.Indexed; + Indexed = index, + ColorType = XLColorType.Indexed + }) + { } - private XLColor(XLThemeColor themeColor) + private XLColor(XLThemeColor themeColor) : this(new XLColorKey { - _themeColor = themeColor; - _themeTint = 0; - _hashCode = 7 ^ _themeColor.GetHashCode() ^ _themeTint.GetHashCode(); - HasValue = true; - _colorType = XLColorType.Theme; + ThemeColor = themeColor, + ColorType = XLColorType.Theme + }) + { } - private XLColor(XLThemeColor themeColor, Double themeTint) + private XLColor(XLThemeColor themeColor, Double themeTint) : this(new XLColorKey { - _themeColor = themeColor; - _themeTint = themeTint; - _hashCode = 7 ^ _themeColor.GetHashCode() ^ _themeTint.GetHashCode(); + ThemeColor = themeColor, + ThemeTint = themeTint, + ColorType = XLColorType.Theme + }) + { + } + + internal XLColor(XLColorKey key) + { + Key = key; HasValue = true; - _colorType = XLColorType.Theme; } } -} \ No newline at end of file +} diff --git a/ClosedXML/Excel/Style/Colors/XLColor_Public.cs b/ClosedXML/Excel/Style/Colors/XLColor_Public.cs index 4d0d9d3..3e0bcb4 100644 --- a/ClosedXML/Excel/Style/Colors/XLColor_Public.cs +++ b/ClosedXML/Excel/Style/Colors/XLColor_Public.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Drawing; namespace ClosedXML.Excel @@ -35,35 +36,28 @@ /// We'll return normal black when index 81 is found. /// private const Int32 TOOLTIPCOLORINDEX = 81; - - private readonly XLColorType _colorType; - private int _hashCode; - private readonly Int32 _indexed; - private readonly XLThemeColor _themeColor; - private readonly Double _themeTint; - - private Color _color; + public Boolean HasValue { get; private set; } public XLColorType ColorType { - get { return _colorType; } + get { return Key.ColorType; } } public Color Color { get { - if (_colorType == XLColorType.Theme) + if (ColorType == XLColorType.Theme) throw new InvalidOperationException("Cannot convert theme color to Color."); - if (_colorType == XLColorType.Indexed) - if (_indexed == TOOLTIPCOLORINDEX) + if (ColorType == XLColorType.Indexed) + if (Indexed == TOOLTIPCOLORINDEX) return Color.FromArgb(255, Color.Black); else - return IndexedColors[_indexed].Color; + return IndexedColors[Indexed].Color; - return _color; + return Key.Color; } } @@ -75,7 +69,7 @@ throw new InvalidOperationException("Cannot convert theme color to indexed color."); if (ColorType == XLColorType.Indexed) - return _indexed; + return Key.Indexed; throw new InvalidOperationException("Cannot convert Color to indexed color."); } @@ -86,7 +80,7 @@ get { if (ColorType == XLColorType.Theme) - return _themeColor; + return Key.ThemeColor; if (ColorType == XLColorType.Indexed) throw new InvalidOperationException("Cannot convert indexed color to theme color."); @@ -100,12 +94,12 @@ get { if (ColorType == XLColorType.Theme) - return _themeTint; + return Key.ThemeTint; if (ColorType == XLColorType.Indexed) throw new InvalidOperationException("Cannot extract theme tint from an indexed color."); - return _color.A/255.0; + return Color.A/255.0; } } @@ -113,23 +107,7 @@ public bool Equals(XLColor other) { - if (_colorType == other._colorType) - { - if (_colorType == XLColorType.Color) - { - // .NET Color.Equals() will return false for Color.FromArgb(255, 255, 255, 255) == Color.White - // Therefore we compare the ToArgb() values - return _color.ToArgb() == other._color.ToArgb(); - } - if (_colorType == XLColorType.Theme) - { - return _themeColor == other._themeColor - && Math.Abs(_themeTint - other._themeTint) < XLHelper.Epsilon; - } - return _indexed == other._indexed; - } - - return false; + return Key == other.Key; } #endregion @@ -141,28 +119,21 @@ public override int GetHashCode() { - if (_hashCode == 0) - { - if (_colorType == XLColorType.Color) - _hashCode = _color.GetHashCode(); - else if (_colorType == XLColorType.Theme) - _hashCode = _themeColor.GetHashCode() ^ _themeTint.GetHashCode(); - else - _hashCode = _indexed; - } - - return _hashCode; + var hashCode = 229333804; + hashCode = hashCode * -1521134295 + HasValue.GetHashCode(); + hashCode = hashCode * -1521134295 + Key.GetHashCode(); + return hashCode; } public override string ToString() { - if (_colorType == XLColorType.Color) + if (ColorType == XLColorType.Color) return Color.ToHex(); - if (_colorType == XLColorType.Theme) - return String.Format("Color Theme: {0}, Tint: {1}", _themeColor.ToString(), _themeTint.ToString()); + if (ColorType == XLColorType.Theme) + return String.Format("Color Theme: {0}, Tint: {1}", ThemeColor.ToString(), ThemeTint.ToString()); - return "Color Index: " + _indexed; + return "Color Index: " + Indexed; } public static Boolean operator ==(XLColor left, XLColor right) diff --git a/ClosedXML/Excel/Style/Colors/XLColor_Static.cs b/ClosedXML/Excel/Style/Colors/XLColor_Static.cs index 3c8ec59..cd88be2 100644 --- a/ClosedXML/Excel/Style/Colors/XLColor_Static.cs +++ b/ClosedXML/Excel/Style/Colors/XLColor_Static.cs @@ -1,3 +1,4 @@ +using ClosedXML.Excel.Caching; using ClosedXML.Utils; using System; using System.Collections.Generic; @@ -7,21 +8,23 @@ { public partial class XLColor { + private static readonly XLColorRepository Repository = new XLColorRepository(key => new XLColor(key)); + private static readonly Dictionary ByColor = new Dictionary(); private static readonly Object ByColorLock = new Object(); + internal static XLColor FromKey(XLColorKey key) + { + return Repository.GetOrCreate(key); + } + public static XLColor FromColor(Color color) { - XLColor ret; - lock (ByColorLock) + return FromKey(new XLColorKey { - if (!ByColor.TryGetValue(color, out ret)) - { - ret = new XLColor(color); - ByColor.Add(color, ret); - } - } - return ret; + ColorType = XLColorType.Color, + Color = color + }); } public static XLColor FromArgb(Int32 argb) @@ -54,67 +57,33 @@ return FromColor(ColorStringParser.ParseFromHtml(htmlColor)); } - private static readonly Dictionary ByIndex = new Dictionary(); - private static readonly Object ByIndexLock = new Object(); public static XLColor FromIndex(Int32 index) { - XLColor ret; - lock (ByIndexLock) + return FromKey(new XLColorKey { - if (!ByIndex.TryGetValue(index, out ret)) - { - ret = new XLColor(index); - ByIndex.Add(index, ret); - } - } - return ret; + ColorType = XLColorType.Indexed, + Indexed = index + }); } - private static readonly Dictionary ByTheme = new Dictionary(); - private static readonly Object ByThemeLock = new Object(); - public static XLColor FromTheme(XLThemeColor themeColor) { - XLColor ret; - lock (ByThemeLock) + return FromKey(new XLColorKey { - if (!ByTheme.TryGetValue(themeColor, out ret)) - { - ret = new XLColor(themeColor); - ByTheme.Add(themeColor, ret); - } - } - return ret; + ColorType = XLColorType.Theme, + ThemeColor = themeColor + }); } - private static readonly Dictionary> ByThemeTint = new Dictionary>(); - private static readonly Object ByThemeTintLock = new Object(); - public static XLColor FromTheme(XLThemeColor themeColor, Double themeTint) { - XLColor ret; - lock (ByThemeTintLock) + return FromKey(new XLColorKey { - Dictionary themeTints; - if (ByThemeTint.TryGetValue(themeColor, out themeTints)) - { - if (!themeTints.TryGetValue(themeTint, out ret)) - { - ret = new XLColor(themeColor, themeTint); - themeTints.Add(themeTint, ret); - } - } - else - { - themeTints = new Dictionary(); - ret = new XLColor(themeColor, themeTint); - themeTints.Add(themeTint, ret); - - ByThemeTint.Add(themeColor, themeTints); - } - } - return ret; + ColorType = XLColorType.Theme, + ThemeColor = themeColor, + ThemeTint = themeTint + }); } private static Dictionary _indexedColors; diff --git a/ClosedXML/Excel/Style/XLAlignmentKey.cs b/ClosedXML/Excel/Style/XLAlignmentKey.cs new file mode 100644 index 0000000..4fd0ac8 --- /dev/null +++ b/ClosedXML/Excel/Style/XLAlignmentKey.cs @@ -0,0 +1,59 @@ +using System; + +namespace ClosedXML.Excel +{ + public struct XLAlignmentKey : IEquatable + { + public XLAlignmentHorizontalValues Horizontal { get; set; } + public XLAlignmentVerticalValues Vertical { get; set; } + public int Indent { get; set; } + public bool JustifyLastLine { get; set; } + public XLAlignmentReadingOrderValues ReadingOrder { get; set; } + public int RelativeIndent { get; set; } + public bool ShrinkToFit { get; set; } + public int TextRotation { get; set; } + public bool WrapText { get; set; } + public bool TopToBottom { get; set; } + + public bool Equals(XLAlignmentKey other) + { + return + Horizontal == other.Horizontal + && Vertical == other.Vertical + && Indent == other.Indent + && JustifyLastLine == other.JustifyLastLine + && ReadingOrder == other.ReadingOrder + && RelativeIndent == other.RelativeIndent + && ShrinkToFit == other.ShrinkToFit + && TextRotation == other.TextRotation + && WrapText == other.WrapText + && TopToBottom == other.TopToBottom; + } + + public override bool Equals(object obj) + { + if (obj is XLAlignment) + return Equals((XLAlignment)obj); + return base.Equals(obj); + } + + public override int GetHashCode() + { + var hashCode = -596887160; + hashCode = hashCode * -1521134295 + Horizontal.GetHashCode(); + hashCode = hashCode * -1521134295 + Vertical.GetHashCode(); + hashCode = hashCode * -1521134295 + Indent.GetHashCode(); + hashCode = hashCode * -1521134295 + JustifyLastLine.GetHashCode(); + hashCode = hashCode * -1521134295 + ReadingOrder.GetHashCode(); + hashCode = hashCode * -1521134295 + RelativeIndent.GetHashCode(); + hashCode = hashCode * -1521134295 + ShrinkToFit.GetHashCode(); + hashCode = hashCode * -1521134295 + TextRotation.GetHashCode(); + hashCode = hashCode * -1521134295 + WrapText.GetHashCode(); + hashCode = hashCode * -1521134295 + TopToBottom.GetHashCode(); + return hashCode; + } + + public static bool operator ==(XLAlignmentKey left, XLAlignmentKey right) => left.Equals(right); + public static bool operator !=(XLAlignmentKey left, XLAlignmentKey right) => !(left.Equals(right)); + } +} diff --git a/ClosedXML/Excel/Style/XLAlignmentValue.cs b/ClosedXML/Excel/Style/XLAlignmentValue.cs new file mode 100644 index 0000000..5702d76 --- /dev/null +++ b/ClosedXML/Excel/Style/XLAlignmentValue.cs @@ -0,0 +1,56 @@ +using ClosedXML.Excel.Caching; + +namespace ClosedXML.Excel +{ + public class XLAlignmentValue + { + private static readonly XLAlignmentRepository Repository = new XLAlignmentRepository(key => new XLAlignmentValue(key)); + + public static XLAlignmentValue FromKey(XLAlignmentKey key) + { + return Repository.GetOrCreate(key); + } + + internal static readonly XLAlignmentValue Default = FromKey(new XLAlignmentKey + { + Indent = 0, + Horizontal = XLAlignmentHorizontalValues.General, + JustifyLastLine = false, + ReadingOrder = XLAlignmentReadingOrderValues.ContextDependent, + RelativeIndent = 0, + ShrinkToFit = false, + TextRotation = 0, + Vertical = XLAlignmentVerticalValues.Bottom, + WrapText = false + }); + + public XLAlignmentKey Key { get; private set; } + public XLAlignmentHorizontalValues Horizontal {get { return Key.Horizontal; } } + public XLAlignmentVerticalValues Vertical {get { return Key.Vertical; } } + public int Indent {get { return Key.Indent; } } + public bool JustifyLastLine {get { return Key.JustifyLastLine; } } + public XLAlignmentReadingOrderValues ReadingOrder {get { return Key.ReadingOrder; } } + public int RelativeIndent {get { return Key.RelativeIndent; } } + public bool ShrinkToFit {get { return Key.ShrinkToFit; } } + public int TextRotation {get { return Key.TextRotation; } } + public bool WrapText {get { return Key.WrapText; } } + public bool TopToBottom {get { return Key.TopToBottom; } } + + private XLAlignmentValue(XLAlignmentKey key) + { + Key = key; + } + + public override bool Equals(object obj) + { + var cached = obj as XLAlignmentValue; + return cached != null && + Key.Equals(cached.Key); + } + + public override int GetHashCode() + { + return 990326508 + Key.GetHashCode(); + } + } +} diff --git a/ClosedXML/Excel/Style/XLBorderKey.cs b/ClosedXML/Excel/Style/XLBorderKey.cs new file mode 100644 index 0000000..5e69ee3 --- /dev/null +++ b/ClosedXML/Excel/Style/XLBorderKey.cs @@ -0,0 +1,66 @@ +using System; + +namespace ClosedXML.Excel +{ + public struct XLBorderKey : IEquatable + { + public XLBorderStyleValues LeftBorder { get; set; } + public XLColorKey LeftBorderColor { get; set; } + public XLBorderStyleValues RightBorder { get; set; } + public XLColorKey RightBorderColor { get; set; } + public XLBorderStyleValues TopBorder { get; set; } + public XLColorKey TopBorderColor { get; set; } + public XLBorderStyleValues BottomBorder { get; set; } + public XLColorKey BottomBorderColor { get; set; } + public XLBorderStyleValues DiagonalBorder { get; set; } + public XLColorKey DiagonalBorderColor { get; set; } + public bool DiagonalUp { get; set; } + public bool DiagonalDown { get; set; } + + public override int GetHashCode() + { + var hashCode = -198124310; + hashCode = hashCode * -1521134295 + LeftBorder.GetHashCode(); + hashCode = hashCode * -1521134295 + LeftBorderColor.GetHashCode(); + hashCode = hashCode * -1521134295 + RightBorder.GetHashCode(); + hashCode = hashCode * -1521134295 + RightBorderColor.GetHashCode(); + hashCode = hashCode * -1521134295 + TopBorder.GetHashCode(); + hashCode = hashCode * -1521134295 + TopBorderColor.GetHashCode(); + hashCode = hashCode * -1521134295 + BottomBorder.GetHashCode(); + hashCode = hashCode * -1521134295 + BottomBorderColor.GetHashCode(); + hashCode = hashCode * -1521134295 + DiagonalBorder.GetHashCode(); + hashCode = hashCode * -1521134295 + DiagonalBorderColor.GetHashCode(); + hashCode = hashCode * -1521134295 + DiagonalUp.GetHashCode(); + hashCode = hashCode * -1521134295 + DiagonalDown.GetHashCode(); + return hashCode; + } + + public bool Equals(XLBorderKey other) + { + return + LeftBorder == other.LeftBorder + && LeftBorderColor == other.LeftBorderColor + && RightBorder == other.RightBorder + && RightBorderColor == other.RightBorderColor + && TopBorder == other.TopBorder + && TopBorderColor == other.TopBorderColor + && BottomBorder == other.BottomBorder + && BottomBorderColor == other.BottomBorderColor + && DiagonalBorder == other.DiagonalBorder + && DiagonalBorderColor == other.DiagonalBorderColor + && DiagonalUp == other.DiagonalUp + && DiagonalDown == other.DiagonalDown; + } + + public override bool Equals(object obj) + { + if (obj is XLBorderKey) + return Equals((XLBorderKey)obj); + return base.Equals(obj); + } + + public static bool operator ==(XLBorderKey left, XLBorderKey right) => left.Equals(right); + public static bool operator !=(XLBorderKey left, XLBorderKey right) => !(left.Equals(right)); + + } +} diff --git a/ClosedXML/Excel/Style/XLBorderValue.cs b/ClosedXML/Excel/Style/XLBorderValue.cs new file mode 100644 index 0000000..4e7ea8d --- /dev/null +++ b/ClosedXML/Excel/Style/XLBorderValue.cs @@ -0,0 +1,67 @@ +using ClosedXML.Excel.Caching; + +namespace ClosedXML.Excel +{ + public class XLBorderValue + { + private static readonly XLBorderRepository Repository = new XLBorderRepository(key => new XLBorderValue(key)); + + public static XLBorderValue FromKey(XLBorderKey key) + { + return Repository.GetOrCreate(key); + } + + internal static readonly XLBorderValue Default = FromKey(new XLBorderKey + { + BottomBorder = XLBorderStyleValues.None, + DiagonalBorder = XLBorderStyleValues.None, + DiagonalDown = false, + DiagonalUp = false, + LeftBorder = XLBorderStyleValues.None, + RightBorder = XLBorderStyleValues.None, + TopBorder = XLBorderStyleValues.None, + BottomBorderColor = XLColor.Black.Key, + DiagonalBorderColor = XLColor.Black.Key, + LeftBorderColor = XLColor.Black.Key, + RightBorderColor = XLColor.Black.Key, + TopBorderColor = XLColor.Black.Key + }); + + public XLBorderKey Key { get; private set; } + public XLBorderStyleValues LeftBorder {get { return Key.LeftBorder; } } + public XLColor LeftBorderColor { get; private set; } + public XLBorderStyleValues RightBorder {get { return Key.RightBorder; } } + public XLColor RightBorderColor { get; private set; } + public XLBorderStyleValues TopBorder {get { return Key.TopBorder; } } + public XLColor TopBorderColor { get; private set; } + public XLBorderStyleValues BottomBorder {get { return Key.BottomBorder; } } + public XLColor BottomBorderColor { get; private set; } + public XLBorderStyleValues DiagonalBorder {get { return Key.DiagonalBorder; } } + public XLColor DiagonalBorderColor { get; private set; } + public bool DiagonalUp {get { return Key.DiagonalUp; } } + public bool DiagonalDown {get { return Key.DiagonalDown; } } + + private XLBorderValue(XLBorderKey key) + { + Key = key; + + LeftBorderColor = XLColor.FromKey(Key.LeftBorderColor); + RightBorderColor = XLColor.FromKey(Key.RightBorderColor); + TopBorderColor = XLColor.FromKey(Key.TopBorderColor); + BottomBorderColor = XLColor.FromKey(Key.BottomBorderColor); + DiagonalBorderColor = XLColor.FromKey(Key.DiagonalBorderColor); + } + + public override bool Equals(object obj) + { + var cached = obj as XLBorderValue; + return cached != null && + Key.Equals(cached.Key); + } + + public override int GetHashCode() + { + return -280332839 + Key.GetHashCode(); + } + } +} diff --git a/ClosedXML/Excel/Style/XLColorKey.cs b/ClosedXML/Excel/Style/XLColorKey.cs new file mode 100644 index 0000000..8847c9d --- /dev/null +++ b/ClosedXML/Excel/Style/XLColorKey.cs @@ -0,0 +1,56 @@ +using System; + +namespace ClosedXML.Excel +{ + public struct XLColorKey : IEquatable + { + public XLColorType ColorType { get; set; } + public System.Drawing.Color Color { get; set; } + public int Indexed { get; set; } + public XLThemeColor ThemeColor { get; set; } + public double ThemeTint { get; set; } + + public override int GetHashCode() + { + var hashCode = -331517974; + hashCode = hashCode * -1521134295 + ColorType.GetHashCode(); + hashCode = hashCode * -1521134295 + (ColorType == XLColorType.Indexed ? Indexed.GetHashCode() : 0); + hashCode = hashCode * -1521134295 + (ColorType == XLColorType.Theme ? ThemeColor.GetHashCode() : 0); + hashCode = hashCode * -1521134295 + (ColorType == XLColorType.Theme ? ThemeTint.GetHashCode() : 0); + hashCode = hashCode * -1521134295 + (ColorType == XLColorType.Color ? Color.ToArgb().GetHashCode() : 0); + return hashCode; + } + + public bool Equals(XLColorKey other) + { + if (ColorType == other.ColorType) + { + if (ColorType == XLColorType.Color) + { + // .NET Color.Equals() will return false for Color.FromArgb(255, 255, 255, 255) == Color.White + // Therefore we compare the ToArgb() values + return Color.ToArgb() == other.Color.ToArgb(); + } + if (ColorType == XLColorType.Theme) + { + return + ThemeColor == other.ThemeColor + && Math.Abs(ThemeTint - other.ThemeTint) < XLHelper.Epsilon; + } + return Indexed == other.Indexed; + } + + return false; + } + + public override bool Equals(object obj) + { + if (obj is XLColorKey) + return Equals((XLColorKey)obj); + return base.Equals(obj); + } + + public static bool operator ==(XLColorKey left, XLColorKey right) => left.Equals(right); + public static bool operator !=(XLColorKey left, XLColorKey right) => !(left.Equals(right)); + } +} diff --git a/ClosedXML/Excel/Style/XLFillKey.cs b/ClosedXML/Excel/Style/XLFillKey.cs new file mode 100644 index 0000000..2d0e2e5 --- /dev/null +++ b/ClosedXML/Excel/Style/XLFillKey.cs @@ -0,0 +1,38 @@ +using System; + +namespace ClosedXML.Excel +{ + public struct XLFillKey : IEquatable + { + public XLColorKey BackgroundColor { get; set; } + public XLColorKey PatternColor { get; set; } + public XLFillPatternValues PatternType { get; set; } + + public override int GetHashCode() + { + var hashCode = 2043579837; + hashCode = hashCode * -1521134295 + BackgroundColor.GetHashCode(); + hashCode = hashCode * -1521134295 + PatternColor.GetHashCode(); + hashCode = hashCode * -1521134295 + PatternType.GetHashCode(); + return hashCode; + } + + public bool Equals(XLFillKey other) + { + return + BackgroundColor == other.BackgroundColor + && PatternColor == other.PatternColor + && PatternType == other.PatternType; + } + + public override bool Equals(object obj) + { + if (obj is XLFillKey) + return Equals((XLFillKey)obj); + return base.Equals(obj); + } + + public static bool operator ==(XLFillKey left, XLFillKey right) => left.Equals(right); + public static bool operator !=(XLFillKey left, XLFillKey right) => !(left.Equals(right)); + } +} diff --git a/ClosedXML/Excel/Style/XLFillValue.cs b/ClosedXML/Excel/Style/XLFillValue.cs new file mode 100644 index 0000000..5d7f513 --- /dev/null +++ b/ClosedXML/Excel/Style/XLFillValue.cs @@ -0,0 +1,45 @@ + +using ClosedXML.Excel.Caching; + +namespace ClosedXML.Excel +{ + public sealed class XLFillValue + { + private static readonly XLFillRepository Repository = new XLFillRepository(key => new XLFillValue(key)); + + public static XLFillValue FromKey(XLFillKey key) + { + return Repository.GetOrCreate(key); + } + + internal static readonly XLFillValue Default = FromKey(new XLFillKey + { + BackgroundColor = XLColor.FromIndex(64).Key, + PatternType = XLFillPatternValues.None, + PatternColor = XLColor.FromIndex(64).Key + }); + + public XLFillKey Key { get; private set; } + public XLColor BackgroundColor { get; private set; } + public XLColor PatternColor { get; private set; } + public XLFillPatternValues PatternType {get { return Key.PatternType; } } + private XLFillValue(XLFillKey key) + { + Key = key; + BackgroundColor = XLColor.FromKey(Key.BackgroundColor); + PatternColor = XLColor.FromKey(Key.PatternColor); + } + + public override bool Equals(object obj) + { + var cached = obj as XLFillValue; + return cached != null && + Key.Equals(cached.Key); + } + + public override int GetHashCode() + { + return -280332839 + Key.GetHashCode(); + } + } +} diff --git a/ClosedXML/Excel/Style/XLFontKey.cs b/ClosedXML/Excel/Style/XLFontKey.cs new file mode 100644 index 0000000..fb87fdf --- /dev/null +++ b/ClosedXML/Excel/Style/XLFontKey.cs @@ -0,0 +1,62 @@ +using System; + +namespace ClosedXML.Excel +{ + public struct XLFontKey : IEquatable + { + public bool Bold { get; set; } + public bool Italic { get; set; } + public XLFontUnderlineValues Underline { get; set; } + public bool Strikethrough { get; set; } + public XLFontVerticalTextAlignmentValues VerticalAlignment { get; set; } + public bool Shadow { get; set; } + public double FontSize { get; set; } + public XLColorKey FontColor { get; set; } + public string FontName { get; set; } + public XLFontFamilyNumberingValues FontFamilyNumbering { get; set; } + public XLFontCharSet FontCharSet { get; set; } + + public bool Equals(XLFontKey other) + { + return + Bold == other.Bold + && Italic == other.Italic + && Underline == other.Underline + && Strikethrough == other.Strikethrough + && VerticalAlignment == other.VerticalAlignment + && Shadow == other.Shadow + && FontSize == other.FontSize + && FontColor == other.FontColor + && FontFamilyNumbering == other.FontFamilyNumbering + && FontCharSet == other.FontCharSet + && string.Equals(FontName, other.FontName, StringComparison.InvariantCultureIgnoreCase); + } + + public override bool Equals(object obj) + { + if (obj is XLFontKey) + return Equals((XLFontKey)obj); + return base.Equals(obj); + } + + public override int GetHashCode() + { + var hashCode = 1158783753; + hashCode = hashCode * -1521134295 + Bold.GetHashCode(); + hashCode = hashCode * -1521134295 + Italic.GetHashCode(); + hashCode = hashCode * -1521134295 + Underline.GetHashCode(); + hashCode = hashCode * -1521134295 + Strikethrough.GetHashCode(); + hashCode = hashCode * -1521134295 + VerticalAlignment.GetHashCode(); + hashCode = hashCode * -1521134295 + Shadow.GetHashCode(); + hashCode = hashCode * -1521134295 + FontSize.GetHashCode(); + hashCode = hashCode * -1521134295 + FontColor.GetHashCode(); + hashCode = hashCode * -1521134295 + FontName.ToUpperInvariant().GetHashCode(); + hashCode = hashCode * -1521134295 + FontFamilyNumbering.GetHashCode(); + hashCode = hashCode * -1521134295 + FontCharSet.GetHashCode(); + return hashCode; + } + + public static bool operator ==(XLFontKey left, XLFontKey right) => left.Equals(right); + public static bool operator !=(XLFontKey left, XLFontKey right) => !(left.Equals(right)); + } +} diff --git a/ClosedXML/Excel/Style/XLFontValue.cs b/ClosedXML/Excel/Style/XLFontValue.cs new file mode 100644 index 0000000..f21297a --- /dev/null +++ b/ClosedXML/Excel/Style/XLFontValue.cs @@ -0,0 +1,61 @@ +using ClosedXML.Excel.Caching; +using System.Collections.Generic; + +namespace ClosedXML.Excel +{ + public sealed class XLFontValue + { + private static readonly XLFontRepository Repository = new XLFontRepository(key => new XLFontValue(key)); + + public static XLFontValue FromKey(XLFontKey key) + { + return Repository.GetOrCreate(key); + } + + internal static readonly XLFontValue Default = FromKey(new XLFontKey + { + Bold = false, + Italic = false, + Underline = XLFontUnderlineValues.None, + Strikethrough = false, + VerticalAlignment = XLFontVerticalTextAlignmentValues.Baseline, + FontSize = 11, + FontColor = XLColor.FromArgb(0, 0, 0).Key, + FontName = "Calibri", + FontFamilyNumbering = XLFontFamilyNumberingValues.Swiss, + FontCharSet = XLFontCharSet.Default + }); + + public XLFontKey Key { get; private set; } + public bool Bold {get { return Key.Bold; } } + public bool Italic {get { return Key.Italic; } } + public XLFontUnderlineValues Underline {get { return Key.Underline; } } + public bool Strikethrough {get { return Key.Strikethrough; } } + public XLFontVerticalTextAlignmentValues VerticalAlignment {get { return Key.VerticalAlignment; } } + public bool Shadow {get { return Key.Shadow; } } + public double FontSize {get { return Key.FontSize; } } + public XLColor FontColor { get; private set; } + public string FontName {get { return Key.FontName; } } + public XLFontFamilyNumberingValues FontFamilyNumbering {get { return Key.FontFamilyNumbering; } } + public XLFontCharSet FontCharSet {get { return Key.FontCharSet; } } + + private XLFontValue(XLFontKey key) + { + Key = key; + + FontColor = XLColor.FromKey(Key.FontColor); + } + + public override bool Equals(object obj) + { + var cached = obj as XLFontValue; + return cached != null && + Key.Equals(cached.Key); + } + + public override int GetHashCode() + { + return -280332839 + EqualityComparer.Default.GetHashCode(Key); + } + } +} diff --git a/ClosedXML/Excel/Style/XLNumberFormatKey.cs b/ClosedXML/Excel/Style/XLNumberFormatKey.cs new file mode 100644 index 0000000..0580412 --- /dev/null +++ b/ClosedXML/Excel/Style/XLNumberFormatKey.cs @@ -0,0 +1,35 @@ +using System; + +namespace ClosedXML.Excel +{ + public struct XLNumberFormatKey : IEquatable + { + public int NumberFormatId { get; set; } + public string Format { get; set; } + + public override int GetHashCode() + { + var hashCode = -759193072; + hashCode = hashCode * -1521134295 + NumberFormatId.GetHashCode(); + hashCode = hashCode * -1521134295 + (Format == null ? 0 : Format.ToUpperInvariant().GetHashCode()); + return hashCode; + } + + public bool Equals(XLNumberFormatKey other) + { + return + NumberFormatId == other.NumberFormatId + && string.Equals(Format, other.Format, StringComparison.InvariantCultureIgnoreCase); + } + + public override bool Equals(object obj) + { + if (obj is XLNumberFormatKey) + return Equals((XLNumberFormatKey)obj); + return base.Equals(obj); + } + + public static bool operator ==(XLNumberFormatKey left, XLNumberFormatKey right) => left.Equals(right); + public static bool operator !=(XLNumberFormatKey left, XLNumberFormatKey right) => !(left.Equals(right)); + } +} diff --git a/ClosedXML/Excel/Style/XLNumberFormatValue.cs b/ClosedXML/Excel/Style/XLNumberFormatValue.cs new file mode 100644 index 0000000..78746c0 --- /dev/null +++ b/ClosedXML/Excel/Style/XLNumberFormatValue.cs @@ -0,0 +1,41 @@ +using ClosedXML.Excel.Caching; + +namespace ClosedXML.Excel +{ + public sealed class XLNumberFormatValue + { + private static readonly XLNumberFormatRepository Repository = new XLNumberFormatRepository(key => new XLNumberFormatValue(key)); + + public static XLNumberFormatValue FromKey(XLNumberFormatKey key) + { + return Repository.GetOrCreate(key); + } + + internal static readonly XLNumberFormatValue Default = FromKey(new XLNumberFormatKey + { + NumberFormatId = 0, + Format = string.Empty + }); + + public XLNumberFormatKey Key { get; private set; } + public int NumberFormatId {get { return Key.NumberFormatId; } } + public string Format {get { return Key.Format; } } + + private XLNumberFormatValue(XLNumberFormatKey key) + { + Key = key; + } + + public override bool Equals(object obj) + { + var cached = obj as XLNumberFormatValue; + return cached != null && + Key.Equals(cached.Key); + } + + public override int GetHashCode() + { + return 1507230172 + Key.GetHashCode(); + } + } +} diff --git a/ClosedXML/Excel/Style/XLProtectionKey.cs b/ClosedXML/Excel/Style/XLProtectionKey.cs new file mode 100644 index 0000000..8cd4725 --- /dev/null +++ b/ClosedXML/Excel/Style/XLProtectionKey.cs @@ -0,0 +1,35 @@ +using System; + +namespace ClosedXML.Excel +{ + public struct XLProtectionKey : IEquatable + { + public bool Locked { get; set; } + public bool Hidden { get; set; } + + public override int GetHashCode() + { + var hashCode = -1357408252; + hashCode = hashCode * -1521134295 + Locked.GetHashCode(); + hashCode = hashCode * -1521134295 + Hidden.GetHashCode(); + return hashCode; + } + + public bool Equals(XLProtectionKey other) + { + return + Locked == other.Locked + && Hidden == other.Hidden; + } + + public override bool Equals(object obj) + { + if (obj is XLProtectionKey) + return Equals((XLProtectionKey)obj); + return base.Equals(obj); + } + + public static bool operator ==(XLProtectionKey left, XLProtectionKey right) => left.Equals(right); + public static bool operator !=(XLProtectionKey left, XLProtectionKey right) => !(left.Equals(right)); + } +} diff --git a/ClosedXML/Excel/Style/XLProtectionValue.cs b/ClosedXML/Excel/Style/XLProtectionValue.cs new file mode 100644 index 0000000..3e27ee8 --- /dev/null +++ b/ClosedXML/Excel/Style/XLProtectionValue.cs @@ -0,0 +1,41 @@ +using ClosedXML.Excel.Caching; + +namespace ClosedXML.Excel +{ + public sealed class XLProtectionValue + { + private static readonly XLProtectionRepository Repository = new XLProtectionRepository(key => new XLProtectionValue(key)); + + public static XLProtectionValue FromKey(XLProtectionKey key) + { + return Repository.GetOrCreate(key); + } + + internal static readonly XLProtectionValue Default = FromKey(new XLProtectionKey + { + Locked = true, + Hidden = false + }); + + public XLProtectionKey Key { get; private set; } + public bool Locked {get { return Key.Locked; } } + public bool Hidden {get { return Key.Hidden; } } + + private XLProtectionValue(XLProtectionKey key) + { + Key = key; + } + + public override bool Equals(object obj) + { + var cached = obj as XLProtectionValue; + return cached != null && + Key.Equals(cached.Key); + } + + public override int GetHashCode() + { + return 909014992 + Key.GetHashCode(); + } + } +} diff --git a/ClosedXML/Excel/Style/XLStyleKey.cs b/ClosedXML/Excel/Style/XLStyleKey.cs new file mode 100644 index 0000000..a24fa56 --- /dev/null +++ b/ClosedXML/Excel/Style/XLStyleKey.cs @@ -0,0 +1,50 @@ +using System; + +namespace ClosedXML.Excel +{ + public struct XLStyleKey : IEquatable + { + public XLAlignmentKey Alignment { get; set; } + + public XLBorderKey Border { get; set; } + + public XLFillKey Fill { get; set; } + + public XLFontKey Font { get; set; } + + public XLNumberFormatKey NumberFormat { get; set; } + + public XLProtectionKey Protection { get; set; } + + public override int GetHashCode() + { + var hashCode = -476701294; + hashCode = hashCode * -1521134295 + Alignment.GetHashCode(); + hashCode = hashCode * -1521134295 + Border.GetHashCode(); + hashCode = hashCode * -1521134295 + Fill.GetHashCode(); + hashCode = hashCode * -1521134295 + Font.GetHashCode(); + hashCode = hashCode * -1521134295 + NumberFormat.GetHashCode(); + hashCode = hashCode * -1521134295 + Protection.GetHashCode(); + return hashCode; + } + + public bool Equals(XLStyleKey other) + { + return Alignment == other.Alignment && + Border == other.Border && + Fill == other.Fill && + Font == other.Font && + NumberFormat == other.NumberFormat && + Protection == other.Protection; + } + + public override bool Equals(object obj) + { + if (obj is XLStyleKey) + return Equals((XLStyleKey)obj); + return base.Equals(obj); + } + public static bool operator ==(XLStyleKey left, XLStyleKey right) => left.Equals(right); + public static bool operator !=(XLStyleKey left, XLStyleKey right) => !(left.Equals(right)); + } +} diff --git a/ClosedXML/Excel/Style/XLStyleValue.cs b/ClosedXML/Excel/Style/XLStyleValue.cs new file mode 100644 index 0000000..693b960 --- /dev/null +++ b/ClosedXML/Excel/Style/XLStyleValue.cs @@ -0,0 +1,59 @@ +using ClosedXML.Excel.Caching; + +namespace ClosedXML.Excel +{ + public class XLStyleValue + { + private static readonly XLStyleRepository Repository = new XLStyleRepository(key => new XLStyleValue(key)); + + public static XLStyleValue FromKey(XLStyleKey key) + { + return Repository.GetOrCreate(key); + } + + internal static readonly XLStyleValue Default = FromKey(new XLStyleKey + { + Alignment = XLAlignmentValue.Default.Key, + Border = XLBorderValue.Default.Key, + Fill = XLFillValue.Default.Key, + Font = XLFontValue.Default.Key, + NumberFormat = XLNumberFormatValue.Default.Key, + Protection = XLProtectionValue.Default.Key + }); + + + public XLStyleKey Key { get; private set; } + public XLAlignmentValue Alignment { get; private set; } + public XLBorderValue Border { get; private set; } + public XLFillValue Fill { get; private set; } + public XLFontValue Font { get; private set; } + public XLNumberFormatValue NumberFormat { get; private set; } + public XLProtectionValue Protection { get; private set; } + + internal XLStyleValue(XLStyleKey key) + { + Key = key; + Alignment = XLAlignmentValue.FromKey(Key.Alignment); + Border = XLBorderValue.FromKey(Key.Border); + Fill = XLFillValue.FromKey(Key.Fill); + Font = XLFontValue.FromKey(Key.Font); + NumberFormat = XLNumberFormatValue.FromKey(Key.NumberFormat); + Protection = XLProtectionValue.FromKey(Key.Protection); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(this, obj)) + return true; + + var cached = obj as XLStyleValue; + return cached != null && + Key.Equals(cached.Key); + } + + public override int GetHashCode() + { + return -280332839 + Key.GetHashCode(); + } + } +} diff --git a/ClosedXML_Tests/ClosedXML_Tests.csproj b/ClosedXML_Tests/ClosedXML_Tests.csproj index f7a92c2..199e828 100644 --- a/ClosedXML_Tests/ClosedXML_Tests.csproj +++ b/ClosedXML_Tests/ClosedXML_Tests.csproj @@ -80,6 +80,7 @@ + diff --git a/ClosedXML_Tests/Excel/Caching/SampleRepositoryTests.cs b/ClosedXML_Tests/Excel/Caching/SampleRepositoryTests.cs new file mode 100644 index 0000000..2cbbb5f --- /dev/null +++ b/ClosedXML_Tests/Excel/Caching/SampleRepositoryTests.cs @@ -0,0 +1,153 @@ +using ClosedXML.Excel.Caching; +using NUnit.Framework; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace ClosedXML_Tests.Excel.Caching +{ + [TestFixture] + public class BaseRepositoryTests + { + [Test] + public void DifferentEntitiesWithSameKeyStoredOnce() + { + // Arrange + int key = 12345; + var entity1 = new SampleEntity(key); + var entity2 = new SampleEntity(key); + var sampleRepository = this.CreateSampleRepository(); + + // Act + var storedEntity1 = sampleRepository.Store(key, entity1); + var storedEntity2 = sampleRepository.Store(key, entity2); + + // Assert + Assert.AreSame(entity1, storedEntity1); + Assert.AreSame(entity1, storedEntity2); + Assert.AreNotSame(entity2, storedEntity2); + } + + + [Test] + public void NonUsedReferencesAreGCed() + { +#if !DEBUG + // Arrange + int key = 12345; + var sampleRepository = this.CreateSampleRepository(); + + // Act + var storedEntityRef1 = new WeakReference(sampleRepository.Store(key, new SampleEntity(key))); + + int count = 0; + do + { + Thread.Sleep(50); + GC.Collect(); + count++; + } while (storedEntityRef1.IsAlive && count < 10); + + // Assert + if (count == 10) + Assert.Fail("storedEntityRef1 was not GCed"); + + Assert.IsFalse(sampleRepository.Any()); +#else + Assert.Ignore("Can't run in DEBUG"); +#endif + } + + + [Test] + public void NonUsedReferencesAreGCed2() + { +#if !DEBUG + // Arrange + int countUnique = 30; + int repeatCount = 1000; + SampleEntity[] entities = new SampleEntity[countUnique * repeatCount]; + for (int i = 0; i < countUnique; i++) + { + for (int j = 0; j < repeatCount; j++) + { + entities[i * repeatCount + j] = new SampleEntity(i); + } + } + + var sampleRepository = this.CreateSampleRepository(); + + // Act + Parallel.ForEach(entities, new ParallelOptions { MaxDegreeOfParallelism = 8 }, + e => sampleRepository.Store(e.Key, e)); + + Thread.Sleep(50); + GC.Collect(); + var storedEntries = sampleRepository.ToList(); + + // Assert + Assert.AreEqual(0, storedEntries.Count); +#else + Assert.Ignore("Can't run in DEBUG"); +#endif + } + + [Test] + public void ConcurrentAddingCausesNoDuplication() + { + // Arrange + int countUnique = 30; + int repeatCount = 1000; + SampleEntity[] entities = new SampleEntity[countUnique * repeatCount]; + for (int i = 0; i < countUnique; i++) + { + for (int j = 0; j < repeatCount; j++) + { + entities[i * repeatCount + j] = new SampleEntity(i); + } + } + + var sampleRepository = this.CreateSampleRepository(); + + // Act + Parallel.ForEach(entities, new ParallelOptions { MaxDegreeOfParallelism = 8 }, + e => sampleRepository.Store(e.Key, e)); + var storedEntries = sampleRepository.ToList(); + + // Assert + Assert.AreEqual(countUnique, storedEntries.Count); + Assert.NotNull(entities); // To protect them from GC + } + + + private SampleRepository CreateSampleRepository() + { + return new SampleRepository(); + } + + + /// + /// Class under testing + /// + internal class SampleRepository : XLRepositoryBase + { + public SampleRepository() : base(key => new SampleEntity(key)) + { + } + } + + public class SampleEntity + { + public int Key { get; private set; } + + public SampleEntity(int key) + { + Key = key; + } + } + } + + +}