// 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);
}
}
}
}