namespace Spectre.Console; /// /// Represents a collection holding table rows. /// public sealed class TableRowCollection : IReadOnlyList { private readonly Table _table; private readonly IList _list; private readonly object _lock; /// TableRow IReadOnlyList.this[int index] { get { lock (_lock) { return _list[index]; } } } /// /// Gets the number of rows in the collection. /// public int Count { get { lock (_lock) { return _list.Count; } } } internal TableRowCollection(Table table) { _table = table ?? throw new ArgumentNullException(nameof(table)); _list = new List(); _lock = new object(); } /// /// Adds a new row. /// /// The columns that are part of the row to add. /// The index of the added item. public int Add(IEnumerable columns) { if (columns is null) { throw new ArgumentNullException(nameof(columns)); } lock (_lock) { var row = CreateRow(columns); _list.Add(row); return _list.IndexOf(row); } } /// /// Inserts a new row at the specified index. /// /// The index to insert the row at. /// The columns that are part of the row to insert. /// The index of the inserted item. public int Insert(int index, IEnumerable columns) { if (columns is null) { throw new ArgumentNullException(nameof(columns)); } lock (_lock) { var row = CreateRow(columns); _list.Insert(index, row); return _list.IndexOf(row); } } /// /// Update a table cell at the specified index. /// /// Index of cell row. /// index of cell column. /// The new cells details. public void Update(int row, int column, IRenderable cellData) { if (cellData is null) { throw new ArgumentNullException(nameof(cellData)); } lock (_lock) { if (row < 0) { throw new IndexOutOfRangeException("Table row index cannot be negative."); } else if (row >= _list.Count) { throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table."); } var tableRow = _list.ElementAt(row); var currentRenderables = tableRow.ToList(); if (column < 0) { throw new IndexOutOfRangeException("Table column index cannot be negative."); } else if (column >= currentRenderables.Count) { throw new IndexOutOfRangeException("Table column index cannot exceed the number of rows in the table."); } currentRenderables.RemoveAt(column); currentRenderables.Insert(column, cellData); var newTableRow = new TableRow(currentRenderables); _list.RemoveAt(row); _list.Insert(row, newTableRow); } } /// /// Removes a row at the specified index. /// /// The index to remove a row at. public void RemoveAt(int index) { lock (_lock) { if (index < 0) { throw new IndexOutOfRangeException("Table row index cannot be negative."); } else if (index >= _list.Count) { throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table."); } _list.RemoveAt(index); } } /// /// Clears all rows. /// public void Clear() { lock (_lock) { _list.Clear(); } } /// public IEnumerator GetEnumerator() { lock (_lock) { var items = new TableRow[_list.Count]; _list.CopyTo(items, 0); return new TableRowEnumerator(items); } } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private TableRow CreateRow(IEnumerable columns) { var row = new TableRow(columns); if (row.Count > _table.Columns.Count) { throw new InvalidOperationException("The number of row columns are greater than the number of table columns."); } // Need to add missing columns if (row.Count < _table.Columns.Count) { var diff = _table.Columns.Count - row.Count; Enumerable.Range(0, diff).ForEach(_ => row.Add(Text.Empty)); } return row; } }