blob: 87fa5beb750d8138de80e77aff91600c5f335303 [file] [log] [blame]
/*******************************************************************************
* 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 AppsheetEpplus;
/// <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 {
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 => _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;
}
}