// Copyright (c) Alexandre Mutel. All rights reserved. // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using Markdig.Helpers; using Markdig.Parsers; namespace Markdig.Syntax { /// /// A base class for container blocks. /// /// [DebuggerDisplay("{GetType().Name} Count = {Count}")] public abstract class ContainerBlock : Block, IList { private Block[] children; /// /// Initializes a new instance of the class. /// /// The parser used to create this block. protected ContainerBlock(BlockParser parser) : base(parser) { children = ArrayHelper.Empty; } /// /// Gets the last child. /// public Block LastChild => Count > 0 ? children[Count - 1] : null; /// /// Specialize enumerator. /// /// public Enumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(Block item) { if (item == null) throw new ArgumentNullException(nameof(item)); if (item.Parent != null) { throw new ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)"); } if (Count == children.Length) { EnsureCapacity(Count + 1); } children[Count++] = item; item.Parent = this; UpdateSpanEnd(item.Span.End); } private void EnsureCapacity(int min) { if (children.Length < min) { int num = (children.Length == 0) ? 4 : (children.Length * 2); if (num < min) { num = min; } var destinationArray = new Block[num]; if (Count > 0) { Array.Copy(children, 0, destinationArray, 0, Count); } children = destinationArray; } } public void Clear() { for (int i = 0; i < Count; i++) { children[i].Parent = null; children[i] = null; } Count = 0; } public bool Contains(Block item) { if (item == null) throw new ArgumentNullException(nameof(item)); for (int i = 0; i < Count; i++) { if (children[i] == item) { return true; } } return false; } public void CopyTo(Block[] array, int arrayIndex) { Array.Copy(children, 0, array, arrayIndex, Count); } public bool Remove(Block item) { if (item == null) throw new ArgumentNullException(nameof(item)); for (int i = Count - 1; i >= 0; i--) { if (children[i] == item) { RemoveAt(i); return true; } } return false; } public int Count { get; private set; } public bool IsReadOnly => false; public int IndexOf(Block item) { if (item == null) throw new ArgumentNullException(nameof(item)); for (int i = 0; i < Count; i++) { if (children[i] == item) { return i; } } return -1; } public void Insert(int index, Block item) { if (item == null) throw new ArgumentNullException(nameof(item)); if (item.Parent != null) { throw new ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)"); } if (index < 0 || index > Count) { throw new ArgumentOutOfRangeException(nameof(index)); } if (Count == children.Length) { EnsureCapacity(Count + 1); } if (index < Count) { Array.Copy(children, index, children, index + 1, Count - index); } children[index] = item; Count++; item.Parent = this; } public void RemoveAt(int index) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index)); Count--; // previous children var item = children[index]; item.Parent = null; if (index < Count) { Array.Copy(children, index + 1, children, index, Count - index); } children[Count] = null; } public Block this[int index] { get { var array = children; if ((uint)index >= (uint)array.Length || index >= Count) { ThrowHelper.ThrowIndexOutOfRangeException(); return null; } return array[index]; } set { if ((uint)index >= (uint)Count) ThrowHelper.ThrowIndexOutOfRangeException(); children[index] = value; } } public void Sort(IComparer comparer) { if (comparer == null) throw new ArgumentNullException(nameof(comparer)); Array.Sort(children, 0, Count, comparer); } public void Sort(Comparison comparison) { if (comparison == null) throw new ArgumentNullException(nameof(comparison)); Array.Sort(children, 0, Count, new BlockComparer(comparison)); } #region Nested type: Enumerator [StructLayout(LayoutKind.Sequential)] public struct Enumerator : IEnumerator { private readonly ContainerBlock block; private int index; private Block current; internal Enumerator(ContainerBlock block) { this.block = block; index = 0; current = null; } public Block Current => current; object IEnumerator.Current => Current; public void Dispose() { } public bool MoveNext() { if (index < block.Count) { current = block[index]; index++; return true; } return MoveNextRare(); } private bool MoveNextRare() { index = block.Count + 1; current = null; return false; } void IEnumerator.Reset() { index = 0; current = null; } } #endregion private sealed class BlockComparer : IComparer { private readonly Comparison comparison; public BlockComparer(Comparison comparison) { this.comparison = comparison; } public int Compare(Block x, Block y) { return comparison(x, y); } } } }