using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace ClosedXML.Excel
{
/// <summary>
/// Base class for any workbook element that has or may have a style.
/// </summary>
internal abstract class XLStylizedBase : IXLStylized
{
#region Properties
/// <summary>
/// Read-only style property.
/// </summary>
internal XLStyleValue StyleValue { get; private set; }
XLStyleValue IXLStylized.StyleValue
{
get { return StyleValue; }
}
/// <summary>
/// Editable style of the workbook element. Modification of this property DOES affect styles of child objects as well - they will
/// be changed accordingly. Accessing this property causes a new <see cref="XLStyle"/> instance generated so use this property
/// with caution. If you need only _read_ the style consider using <see cref="StyleValue"/> property instead.
/// </summary>
public IXLStyle Style
{
get { return InnerStyle; }
set { SetStyle(value, true); }
}
/// <summary>
/// Editable style of the workbook element. Modification of this property DOES NOT affect styles of child objects.
/// Accessing this property causes a new <see cref="XLStyle"/> instance generated so use this property with caution. If you need
/// only _read_ the style consider using <see cref="StyleValue"/> property instead.
/// </summary>
public IXLStyle InnerStyle
{
get { return new XLStyle(this, StyleValue.Key); }
set { SetStyle(value, false); }
}
/// <summary>
/// Get a collection of stylized entities which current entity's style changes should be propagated to.
/// </summary>
protected abstract IEnumerable<XLStylizedBase> Children { get; }
public abstract IXLRanges RangesUsed { get; }
public abstract IEnumerable<IXLStyle> Styles { get; }
#endregion Properties
protected XLStylizedBase(XLStyleValue styleValue)
{
StyleValue = styleValue;
}
#region Private methods
private void SetStyle(IXLStyle style, bool propagate = false)
{
if (style is XLStyle xlStyle)
SetStyle(xlStyle.Value, propagate);
else
SetStyle(XLStyleValue.FromKey(XLStyle.GenerateKey(style)), propagate);
}
/// <summary>
/// Apply specified style to the container.
/// </summary>
/// <param name="value">Style to apply.</param>
/// <param name="propagate">Whether or not propagate the style to inner ranges.</param>
private void SetStyle(XLStyleValue value, bool propagate = false)
{
StyleValue = value;
if (propagate)
{
Children.ForEach(child => child.SetStyle(StyleValue, true));
}
}
private static ReferenceEqualityComparer<XLStyleValue> _comparer = new ReferenceEqualityComparer<XLStyleValue>();
void IXLStylized.ModifyStyle(Func<XLStyleKey, XLStyleKey> modification)
{
var children = GetChildrenRecursively(this)
.GroupBy(child => child.StyleValue, _comparer);
foreach (var group in children)
{
var styleKey = modification(group.Key.Key);
var styleValue = XLStyleValue.FromKey(styleKey);
foreach (var child in group)
{
child.StyleValue = styleValue;
}
}
}
private IEnumerable<XLStylizedBase> GetChildrenRecursively(XLStylizedBase parent)
{
return new List<XLStylizedBase> { parent }
.Union(parent.Children.Where(child => child != parent).SelectMany(child => GetChildrenRecursively(child)));
}
#endregion Private methods
#region Nested classes
public sealed class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public bool Equals(T x, T y) => ReferenceEquals(x, y);
public int GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj);
}
#endregion Nested classes
}
}