com.fofx.observething 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/README.md.meta +7 -0
- package/Runtime/CollectionIdProvider.cs +72 -0
- package/Runtime/CollectionIdProvider.cs.meta +2 -0
- package/Runtime/IObservable.cs +66 -0
- package/Runtime/IObservable.cs.meta +2 -0
- package/Runtime/Implementations/Observable.cs +30 -0
- package/Runtime/Implementations/Observable.cs.meta +2 -0
- package/Runtime/Implementations/ObservableBase.cs +173 -0
- package/Runtime/Implementations/ObservableBase.cs.meta +2 -0
- package/Runtime/Implementations/ObservableCollection.cs +141 -0
- package/Runtime/Implementations/ObservableCollection.cs.meta +2 -0
- package/Runtime/Implementations/ObservableDictionary.cs +166 -0
- package/Runtime/Implementations/ObservableDictionary.cs.meta +2 -0
- package/Runtime/Implementations/ObservableList.cs +163 -0
- package/Runtime/Implementations/ObservableList.cs.meta +2 -0
- package/Runtime/Implementations/ObservableSet.cs +137 -0
- package/Runtime/Implementations/ObservableSet.cs.meta +2 -0
- package/Runtime/Implementations/ObservableValue.cs +59 -0
- package/Runtime/Implementations/ObservableValue.cs.meta +2 -0
- package/Runtime/Implementations.meta +8 -0
- package/Runtime/Observables/BatchObservable.cs +72 -0
- package/Runtime/Observables/BatchObservable.cs.meta +2 -0
- package/Runtime/Observables/CastObservable.cs +128 -0
- package/Runtime/Observables/CastObservable.cs.meta +2 -0
- package/Runtime/Observables/CombineObservable.cs +96 -0
- package/Runtime/Observables/CombineObservable.cs.meta +2 -0
- package/Runtime/Observables/CombineValueObservable.cs +319 -0
- package/Runtime/Observables/CombineValueObservable.cs.meta +2 -0
- package/Runtime/Observables/ConcatObservable.cs +75 -0
- package/Runtime/Observables/ConcatObservable.cs.meta +2 -0
- package/Runtime/Observables/ContainsObservable.cs +71 -0
- package/Runtime/Observables/ContainsObservable.cs.meta +2 -0
- package/Runtime/Observables/CountObservable.cs +29 -0
- package/Runtime/Observables/CountObservable.cs.meta +2 -0
- package/Runtime/Observables/DistinctObservable.cs +55 -0
- package/Runtime/Observables/DistinctObservable.cs.meta +2 -0
- package/Runtime/Observables/FirstObservable.cs +53 -0
- package/Runtime/Observables/FirstObservable.cs.meta +2 -0
- package/Runtime/Observables/IndexOfObservable.cs +83 -0
- package/Runtime/Observables/IndexOfObservable.cs.meta +2 -0
- package/Runtime/Observables/OperationStreamObservable.cs +194 -0
- package/Runtime/Observables/OperationStreamObservable.cs.meta +2 -0
- package/Runtime/Observables/OrderByObservable.cs +116 -0
- package/Runtime/Observables/OrderByObservable.cs.meta +2 -0
- package/Runtime/Observables/SelectCollectionObservable.cs +29 -0
- package/Runtime/Observables/SelectCollectionObservable.cs.meta +2 -0
- package/Runtime/Observables/SelectListObservable.cs +29 -0
- package/Runtime/Observables/SelectListObservable.cs.meta +2 -0
- package/Runtime/Observables/SelectManyObservable.cs +78 -0
- package/Runtime/Observables/SelectManyObservable.cs.meta +2 -0
- package/Runtime/Observables/SelectValueObservable.cs +28 -0
- package/Runtime/Observables/SelectValueObservable.cs.meta +2 -0
- package/Runtime/Observables/SkipWhileObservable.cs +32 -0
- package/Runtime/Observables/SkipWhileObservable.cs.meta +2 -0
- package/Runtime/Observables/ThenObservable.cs +209 -0
- package/Runtime/Observables/ThenObservable.cs.meta +2 -0
- package/Runtime/Observables/ToDictionaryObservable.cs +35 -0
- package/Runtime/Observables/ToDictionaryObservable.cs.meta +2 -0
- package/Runtime/Observables/TrackObservable.cs +74 -0
- package/Runtime/Observables/TrackObservable.cs.meta +2 -0
- package/Runtime/Observables/UnwrapCollectionObservable.cs +64 -0
- package/Runtime/Observables/UnwrapCollectionObservable.cs.meta +2 -0
- package/Runtime/Observables/UnwrapListObservable.cs +69 -0
- package/Runtime/Observables/UnwrapListObservable.cs.meta +2 -0
- package/Runtime/Observables/UnwrapValueObservable.cs +56 -0
- package/Runtime/Observables/UnwrapValueObservable.cs.meta +2 -0
- package/Runtime/Observables/WhereObservable.cs +85 -0
- package/Runtime/Observables/WhereObservable.cs.meta +2 -0
- package/Runtime/Observables/WithPreviousObservable.cs +36 -0
- package/Runtime/Observables/WithPreviousObservable.cs.meta +2 -0
- package/Runtime/Observables.cs +467 -0
- package/Runtime/Observables.cs.meta +2 -0
- package/Runtime/Observables.meta +8 -0
- package/Runtime/ObservationContext.cs +126 -0
- package/Runtime/ObservationContext.cs.meta +2 -0
- package/Runtime/Observers.cs +403 -0
- package/Runtime/Observers.cs.meta +2 -0
- package/Runtime/Operators/BatchOperator.cs +71 -0
- package/Runtime/Operators/BatchOperator.cs.meta +2 -0
- package/Runtime/Operators/CollectionOperator.cs +30 -0
- package/Runtime/Operators/CollectionOperator.cs.meta +2 -0
- package/Runtime/Operators/DictionaryOperator.cs +45 -0
- package/Runtime/Operators/DictionaryOperator.cs.meta +2 -0
- package/Runtime/Operators/IInitializationOperationsProvider.cs +10 -0
- package/Runtime/Operators/IInitializationOperationsProvider.cs.meta +2 -0
- package/Runtime/Operators/ListOperator.cs +44 -0
- package/Runtime/Operators/ListOperator.cs.meta +2 -0
- package/Runtime/Operators/ObservableOperator.cs +75 -0
- package/Runtime/Operators/ObservableOperator.cs.meta +2 -0
- package/Runtime/Operators/Operator.cs +65 -0
- package/Runtime/Operators/Operator.cs.meta +2 -0
- package/Runtime/Operators/SetOperator.cs +44 -0
- package/Runtime/Operators/SetOperator.cs.meta +2 -0
- package/Runtime/Operators/ValueOperator.cs +30 -0
- package/Runtime/Operators/ValueOperator.cs.meta +2 -0
- package/Runtime/Operators.meta +8 -0
- package/Runtime/PriorityQueue.cs +1096 -0
- package/Runtime/PriorityQueue.cs.meta +2 -0
- package/Runtime/com.fofx.observething.asmdef +14 -0
- package/Runtime/com.fofx.observething.asmdef.meta +7 -0
- package/Runtime.meta +8 -0
- package/Tests/CollectionObservableTests.cs +739 -0
- package/Tests/CollectionObservableTests.cs.meta +2 -0
- package/Tests/DictionaryObservableTests.cs +85 -0
- package/Tests/DictionaryObservableTests.cs.meta +2 -0
- package/Tests/ListObservableTests.cs +148 -0
- package/Tests/ListObservableTests.cs.meta +2 -0
- package/Tests/ObservationContextTests.cs +359 -0
- package/Tests/ObservationContextTests.cs.meta +2 -0
- package/Tests/ValueObservableTests.cs +322 -0
- package/Tests/ValueObservableTests.cs.meta +2 -0
- package/Tests/com.fofx.observething.tests.asmdef +24 -0
- package/Tests/com.fofx.observething.tests.asmdef.meta +7 -0
- package/Tests.meta +8 -0
- package/package.json +24 -0
- package/package.json.meta +7 -0
package/README.md
ADDED
package/README.md.meta
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
|
|
3
|
+
namespace ObserveThing
|
|
4
|
+
{
|
|
5
|
+
public class CollectionIdProvider
|
|
6
|
+
{
|
|
7
|
+
private Func<uint, bool> _validateId;
|
|
8
|
+
private bool _validateBeforeFirstLoop;
|
|
9
|
+
private uint _nextId = 0;
|
|
10
|
+
private bool _looped;
|
|
11
|
+
|
|
12
|
+
public CollectionIdProvider(Func<uint, bool> validateId, bool validateBeforeFirstLoop = false)
|
|
13
|
+
{
|
|
14
|
+
_validateId = validateId;
|
|
15
|
+
_validateBeforeFirstLoop = validateBeforeFirstLoop;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public uint GetUnusedId()
|
|
19
|
+
{
|
|
20
|
+
uint id = _nextId;
|
|
21
|
+
|
|
22
|
+
if (_looped || _validateBeforeFirstLoop)
|
|
23
|
+
{
|
|
24
|
+
bool found = false;
|
|
25
|
+
|
|
26
|
+
for (uint i = _nextId; i < uint.MaxValue; i++)
|
|
27
|
+
{
|
|
28
|
+
if (_validateId(i))
|
|
29
|
+
{
|
|
30
|
+
id = i;
|
|
31
|
+
found = true;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!found)
|
|
37
|
+
{
|
|
38
|
+
for (uint i = 0; i < _nextId; i++)
|
|
39
|
+
{
|
|
40
|
+
if (_validateId(i))
|
|
41
|
+
{
|
|
42
|
+
id = i;
|
|
43
|
+
found = true;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!found)
|
|
49
|
+
throw new Exception("No valid ID found!");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (id == uint.MaxValue)
|
|
54
|
+
{
|
|
55
|
+
_nextId = 0;
|
|
56
|
+
_looped = true;
|
|
57
|
+
}
|
|
58
|
+
else
|
|
59
|
+
{
|
|
60
|
+
_nextId = id + 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return id;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public void Reset()
|
|
67
|
+
{
|
|
68
|
+
_looped = false;
|
|
69
|
+
_nextId = 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
|
|
4
|
+
namespace ObserveThing
|
|
5
|
+
{
|
|
6
|
+
public interface IObservable<out T> where T : IOperation
|
|
7
|
+
{
|
|
8
|
+
ObservationContext context { get; }
|
|
9
|
+
IDisposable Subscribe(IObserver<T> observer, bool immediate = default, uint? priority = default);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public interface IValueObservable : IObservable<IOperation>
|
|
13
|
+
{
|
|
14
|
+
IDisposable Subscribe(IValueObserver observer, bool immediate = default, uint? priority = default);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public interface IValueObservable<out T> : IValueObservable
|
|
18
|
+
{
|
|
19
|
+
IDisposable Subscribe(IValueObserver<T> observer, bool immediate = default, uint? priority = default);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public interface ICollectionObservable : IObservable<IOperation>
|
|
23
|
+
{
|
|
24
|
+
IDisposable Subscribe(ICollectionObserver observer, bool immediate = default, uint? priority = default);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public interface ICollectionObservable<out T> : ICollectionObservable
|
|
28
|
+
{
|
|
29
|
+
IDisposable Subscribe(ICollectionObserver<T> observer, bool immediate = default, uint? priority = default);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public interface IListObservable : ICollectionObservable
|
|
33
|
+
{
|
|
34
|
+
IDisposable Subscribe(IListObserver observer, bool immediate = default, uint? priority = default);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public interface IListObservable<out T> : IListObservable, ICollectionObservable<T>
|
|
38
|
+
{
|
|
39
|
+
IDisposable Subscribe(IListObserver<T> observer, bool immediate = default, uint? priority = default);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public interface ISetObservable : ICollectionObservable
|
|
43
|
+
{
|
|
44
|
+
IDisposable Subscribe(ISetObserver observer, bool immediate = default, uint? priority = default);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public interface ISetObservable<out T> : ISetObservable, ICollectionObservable<T>
|
|
48
|
+
{
|
|
49
|
+
IDisposable Subscribe(ISetObserver<T> observer, bool immediate = default, uint? priority = default);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public interface IDictionaryObservable : ICollectionObservable
|
|
53
|
+
{
|
|
54
|
+
IDisposable Subscribe(IDictionaryObserver observer, bool immediate = default, uint? priority = default);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public interface IDictionaryObservable<TKey, TValue> : IDictionaryObservable, ICollectionObservable<KeyValuePair<TKey, TValue>>
|
|
58
|
+
{
|
|
59
|
+
IDisposable Subscribe(IDictionaryObserver<TKey, TValue> observer, bool immediate = default, uint? priority = default);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// public interface IBatchObservable<out T> : IObservable<IOperation> where T : IOperation
|
|
63
|
+
// {
|
|
64
|
+
// public IDisposable Subscribe(IBatchObserver<T> observer, bool immediate = false, uint? priority = default);
|
|
65
|
+
// }
|
|
66
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
|
|
4
|
+
namespace ObserveThing
|
|
5
|
+
{
|
|
6
|
+
public class Observable<T> : ObservableBase<IObserver<T>, T> where T : IOperation
|
|
7
|
+
{
|
|
8
|
+
private IInitializationOperationsProvider<T> _initializationOperationsProvider;
|
|
9
|
+
|
|
10
|
+
public Observable(ObservationContext context, IInitializationOperationsProvider<T> initializationOperationsProvider) : base(context)
|
|
11
|
+
{
|
|
12
|
+
_initializationOperationsProvider = initializationOperationsProvider;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
protected override IEnumerable<T> GetInitializationOperations()
|
|
16
|
+
{
|
|
17
|
+
if (_initializationOperationsProvider == null)
|
|
18
|
+
yield break;
|
|
19
|
+
|
|
20
|
+
foreach (var op in _initializationOperationsProvider.GetInitializationOperations())
|
|
21
|
+
yield return op;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected override void SendOperation(IObserver<T> observer, T operation)
|
|
25
|
+
=> observer.OnNext(operation);
|
|
26
|
+
|
|
27
|
+
public void EnqueueOperation(T operation)
|
|
28
|
+
=> EnqueuePendingOperation(operation);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using System.Linq;
|
|
4
|
+
|
|
5
|
+
namespace ObserveThing
|
|
6
|
+
{
|
|
7
|
+
public interface IOperation
|
|
8
|
+
{
|
|
9
|
+
IObservable<IOperation> source { get; }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public abstract class ObservableBase<TObserver, TOperation> : IObservable<IOperation>, IDisposable
|
|
13
|
+
where TObserver : IObserverBase
|
|
14
|
+
where TOperation : IOperation
|
|
15
|
+
{
|
|
16
|
+
private class ObserverData : IPendingObserver, IDisposable
|
|
17
|
+
{
|
|
18
|
+
public IObserverBase observer { get; }
|
|
19
|
+
public bool immediate { get; }
|
|
20
|
+
public uint priority { get; }
|
|
21
|
+
public bool priorityAllocated { get; }
|
|
22
|
+
public bool disposed { get; private set; }
|
|
23
|
+
|
|
24
|
+
private Queue<TOperation> _pendingOperations = new Queue<TOperation>();
|
|
25
|
+
private Action<TOperation> _sendOperation;
|
|
26
|
+
private Action<ObserverData> _onDispose;
|
|
27
|
+
|
|
28
|
+
public ObserverData(IObserverBase observer, bool immediate, uint priority, bool priorityAllocated, Action<TOperation> sendOperation, Action<ObserverData> onDispose)
|
|
29
|
+
{
|
|
30
|
+
this.observer = observer;
|
|
31
|
+
this.immediate = immediate;
|
|
32
|
+
this.priority = priority;
|
|
33
|
+
this.priorityAllocated = priorityAllocated;
|
|
34
|
+
|
|
35
|
+
_sendOperation = sendOperation;
|
|
36
|
+
_onDispose = onDispose;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public void EnqueuePendingOperation(TOperation operation)
|
|
40
|
+
{
|
|
41
|
+
_pendingOperations.Enqueue(operation);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public void SendNext()
|
|
45
|
+
{
|
|
46
|
+
if (_pendingOperations.Count == 0)
|
|
47
|
+
return;
|
|
48
|
+
|
|
49
|
+
try
|
|
50
|
+
{
|
|
51
|
+
_sendOperation(_pendingOperations.Dequeue());
|
|
52
|
+
}
|
|
53
|
+
catch (Exception exc)
|
|
54
|
+
{
|
|
55
|
+
observer.OnError(exc);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public void Dispose()
|
|
60
|
+
{
|
|
61
|
+
if (disposed)
|
|
62
|
+
return;
|
|
63
|
+
|
|
64
|
+
disposed = true;
|
|
65
|
+
|
|
66
|
+
_onDispose?.Invoke(this);
|
|
67
|
+
|
|
68
|
+
observer.OnDispose();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public ObservationContext context { get; protected set; }
|
|
73
|
+
public bool disposed { get; private set; }
|
|
74
|
+
|
|
75
|
+
private List<ObserverData> _observers = new List<ObserverData>();
|
|
76
|
+
|
|
77
|
+
public ObservableBase(ObservationContext context)
|
|
78
|
+
{
|
|
79
|
+
this.context = context ?? Settings.DefaultObservationContext;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private void HandleObserverDisposed(ObserverData data)
|
|
83
|
+
{
|
|
84
|
+
if (disposed)
|
|
85
|
+
return;
|
|
86
|
+
|
|
87
|
+
if (!_observers.Remove(data))
|
|
88
|
+
return;
|
|
89
|
+
|
|
90
|
+
if (data.priorityAllocated)
|
|
91
|
+
context.DeallocateObserverPriority(data.priority);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
protected void EnqueuePendingOperation(TOperation operation)
|
|
95
|
+
{
|
|
96
|
+
if (disposed)
|
|
97
|
+
throw new ObjectDisposedException(GetType().Name);
|
|
98
|
+
|
|
99
|
+
if (_observers.Count == 0)
|
|
100
|
+
return;
|
|
101
|
+
|
|
102
|
+
foreach (var observer in _observers)
|
|
103
|
+
{
|
|
104
|
+
observer.EnqueuePendingOperation(operation);
|
|
105
|
+
context.RegisterPendingObserver(observer);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
context.NotifyPendingObserversIfNecessary();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected virtual void DisposeInternal() { }
|
|
112
|
+
|
|
113
|
+
public void OnError(Exception error)
|
|
114
|
+
{
|
|
115
|
+
foreach (var observer in _observers.OrderByDescending(x => x.immediate).ThenBy(x => x.priority))
|
|
116
|
+
observer.observer.OnError(error);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected abstract IEnumerable<TOperation> GetInitializationOperations();
|
|
120
|
+
|
|
121
|
+
protected IDisposable AddObserver(TObserver observer, bool immediate, uint? priority)
|
|
122
|
+
=> AddObserverInternal(observer, immediate, priority, op => SendOperation(observer, op));
|
|
123
|
+
|
|
124
|
+
private IDisposable AddObserverInternal(IObserverBase observer, bool immediate, uint? priority, Action<TOperation> sendOperation)
|
|
125
|
+
{
|
|
126
|
+
if (disposed)
|
|
127
|
+
throw new ObjectDisposedException(GetType().Name);
|
|
128
|
+
|
|
129
|
+
var observerData = new ObserverData(
|
|
130
|
+
observer,
|
|
131
|
+
immediate,
|
|
132
|
+
priority ?? context.AllocateObserverPriority(),
|
|
133
|
+
priority == null,
|
|
134
|
+
sendOperation,
|
|
135
|
+
HandleObserverDisposed
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// do this after calling OnFirstObserverAdded so any resulting operations won't be queued (they'll be reflected in GetInitializationOperations)
|
|
139
|
+
_observers.Add(observerData);
|
|
140
|
+
|
|
141
|
+
foreach (var op in GetInitializationOperations())
|
|
142
|
+
sendOperation(op);
|
|
143
|
+
|
|
144
|
+
return observerData;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
protected abstract void SendOperation(TObserver observer, TOperation operation);
|
|
148
|
+
|
|
149
|
+
public virtual IDisposable Subscribe(TObserver observer, bool immediate = default, uint? priority = default)
|
|
150
|
+
=> AddObserverInternal(observer, immediate, priority, op => SendOperation(observer, op));
|
|
151
|
+
|
|
152
|
+
public virtual IDisposable Subscribe(IObserver<IOperation> observer, bool immediate = default, uint? priority = default)
|
|
153
|
+
=> AddObserverInternal(observer, immediate, priority, op => observer.OnNext(op));
|
|
154
|
+
|
|
155
|
+
public void Dispose()
|
|
156
|
+
{
|
|
157
|
+
if (disposed)
|
|
158
|
+
return;
|
|
159
|
+
|
|
160
|
+
disposed = true;
|
|
161
|
+
|
|
162
|
+
foreach (var observer in _observers.OrderByDescending(x => x.immediate).ThenBy(x => x.priority))
|
|
163
|
+
{
|
|
164
|
+
observer.Dispose();
|
|
165
|
+
context.DeallocateObserverPriority(observer.priority);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
_observers.Clear();
|
|
169
|
+
|
|
170
|
+
DisposeInternal();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.Linq;
|
|
5
|
+
|
|
6
|
+
namespace ObserveThing
|
|
7
|
+
{
|
|
8
|
+
public interface ICollectionOp : IOperation
|
|
9
|
+
{
|
|
10
|
+
object element { get; }
|
|
11
|
+
uint elementId { get; }
|
|
12
|
+
bool isRemove { get; }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public struct CollectionOp<T> : ICollectionOp
|
|
16
|
+
{
|
|
17
|
+
public IObservable<IOperation> source { get; set; }
|
|
18
|
+
public T element { get; set; }
|
|
19
|
+
public uint elementId { get; set; }
|
|
20
|
+
public bool isRemove { get; set; }
|
|
21
|
+
|
|
22
|
+
object ICollectionOp.element => element;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public class ObservableCollection<T> : ObservableBase<ICollectionObserver<T>, CollectionOp<T>>, ICollectionObservable<T>, IEnumerable<T>
|
|
26
|
+
{
|
|
27
|
+
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
|
28
|
+
=> _collection.Values.GetEnumerator();
|
|
29
|
+
|
|
30
|
+
IEnumerator IEnumerable.GetEnumerator()
|
|
31
|
+
=> _collection.Values.GetEnumerator();
|
|
32
|
+
|
|
33
|
+
public int Count => _collection.Count;
|
|
34
|
+
|
|
35
|
+
private Dictionary<uint, T> _collection = new Dictionary<uint, T>();
|
|
36
|
+
|
|
37
|
+
private CollectionIdProvider _idProvider;
|
|
38
|
+
|
|
39
|
+
public ObservableCollection() : this(default, default(IEnumerable<T>)) { }
|
|
40
|
+
public ObservableCollection(params T[] value) : this(default, (IEnumerable<T>)value) { }
|
|
41
|
+
public ObservableCollection(IEnumerable<T> value) : this(default, value) { }
|
|
42
|
+
|
|
43
|
+
public ObservableCollection(ObservationContext context) : this(context, default(IEnumerable<T>)) { }
|
|
44
|
+
public ObservableCollection(ObservationContext context, params T[] value) : this(context, (IEnumerable<T>)value) { }
|
|
45
|
+
public ObservableCollection(ObservationContext context, IEnumerable<T> value) : base(context)
|
|
46
|
+
{
|
|
47
|
+
_idProvider = new CollectionIdProvider(_collection.ContainsKey);
|
|
48
|
+
|
|
49
|
+
if (value == null)
|
|
50
|
+
return;
|
|
51
|
+
|
|
52
|
+
foreach (var element in value)
|
|
53
|
+
{
|
|
54
|
+
var id = _idProvider.GetUnusedId();
|
|
55
|
+
_collection.Add(id, element);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public bool Contains(T element)
|
|
60
|
+
=> _collection.ContainsValue(element);
|
|
61
|
+
|
|
62
|
+
public uint Add(T element)
|
|
63
|
+
{
|
|
64
|
+
var id = _idProvider.GetUnusedId();
|
|
65
|
+
Add(id, element);
|
|
66
|
+
return id;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public void Add(uint id, T element)
|
|
70
|
+
{
|
|
71
|
+
_collection.Add(id, element);
|
|
72
|
+
EnqueuePendingOperation(new CollectionOp<T>() { source = this, elementId = id, element = element, isRemove = false });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public bool Remove(T element)
|
|
76
|
+
{
|
|
77
|
+
var id = default(uint);
|
|
78
|
+
var found = false;
|
|
79
|
+
|
|
80
|
+
foreach (var kvp in _collection)
|
|
81
|
+
{
|
|
82
|
+
if (!Equals(element, kvp.Value))
|
|
83
|
+
continue;
|
|
84
|
+
|
|
85
|
+
id = kvp.Key;
|
|
86
|
+
found = true;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!found)
|
|
91
|
+
return false;
|
|
92
|
+
|
|
93
|
+
return Remove(id);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public bool Remove(uint id)
|
|
97
|
+
{
|
|
98
|
+
if (!_collection.TryGetValue(id, out var element))
|
|
99
|
+
return false;
|
|
100
|
+
|
|
101
|
+
_collection.Remove(id);
|
|
102
|
+
EnqueuePendingOperation(new CollectionOp<T>() { source = this, elementId = id, element = element, isRemove = true });
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public void Clear()
|
|
107
|
+
{
|
|
108
|
+
foreach (var kvp in _collection.ToArray())
|
|
109
|
+
{
|
|
110
|
+
_collection.Remove(kvp.Key);
|
|
111
|
+
EnqueuePendingOperation(new CollectionOp<T>() { source = this, elementId = kvp.Key, element = kvp.Value, isRemove = true });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
protected override IEnumerable<CollectionOp<T>> GetInitializationOperations()
|
|
116
|
+
{
|
|
117
|
+
foreach (var elementData in _collection)
|
|
118
|
+
yield return new CollectionOp<T>() { source = this, element = elementData.Value, elementId = elementData.Key, isRemove = false };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
protected override void SendOperation(ICollectionObserver<T> observer, CollectionOp<T> operation)
|
|
122
|
+
{
|
|
123
|
+
if (operation.isRemove)
|
|
124
|
+
{
|
|
125
|
+
observer.OnRemove(operation.elementId, operation.element);
|
|
126
|
+
}
|
|
127
|
+
else
|
|
128
|
+
{
|
|
129
|
+
observer.OnAdd(operation.elementId, operation.element);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
IDisposable ICollectionObservable.Subscribe(ICollectionObserver observer, bool immediate, uint? priority)
|
|
134
|
+
=> Subscribe(new CollectionObserver<T>(
|
|
135
|
+
onAdd: (id, element) => observer.OnAdd(id, element),
|
|
136
|
+
onRemove: (id, element) => observer.OnRemove(id, element),
|
|
137
|
+
onDispose: observer.OnDispose,
|
|
138
|
+
onError: observer.OnError
|
|
139
|
+
), immediate, priority);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.Linq;
|
|
5
|
+
|
|
6
|
+
namespace ObserveThing
|
|
7
|
+
{
|
|
8
|
+
public interface IDictionaryOp : IOperation
|
|
9
|
+
{
|
|
10
|
+
uint elementId { get; }
|
|
11
|
+
object key { get; }
|
|
12
|
+
object value { get; }
|
|
13
|
+
bool isRemove { get; }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public struct DictionaryOp<TKey, TValue> : IDictionaryOp
|
|
17
|
+
{
|
|
18
|
+
public IObservable<IOperation> source { get; set; }
|
|
19
|
+
public uint elementId { get; set; }
|
|
20
|
+
public TKey key { get; set; }
|
|
21
|
+
public TValue value { get; set; }
|
|
22
|
+
public bool isRemove { get; set; }
|
|
23
|
+
|
|
24
|
+
object IDictionaryOp.key => key;
|
|
25
|
+
object IDictionaryOp.value => value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public class ObservableDictionary<TKey, TValue> : ObservableBase<IDictionaryObserver<TKey, TValue>, DictionaryOp<TKey, TValue>>, IDictionaryObservable<TKey, TValue>, IEnumerable<KeyValuePair<TKey, TValue>>
|
|
29
|
+
{
|
|
30
|
+
public TValue this[TKey key]
|
|
31
|
+
{
|
|
32
|
+
get => _dictionary[key];
|
|
33
|
+
set
|
|
34
|
+
{
|
|
35
|
+
Remove(key);
|
|
36
|
+
Add(key, value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public int Count => _dictionary.Count;
|
|
41
|
+
|
|
42
|
+
public IEnumerable<TKey> Keys => _dictionary.Keys;
|
|
43
|
+
public IEnumerable<TValue> Values => _dictionary.Values;
|
|
44
|
+
|
|
45
|
+
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
|
|
46
|
+
=> _dictionary.GetEnumerator();
|
|
47
|
+
|
|
48
|
+
IEnumerator IEnumerable.GetEnumerator()
|
|
49
|
+
=> _dictionary.GetEnumerator();
|
|
50
|
+
|
|
51
|
+
private Dictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>();
|
|
52
|
+
private Dictionary<TKey, uint> _ids = new Dictionary<TKey, uint>();
|
|
53
|
+
private CollectionIdProvider _idProvider;
|
|
54
|
+
|
|
55
|
+
public ObservableDictionary() : this(default, default) { }
|
|
56
|
+
public ObservableDictionary(IEnumerable<KeyValuePair<TKey, TValue>> value) : this(default, value) { }
|
|
57
|
+
|
|
58
|
+
public ObservableDictionary(ObservationContext context) : this(context, default) { }
|
|
59
|
+
public ObservableDictionary(ObservationContext context, IEnumerable<KeyValuePair<TKey, TValue>> value) : base(context)
|
|
60
|
+
{
|
|
61
|
+
_idProvider = new CollectionIdProvider(_ids.Values.Contains);
|
|
62
|
+
|
|
63
|
+
if (value == null)
|
|
64
|
+
return;
|
|
65
|
+
|
|
66
|
+
foreach (var kvp in value)
|
|
67
|
+
{
|
|
68
|
+
_dictionary.Add(kvp.Key, kvp.Value);
|
|
69
|
+
_ids.Add(kvp.Key, _idProvider.GetUnusedId());
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public void Add(TKey key, TValue value)
|
|
74
|
+
{
|
|
75
|
+
var id = _idProvider.GetUnusedId();
|
|
76
|
+
_dictionary.Add(key, value);
|
|
77
|
+
_ids.Add(key, id);
|
|
78
|
+
EnqueuePendingOperation(new DictionaryOp<TKey, TValue>() { source = this, elementId = id, key = key, value = value, isRemove = false });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public bool Remove(TKey key)
|
|
82
|
+
{
|
|
83
|
+
if (!_dictionary.TryGetValue(key, out var value))
|
|
84
|
+
return false;
|
|
85
|
+
|
|
86
|
+
var id = _ids[key];
|
|
87
|
+
|
|
88
|
+
_dictionary.Remove(key);
|
|
89
|
+
_ids.Remove(key);
|
|
90
|
+
EnqueuePendingOperation(new DictionaryOp<TKey, TValue>() { source = this, elementId = id, key = key, value = value, isRemove = true });
|
|
91
|
+
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public void Clear()
|
|
96
|
+
{
|
|
97
|
+
foreach (var kvp in _dictionary.ToArray())
|
|
98
|
+
{
|
|
99
|
+
var id = _ids[kvp.Key];
|
|
100
|
+
_dictionary.Remove(kvp.Key);
|
|
101
|
+
_ids.Remove(kvp.Key);
|
|
102
|
+
EnqueuePendingOperation(new DictionaryOp<TKey, TValue>() { source = this, elementId = id, key = kvp.Key, value = kvp.Value, isRemove = true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public bool TryGetValueInternal(TKey key, out TValue value)
|
|
107
|
+
=> _dictionary.TryGetValue(key, out value);
|
|
108
|
+
|
|
109
|
+
public bool ContainsKey(TKey key)
|
|
110
|
+
=> _dictionary.ContainsKey(key);
|
|
111
|
+
|
|
112
|
+
public bool ContainsValue(TValue value)
|
|
113
|
+
=> _dictionary.ContainsValue(value);
|
|
114
|
+
|
|
115
|
+
protected override IEnumerable<DictionaryOp<TKey, TValue>> GetInitializationOperations()
|
|
116
|
+
{
|
|
117
|
+
foreach (var kvp in _dictionary)
|
|
118
|
+
{
|
|
119
|
+
yield return new DictionaryOp<TKey, TValue>()
|
|
120
|
+
{
|
|
121
|
+
source = this,
|
|
122
|
+
elementId = _ids[kvp.Key],
|
|
123
|
+
key = kvp.Key,
|
|
124
|
+
value = kvp.Value,
|
|
125
|
+
isRemove = false
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
protected override void SendOperation(IDictionaryObserver<TKey, TValue> observer, DictionaryOp<TKey, TValue> operation)
|
|
131
|
+
{
|
|
132
|
+
if (operation.isRemove)
|
|
133
|
+
{
|
|
134
|
+
observer.OnRemove(operation.elementId, KeyValuePair.Create(operation.key, operation.value));
|
|
135
|
+
}
|
|
136
|
+
else
|
|
137
|
+
{
|
|
138
|
+
observer.OnAdd(operation.elementId, KeyValuePair.Create(operation.key, operation.value));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
IDisposable IDictionaryObservable.Subscribe(IDictionaryObserver observer, bool immediate, uint? priority)
|
|
143
|
+
=> Subscribe(new DictionaryObserver<TKey, TValue>(
|
|
144
|
+
onAdd: (id, kvp) => observer.OnAdd(id, new KeyValuePair<object, object>(kvp.Key, kvp.Value)),
|
|
145
|
+
onRemove: (id, kvp) => observer.OnRemove(id, new KeyValuePair<object, object>(kvp.Key, kvp.Value)),
|
|
146
|
+
onDispose: observer.OnDispose,
|
|
147
|
+
onError: observer.OnError
|
|
148
|
+
), immediate, priority);
|
|
149
|
+
|
|
150
|
+
IDisposable ICollectionObservable<KeyValuePair<TKey, TValue>>.Subscribe(ICollectionObserver<KeyValuePair<TKey, TValue>> observer, bool immediate, uint? priority)
|
|
151
|
+
=> Subscribe(new DictionaryObserver<TKey, TValue>(
|
|
152
|
+
onAdd: (id, kvp) => observer.OnAdd(id, kvp),
|
|
153
|
+
onRemove: (id, kvp) => observer.OnRemove(id, kvp),
|
|
154
|
+
onDispose: observer.OnDispose,
|
|
155
|
+
onError: observer.OnError
|
|
156
|
+
), immediate, priority);
|
|
157
|
+
|
|
158
|
+
IDisposable ICollectionObservable.Subscribe(ICollectionObserver observer, bool immediate, uint? priority)
|
|
159
|
+
=> Subscribe(new DictionaryObserver<TKey, TValue>(
|
|
160
|
+
onAdd: (id, kvp) => observer.OnAdd(id, kvp),
|
|
161
|
+
onRemove: (id, kvp) => observer.OnRemove(id, kvp),
|
|
162
|
+
onDispose: observer.OnDispose,
|
|
163
|
+
onError: observer.OnError
|
|
164
|
+
), immediate, priority);
|
|
165
|
+
}
|
|
166
|
+
}
|