|  | /******************************************************************************* | 
|  | * You may amend and distribute as you like, but don't remove this header! | 
|  | * | 
|  | * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. | 
|  | * See http://www.codeplex.com/EPPlus for details. | 
|  | * | 
|  | * Copyright (C) 2011  Jan Källman | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  |  | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 
|  | * See the GNU Lesser General Public License for more details. | 
|  | * | 
|  | * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php | 
|  | * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html | 
|  | * | 
|  | * All code and executables are provided "as is" with no warranty either express or implied. | 
|  | * The author accepts no liability for any damage or loss of business that this product may cause. | 
|  | * | 
|  | * Code change notes: | 
|  | * | 
|  | * Author							Change						Date | 
|  | * ****************************************************************************** | 
|  | * Jan Källman		    Added       		        2010-02-04 | 
|  | * Jan Källman		    License changed GPL-->LGPL  2011-12-27 | 
|  | *******************************************************************************/ | 
|  |  | 
|  | using System; | 
|  | using System.Collections; | 
|  | using System.Collections.Generic; | 
|  |  | 
|  | namespace OfficeOpenXml; | 
|  |  | 
|  | /// <summary> | 
|  | /// This is the store for all Rows, Columns and Cells. | 
|  | /// It is a Dictionary implementation that allows you to change the Key (the RowID, ColumnID or CellID ) | 
|  | /// </summary> | 
|  | internal class RangeCollection : IEnumerator<IRangeId>, IEnumerable, IDisposable { | 
|  | private class IndexItem { | 
|  | internal IndexItem(ulong cellId) { | 
|  | RangeID = cellId; | 
|  | } | 
|  |  | 
|  | internal IndexItem(ulong cellId, int listPointer) { | 
|  | RangeID = cellId; | 
|  | ListPointer = listPointer; | 
|  | } | 
|  |  | 
|  | internal ulong RangeID; | 
|  | internal int ListPointer; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Compares an IndexItem | 
|  | /// </summary> | 
|  | internal class Compare : IComparer<IndexItem> { | 
|  | int IComparer<IndexItem>.Compare(IndexItem x, IndexItem y) { | 
|  | return x.RangeID < y.RangeID | 
|  | ? -1 | 
|  | : x.RangeID > y.RangeID | 
|  | ? 1 | 
|  | : 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | private IndexItem[] _cellIndex; | 
|  | private List<IRangeId> _cells; | 
|  | private static readonly Compare _comparer = new(); | 
|  |  | 
|  | /// <summary> | 
|  | /// Creates a new collection | 
|  | /// </summary> | 
|  | /// <param name="cells">The Cells. This list must be sorted</param> | 
|  | internal RangeCollection(List<IRangeId> cells) { | 
|  | _cells = cells; | 
|  | InitSize(_cells); | 
|  | for (int i = 0; i < _cells.Count; i++) { | 
|  | _cellIndex[i] = new(cells[i].RangeID, i); | 
|  | } | 
|  | } | 
|  |  | 
|  | ~RangeCollection() { | 
|  | _cells = null; | 
|  | _cellIndex = null; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Return the item with the RangeID | 
|  | /// </summary> | 
|  | /// <param name="rangeId"></param> | 
|  | /// <returns></returns> | 
|  | internal IRangeId this[ulong rangeId] => _cells[_cellIndex[IndexOf(rangeId)].ListPointer]; | 
|  |  | 
|  | /// <summary> | 
|  | /// Return specified index from the sorted list | 
|  | /// </summary> | 
|  | /// <param name="index"></param> | 
|  | /// <returns></returns> | 
|  | internal IRangeId this[int index] => _cells[_cellIndex[index].ListPointer]; | 
|  |  | 
|  | internal int Count { | 
|  | get { return _cells.Count; } | 
|  | } | 
|  |  | 
|  | internal void Add(IRangeId cell) { | 
|  | var ix = IndexOf(cell.RangeID); | 
|  | if (ix >= 0) { | 
|  | throw (new("Item already exists")); | 
|  | } | 
|  | Insert(~ix, cell); | 
|  | } | 
|  |  | 
|  | internal void Delete(ulong key) { | 
|  | var ix = IndexOf(key); | 
|  | if (ix < 0) { | 
|  | throw (new("Key does not exists")); | 
|  | } | 
|  | int listPointer = _cellIndex[ix].ListPointer; | 
|  | Array.Copy(_cellIndex, ix + 1, _cellIndex, ix, _cells.Count - ix - 1); | 
|  | _cells.RemoveAt(listPointer); | 
|  |  | 
|  | //Item is removed subtract one from all items with greater ListPointer | 
|  | for (int i = 0; i < _cells.Count; i++) { | 
|  | if (_cellIndex[i].ListPointer >= listPointer) { | 
|  | _cellIndex[i].ListPointer--; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | internal int IndexOf(ulong key) { | 
|  | return Array.BinarySearch(_cellIndex, 0, _cells.Count, new(key), _comparer); | 
|  | } | 
|  |  | 
|  | internal bool ContainsKey(ulong key) { | 
|  | return IndexOf(key) < 0 ? false : true; | 
|  | } | 
|  |  | 
|  | private int _size { get; set; } | 
|  |  | 
|  | /// <summary> | 
|  | /// Insert a number of rows in the collecion but dont update the cell only the index | 
|  | /// </summary> | 
|  | /// <param name="rowId"></param> | 
|  | /// <param name="rows"></param> | 
|  | /// <returns>Index of first rangeItem</returns> | 
|  | internal int InsertRowsUpdateIndex(ulong rowId, int rows) { | 
|  | int index = IndexOf(rowId); | 
|  | if (index < 0) { | 
|  | index = ~index; //No match found invert to get start cell | 
|  | } | 
|  | ulong rowAdd = (((ulong)rows) << 29); | 
|  | for (int i = index; i < _cells.Count; i++) { | 
|  | _cellIndex[i].RangeID += rowAdd; | 
|  | } | 
|  | return index; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Insert a number of rows in the collecion | 
|  | /// </summary> | 
|  | /// <param name="rowId"></param> | 
|  | /// <param name="rows"></param> | 
|  | /// <returns>Index of first rangeItem</returns> | 
|  | internal int InsertRows(ulong rowId, int rows) { | 
|  | int index = IndexOf(rowId); | 
|  | if (index < 0) { | 
|  | index = ~index; //No match found invert to get start cell | 
|  | } | 
|  | ulong rowAdd = (((ulong)rows) << 29); | 
|  | for (int i = index; i < _cells.Count; i++) { | 
|  | _cellIndex[i].RangeID += rowAdd; | 
|  | _cells[_cellIndex[i].ListPointer].RangeID += rowAdd; | 
|  | } | 
|  | return index; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Delete rows from the collecion | 
|  | /// </summary> | 
|  | /// <param name="rowId"></param> | 
|  | /// <param name="rows"></param> | 
|  | /// <param name="updateCells">Update range id's on cells</param> | 
|  | internal int DeleteRows(ulong rowId, int rows, bool updateCells) { | 
|  | ulong rowAdd = (((ulong)rows) << 29); | 
|  | var index = IndexOf(rowId); | 
|  | if (index < 0) { | 
|  | index = ~index; //No match found invert to get start cell | 
|  | } | 
|  |  | 
|  | if (index >= _cells.Count || _cellIndex[index] == null) { | 
|  | return -1; //No row above this row | 
|  | } | 
|  | while (index < _cells.Count && _cellIndex[index].RangeID < rowId + rowAdd) { | 
|  | Delete(_cellIndex[index].RangeID); | 
|  | } | 
|  |  | 
|  | int updIndex = IndexOf(rowId + rowAdd); | 
|  | if (updIndex < 0) { | 
|  | updIndex = ~updIndex; //No match found invert to get start cell | 
|  | } | 
|  |  | 
|  | for (int i = updIndex; i < _cells.Count; i++) { | 
|  | _cellIndex[i].RangeID -= rowAdd; //Change the index | 
|  | if (updateCells) { | 
|  | _cells[_cellIndex[i].ListPointer].RangeID -= rowAdd; //Change the cell/row or column object | 
|  | } | 
|  | } | 
|  | return index; | 
|  | } | 
|  |  | 
|  | internal void InsertColumn(ulong columnId, int columns) { | 
|  | throw (new("Working on it...")); | 
|  | } | 
|  |  | 
|  | internal void DeleteColumn(ulong columnId, int columns) { | 
|  | throw (new("Working on it...")); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Init the size starting from 128 items. Double the size until the list fits. | 
|  | /// </summary> | 
|  | /// <param name="_cells"></param> | 
|  | private void InitSize(List<IRangeId> cells) { | 
|  | _size = 128; | 
|  | while (cells.Count > _size) { | 
|  | _size <<= 1; | 
|  | } | 
|  | _cellIndex = new IndexItem[_size]; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Check the size and double the size if out of bound | 
|  | /// </summary> | 
|  | private void CheckSize() { | 
|  | if (_cells.Count >= _size) { | 
|  | _size <<= 1; | 
|  | Array.Resize(ref _cellIndex, _size); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void Insert(int ix, IRangeId cell) { | 
|  | CheckSize(); | 
|  | Array.Copy(_cellIndex, ix, _cellIndex, ix + 1, _cells.Count - ix); | 
|  | _cellIndex[ix] = new(cell.RangeID, _cells.Count); | 
|  | _cells.Add(cell); | 
|  | } | 
|  |  | 
|  | IRangeId IEnumerator<IRangeId>.Current { | 
|  | get { throw new NotImplementedException(); } | 
|  | } | 
|  |  | 
|  | void IDisposable.Dispose() { | 
|  | _ix = -1; | 
|  | } | 
|  |  | 
|  | private int _ix = -1; | 
|  |  | 
|  | object IEnumerator.Current { | 
|  | get { return _cells[_cellIndex[_ix].ListPointer]; } | 
|  | } | 
|  |  | 
|  | bool IEnumerator.MoveNext() { | 
|  | _ix++; | 
|  | return _ix < _cells.Count; | 
|  | } | 
|  |  | 
|  | void IEnumerator.Reset() { | 
|  | _ix = -1; | 
|  | } | 
|  |  | 
|  | IEnumerator IEnumerable.GetEnumerator() { | 
|  | return MemberwiseClone() as IEnumerator; | 
|  | } | 
|  | } |