A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections;
3using System.Collections.Generic;
4
5namespace Unity.VisualScripting
6{
7 public class ConnectionCollectionBase<TConnection, TSource, TDestination, TCollection> : IConnectionCollection<TConnection, TSource, TDestination>
8 where TConnection : IConnection<TSource, TDestination>
9 where TCollection : ICollection<TConnection>
10 {
11 public ConnectionCollectionBase(TCollection collection)
12 {
13 this.collection = collection;
14 bySource = new Dictionary<TSource, List<TConnection>>();
15 byDestination = new Dictionary<TDestination, List<TConnection>>();
16 }
17
18 // Using lists instead of HashSet to allow access by index
19 // instead of creating an enumeration and allocating memory
20 // specifically for the "With*NoAlloc" methods, used
21 // very often in flow graphs.
22 private readonly Dictionary<TDestination, List<TConnection>> byDestination;
23 private readonly Dictionary<TSource, List<TConnection>> bySource;
24 protected readonly TCollection collection;
25
26 public IEnumerable<TConnection> this[TSource source] => WithSource(source);
27
28 public IEnumerable<TConnection> this[TDestination destination] => WithDestination(destination);
29
30 public int Count => collection.Count;
31
32 public bool IsReadOnly => false;
33
34 public IEnumerator<TConnection> GetEnumerator()
35 {
36 return collection.GetEnumerator();
37 }
38
39 IEnumerator IEnumerable.GetEnumerator()
40 {
41 return GetEnumerator();
42 }
43
44 public IEnumerable<TConnection> WithSource(TSource source)
45 {
46 return WithSourceNoAlloc(source);
47 }
48
49 public List<TConnection> WithSourceNoAlloc(TSource source)
50 {
51 Ensure.That(nameof(source)).IsNotNull(source);
52
53 if (bySource.TryGetValue(source, out var withSource))
54 {
55 return withSource;
56 }
57 else
58 {
59 return Empty<TConnection>.list;
60 }
61 }
62
63 public TConnection SingleOrDefaultWithSource(TSource source)
64 {
65 Ensure.That(nameof(source)).IsNotNull(source);
66
67 if (bySource.TryGetValue(source, out var withSource))
68 {
69 if (withSource.Count == 1)
70 {
71 return withSource[0];
72 }
73 else if (withSource.Count == 0)
74 {
75 return default(TConnection);
76 }
77 else
78 {
79 throw new InvalidOperationException();
80 }
81 }
82 else
83 {
84 return default(TConnection);
85 }
86 }
87
88 public IEnumerable<TConnection> WithDestination(TDestination destination)
89 {
90 return WithDestinationNoAlloc(destination);
91 }
92
93 public List<TConnection> WithDestinationNoAlloc(TDestination destination)
94 {
95 Ensure.That(nameof(destination)).IsNotNull(destination);
96
97 if (byDestination.TryGetValue(destination, out var withDestination))
98 {
99 return withDestination;
100 }
101 else
102 {
103 return Empty<TConnection>.list;
104 }
105 }
106
107 public TConnection SingleOrDefaultWithDestination(TDestination destination)
108 {
109 Ensure.That(nameof(destination)).IsNotNull(destination);
110
111 if (byDestination.TryGetValue(destination, out var withDestination))
112 {
113 if (withDestination.Count == 1)
114 {
115 return withDestination[0];
116 }
117 else if (withDestination.Count == 0)
118 {
119 return default(TConnection);
120 }
121 else
122 {
123 throw new InvalidOperationException();
124 }
125 }
126 else
127 {
128 return default(TConnection);
129 }
130 }
131
132 public void Add(TConnection item)
133 {
134 if (item == null)
135 {
136 throw new ArgumentNullException(nameof(item));
137 }
138
139 if (item.source == null)
140 {
141 throw new ArgumentNullException("item.source");
142 }
143
144 if (item.destination == null)
145 {
146 throw new ArgumentNullException("item.destination");
147 }
148
149 BeforeAdd(item);
150 collection.Add(item);
151 AddToDictionaries(item);
152 AfterAdd(item);
153 }
154
155 public void Clear()
156 {
157 collection.Clear();
158 bySource.Clear();
159 byDestination.Clear();
160 }
161
162 public bool Contains(TConnection item)
163 {
164 if (item == null)
165 {
166 throw new ArgumentNullException(nameof(item));
167 }
168
169 return collection.Contains(item);
170 }
171
172 public void CopyTo(TConnection[] array, int arrayIndex)
173 {
174 collection.CopyTo(array, arrayIndex);
175 }
176
177 public bool Remove(TConnection item)
178 {
179 if (item == null)
180 {
181 throw new ArgumentNullException(nameof(item));
182 }
183
184 if (item.source == null)
185 {
186 throw new ArgumentNullException("item.source");
187 }
188
189 if (item.destination == null)
190 {
191 throw new ArgumentNullException("item.destination");
192 }
193
194 if (!collection.Contains(item))
195 {
196 return false;
197 }
198
199 BeforeRemove(item);
200 collection.Remove(item);
201 RemoveFromDictionaries(item);
202 AfterRemove(item);
203 return true;
204 }
205
206 protected virtual void BeforeAdd(TConnection item) { }
207 protected virtual void AfterAdd(TConnection item) { }
208 protected virtual void BeforeRemove(TConnection item) { }
209 protected virtual void AfterRemove(TConnection item) { }
210
211 private void AddToDictionaries(TConnection item)
212 {
213 if (!bySource.ContainsKey(item.source))
214 {
215 bySource.Add(item.source, new List<TConnection>());
216 }
217
218 bySource[item.source].Add(item);
219
220 if (!byDestination.ContainsKey(item.destination))
221 {
222 byDestination.Add(item.destination, new List<TConnection>());
223 }
224
225 byDestination[item.destination].Add(item);
226 }
227
228 private void RemoveFromDictionaries(TConnection item)
229 {
230 bySource[item.source].Remove(item);
231 byDestination[item.destination].Remove(item);
232 }
233 }
234}