Newer
Older
ClosedXML / ClosedXML / Excel / ConditionalFormats / XLConditionalFormats.cs
@Rozhkov Alexey Rozhkov Alexey on 15 Nov 2017 4 KB Implementing code review feedback.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClosedXML.Excel
{
    internal class XLConditionalFormats : IXLConditionalFormats
    {
        private readonly List<IXLConditionalFormat> _conditionalFormats = new List<IXLConditionalFormat>();
        public void Add(IXLConditionalFormat conditionalFormat)
        {
            _conditionalFormats.Add(conditionalFormat);
        }

        private bool IsRangeAbove(IXLRangeAddress newAddr, IXLRangeAddress addr)
        {
            return newAddr.FirstAddress.ColumnNumber == addr.FirstAddress.ColumnNumber
                   && newAddr.LastAddress.ColumnNumber == addr.LastAddress.ColumnNumber
                   && newAddr.FirstAddress.RowNumber < addr.FirstAddress.RowNumber
                   && (newAddr.LastAddress.RowNumber+1).Between(addr.FirstAddress.RowNumber, addr.LastAddress.RowNumber);
        }

        private bool IsRangeToLeft(IXLRangeAddress newAddr, IXLRangeAddress addr)
        {
            return newAddr.FirstAddress.RowNumber == addr.FirstAddress.RowNumber
                   && newAddr.LastAddress.RowNumber == addr.LastAddress.RowNumber
                   && newAddr.FirstAddress.ColumnNumber < addr.FirstAddress.ColumnNumber
                   && (newAddr.LastAddress.ColumnNumber+1).Between(addr.FirstAddress.ColumnNumber, addr.LastAddress.ColumnNumber);
        }

        public IEnumerator<IXLConditionalFormat> GetEnumerator()
        {
            return _conditionalFormats.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void Remove(Predicate<IXLConditionalFormat> predicate)
        {
            _conditionalFormats.Where(cf=>predicate(cf)).ForEach(cf=>cf.Range.Dispose());
            _conditionalFormats.RemoveAll(predicate);
        }

        /// <summary>
        /// The method consolidate the same conditional formats, which are located in adjacent ranges.
        /// </summary>
        internal void Consolidate()
        {
            var formats = _conditionalFormats
                .OrderByDescending(x => x.Range.RangeAddress.FirstAddress.RowNumber)
                .ThenByDescending(x => x.Range.RangeAddress.FirstAddress.ColumnNumber);

            var orderedFormats = formats.ToList();

            foreach (var item in formats)
            {
                var itemAddr = item.Range.RangeAddress;
                var itemRowNum = itemAddr.FirstAddress.RowNumber;

                Func<IXLConditionalFormat, bool> IsSameFormat = f => f != item && f.Range.Worksheet.Position == item.Range.Worksheet.Position &&
                                                             XLConditionalFormat.NoRangeComparer.Equals(f, item);

                // search for an adjacent range
                var format = orderedFormats
                    .TakeWhile(f => f.Range.RangeAddress.FirstAddress.RowNumber >= itemRowNum)
                    .FirstOrDefault(f => (IsRangeAbove(itemAddr, f.Range.RangeAddress) || IsRangeToLeft(itemAddr, f.Range.RangeAddress)) && IsSameFormat(f));
                if (format != null)
                {
                    Merge(format, item);
                    _conditionalFormats.Remove(item);
                    orderedFormats.Remove(item);
                    // compress with bottom range
                    var newaddr = format.Range.RangeAddress;
                    var newRowNum = newaddr.FirstAddress.RowNumber;
                    var bottom = orderedFormats
                        .TakeWhile(f => f.Range.RangeAddress.FirstAddress.RowNumber >= newRowNum)
                        .FirstOrDefault(f => IsRangeAbove(newaddr, f.Range.RangeAddress) && IsSameFormat(f));
                    if (bottom != null)
                    {
                        Merge(bottom, format);
                        _conditionalFormats.Remove(format);
                        orderedFormats.Remove(format);
                    }
                    continue;
                }

                // search for an encompassable range
                format = _conditionalFormats.FirstOrDefault(f => f.Range.Contains(item.Range) && IsSameFormat(f));
                if (format != null)
                {
                    _conditionalFormats.Remove(item);
                    orderedFormats.Remove(item);
                }
            }
        }

        private static void Merge(IXLConditionalFormat format, IXLConditionalFormat item)
        {
            foreach (var v in format.Values.ToList())
                format.Values[v.Key] = item.Values[v.Key];
            format.Range.RangeAddress.FirstAddress = item.Range.RangeAddress.FirstAddress;
        }

        public void RemoveAll()
        {
            _conditionalFormats.ForEach(cf => cf.Range.Dispose());
            _conditionalFormats.Clear();
        }
    }
}