Finialized TrackedQueue
This commit is contained in:
parent
36e5a6ac8d
commit
c39e748749
@ -1,175 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LibationWinForms.ProcessQueue
|
|
||||||
{
|
|
||||||
internal class TrackedQueue<T> where T : class
|
|
||||||
{
|
|
||||||
public T Current { get; private set; }
|
|
||||||
private readonly LinkedList<T> Queued = new();
|
|
||||||
private readonly List<T> Completed = new();
|
|
||||||
private readonly object lockObject = new();
|
|
||||||
|
|
||||||
public int Count => Queued.Count + Completed.Count + (Current == null ? 0 : 1);
|
|
||||||
|
|
||||||
public T this[int index]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (index < Completed.Count)
|
|
||||||
return Completed[index];
|
|
||||||
index -= Completed.Count;
|
|
||||||
|
|
||||||
if (index == 0&& Current != null) return Current;
|
|
||||||
|
|
||||||
if (Current != null) index--;
|
|
||||||
|
|
||||||
if (index < Queued.Count) return Queued.ElementAt(index);
|
|
||||||
|
|
||||||
throw new IndexOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<T> QueuedItems()
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
return Queued.ToList();
|
|
||||||
}
|
|
||||||
public List<T> CompletedItems()
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
return Completed.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool QueueCount
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
return Queued.Count > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CompleteCount
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
return Completed.Count > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void ClearQueue()
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
{
|
|
||||||
Queued.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearCompleted()
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
{
|
|
||||||
Completed.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Any(Func<T, bool> predicate)
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
{
|
|
||||||
return (Current != null && predicate(Current)) || Completed.Any(predicate) || Queued.Any(predicate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueuePosition MoveQueuePosition(T item, QueuePositionRequest requestedPosition)
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
{
|
|
||||||
if (Queued.Count == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (Current != null && Current == item)
|
|
||||||
return QueuePosition.Current;
|
|
||||||
if (Completed.Contains(item))
|
|
||||||
return QueuePosition.Completed;
|
|
||||||
return QueuePosition.Absent;
|
|
||||||
}
|
|
||||||
|
|
||||||
var node = Queued.Find(item);
|
|
||||||
if (node is null) return QueuePosition.Absent;
|
|
||||||
|
|
||||||
if ((requestedPosition == QueuePositionRequest.Fisrt || requestedPosition == QueuePositionRequest.OneUp) && Queued.First.Value == item)
|
|
||||||
return QueuePosition.Fisrt;
|
|
||||||
if ((requestedPosition == QueuePositionRequest.Last || requestedPosition == QueuePositionRequest.OneDown) && Queued.Last.Value == item)
|
|
||||||
return QueuePosition.Last;
|
|
||||||
|
|
||||||
if (requestedPosition == QueuePositionRequest.OneUp)
|
|
||||||
{
|
|
||||||
var oneUp = node.Previous;
|
|
||||||
Queued.Remove(node);
|
|
||||||
Queued.AddBefore(oneUp, node.Value);
|
|
||||||
return Queued.First.Value == item? QueuePosition.Fisrt : QueuePosition.OneUp;
|
|
||||||
}
|
|
||||||
else if (requestedPosition == QueuePositionRequest.OneDown)
|
|
||||||
{
|
|
||||||
var oneDown = node.Next;
|
|
||||||
Queued.Remove(node);
|
|
||||||
Queued.AddAfter(oneDown, node.Value);
|
|
||||||
return Queued.Last.Value == item ? QueuePosition.Last : QueuePosition.OneDown;
|
|
||||||
}
|
|
||||||
else if (requestedPosition == QueuePositionRequest.Fisrt)
|
|
||||||
{
|
|
||||||
Queued.Remove(node);
|
|
||||||
Queued.AddFirst(node);
|
|
||||||
return QueuePosition.Fisrt;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Queued.Remove(node);
|
|
||||||
Queued.AddLast(node);
|
|
||||||
return QueuePosition.Last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Remove(T item)
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
{
|
|
||||||
return Queued.Remove(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
{
|
|
||||||
if (Current != null)
|
|
||||||
Completed.Add(Current);
|
|
||||||
if (Queued.Count == 0) return false;
|
|
||||||
Current = Queued.First.Value;
|
|
||||||
Queued.RemoveFirst();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public T PeekNext()
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
return Queued.Count > 0 ? Queued.First.Value : default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EnqueueBook(T item)
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
Queued.AddLast(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
209
Source/LibationWinForms/ProcessQueue/TrackedQueue[T].cs
Normal file
209
Source/LibationWinForms/ProcessQueue/TrackedQueue[T].cs
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace LibationWinForms.ProcessQueue
|
||||||
|
{
|
||||||
|
public enum QueuePosition
|
||||||
|
{
|
||||||
|
Fisrt,
|
||||||
|
OneUp,
|
||||||
|
OneDown,
|
||||||
|
Last
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This data structure is like lifting a metal chain one link at a time.
|
||||||
|
* Each time you grab and lift a new link, the remaining chain shortens
|
||||||
|
* by 1 link and the pile of chain at yuor feet grows by one link. The
|
||||||
|
* index is the link position from the first link you lifted to the last
|
||||||
|
* one in the chain.
|
||||||
|
*/
|
||||||
|
public class TrackedQueue<T> where T : class
|
||||||
|
{
|
||||||
|
public event EventHandler<int> CompletedCountChanged;
|
||||||
|
public event EventHandler<int> QueuededCountChanged;
|
||||||
|
public T Current { get; private set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<T> Queued => _queued;
|
||||||
|
public IReadOnlyList<T> Completed => _completed;
|
||||||
|
|
||||||
|
private readonly List<T> _queued = new();
|
||||||
|
private readonly List<T> _completed = new();
|
||||||
|
private readonly object lockObject = new();
|
||||||
|
|
||||||
|
public T this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
if (index < _completed.Count)
|
||||||
|
return _completed[index];
|
||||||
|
index -= _completed.Count;
|
||||||
|
|
||||||
|
if (index == 0 && Current != null) return Current;
|
||||||
|
|
||||||
|
if (Current != null) index--;
|
||||||
|
|
||||||
|
if (index < _queued.Count) return _queued.ElementAt(index);
|
||||||
|
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
return _queued.Count + _completed.Count + (Current == null ? 0 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(T item)
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
if (_completed.Contains(item))
|
||||||
|
return _completed.IndexOf(item);
|
||||||
|
|
||||||
|
if (Current == item) return _completed.Count;
|
||||||
|
|
||||||
|
if (_queued.Contains(item))
|
||||||
|
return _queued.IndexOf(item) + (Current is null ? 0 : 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveQueued(T item)
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
bool removed = _queued.Remove(item);
|
||||||
|
if (removed)
|
||||||
|
QueuededCountChanged?.Invoke(this, _queued.Count);
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearQueue()
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
_queued.Clear();
|
||||||
|
QueuededCountChanged?.Invoke(this, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearCompleted()
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
_completed.Clear();
|
||||||
|
CompletedCountChanged?.Invoke(this, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Any(Func<T, bool> predicate)
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
return (Current != null && predicate(Current)) || _completed.Any(predicate) || _queued.Any(predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MoveQueuePosition(T item, QueuePosition requestedPosition)
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
if (_queued.Count == 0 || !_queued.Contains(item)) return;
|
||||||
|
|
||||||
|
if ((requestedPosition == QueuePosition.Fisrt || requestedPosition == QueuePosition.OneUp) && _queued[0] == item)
|
||||||
|
return;
|
||||||
|
if ((requestedPosition == QueuePosition.Last || requestedPosition == QueuePosition.OneDown) && _queued[^1] == item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int queueIndex = _queued.IndexOf(item);
|
||||||
|
|
||||||
|
if (requestedPosition == QueuePosition.OneUp)
|
||||||
|
{
|
||||||
|
_queued.RemoveAt(queueIndex);
|
||||||
|
_queued.Insert(queueIndex - 1, item);
|
||||||
|
}
|
||||||
|
else if (requestedPosition == QueuePosition.OneDown)
|
||||||
|
{
|
||||||
|
_queued.RemoveAt(queueIndex);
|
||||||
|
_queued.Insert(queueIndex + 1, item);
|
||||||
|
}
|
||||||
|
else if (requestedPosition == QueuePosition.Fisrt)
|
||||||
|
{
|
||||||
|
_queued.RemoveAt(queueIndex);
|
||||||
|
_queued.Insert(0, item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_queued.RemoveAt(queueIndex);
|
||||||
|
_queued.Insert(_queued.Count, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
if (Current != null)
|
||||||
|
{
|
||||||
|
_completed.Add(Current);
|
||||||
|
CompletedCountChanged?.Invoke(this, _completed.Count);
|
||||||
|
}
|
||||||
|
if (_queued.Count == 0)
|
||||||
|
{
|
||||||
|
Current = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Current = _queued[0];
|
||||||
|
_queued.RemoveAt(0);
|
||||||
|
|
||||||
|
QueuededCountChanged?.Invoke(this, _queued.Count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryPeek(out T item)
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
if (_queued.Count == 0)
|
||||||
|
{
|
||||||
|
item = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
item = _queued[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Peek()
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
if (_queued.Count == 0) throw new InvalidOperationException("Queue empty");
|
||||||
|
return _queued.Count > 0 ? _queued[0] : default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Enqueue(T item)
|
||||||
|
{
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
_queued.Add(item);
|
||||||
|
QueuededCountChanged?.Invoke(this, _queued.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user