1 // Written in the D programming language.
2
3 /**
4
5 This module implements object collection.
6
7 Wrapper around array of objects, providing a set of useful operations, and handling of object ownership.
8
9 Optionally can be owner of its items if instanciated with ownItems=true - will destroy removed items.
10
11
12 Synopsis:
13
14 ----
15 import dlangui.core.collections;
16
17 // add
18 Collection!Widget widgets;
19 widgets ~= new Widget("id1");
20 widgets ~= new Widget("id2");
21 Widget w3 = new Widget("id3");
22 widgets ~= w3;
23
24 // remove by index
25 widgets.remove(1);
26
27 // foreach
28 foreach(w; widgets)
29 writeln("widget: ", w.id);
30
31 // remove by value
32 widgets -= w3;
33 writeln(widgets[0].id);
34 ----
35
36
37 Copyright: Vadim Lopatin, 2014
38 License: Boost License 1.0
39 Authors: Vadim Lopatin, coolreader.org@gmail.com
40 */
41 module dlangui.core.collections;
42
43 import std.algorithm;
44
45 /**
46 Array based collection of items.
47
48 Retains item order when during add/remove operations.
49
50 Optionally destroys removed objects when instanciated with ownItems = true.
51 */
52 struct Collection(T, bool ownItems = false) {
53 private T[] _items;
54 private size_t _len;
55 /// returns true if there are no items in collection
56 @property bool empty() { return _len == 0; }
57 /// returns number of items in collection
58 @property size_t length() { return _len; }
59 /// returns currently allocated capacity (may be more than length)
60 @property size_t size() { return _items.length; }
61 /// change capacity (e.g. to reserve big space to avoid multiple reallocations)
62 @property void size(size_t newSize) {
63 if (_len > newSize)
64 length = newSize; // shrink
65 _items.length = newSize;
66 }
67 /// returns number of items in collection
68 @property void length(size_t newSize) {
69 if (newSize < _len) {
70 // shrink
71 static if (is(T == class) || is(T == struct)) {
72 // clear items
73 for (size_t i = newSize; i < _len; i++) {
74 static if (ownItems)
75 destroy(_items[i]);
76 _items[i] = T.init;
77 }
78 }
79 } else if (newSize > _len) {
80 // expand
81 if (_items.length < newSize)
82 _items.length = newSize;
83 }
84 _len = newSize;
85 }
86 /// access item by index
87 ref T opIndex(size_t index) {
88 assert(index < _len);
89 return _items[index];
90 }
91 /// insert new item in specified position
92 void add(T item, size_t index = size_t.max) {
93 if (index > _len)
94 index = _len;
95 if (_items.length <= _len) {
96 if (_items.length < 4)
97 _items.length = 4;
98 else
99 _items.length = _items.length * 2;
100 }
101 if (index < _len) {
102 for (size_t i = _len; i > index; i--)
103 _items[i] = _items[i - 1];
104 }
105 _items[index] = item;
106 _len++;
107 }
108 /// add all items from other collection
109 void addAll(ref Collection!(T, ownItems) v) {
110 for (int i = 0; i < v.length; i++)
111 add(v[i]);
112 }
113 /// support for appending (~=, +=) and removing by value (-=)
114 ref Collection opOpAssign(string op)(T item) {
115 static if (op.equal("~") || op.equal("+")) {
116 // append item to end of collection
117 add(item);
118 return this;
119 } else if (op.equal("-")) {
120 // remove item from collection, if present
121 removeValue(item);
122 return this;
123 } else {
124 assert(false, "not supported opOpAssign " ~ op);
125 }
126 }
127 /// returns index of first occurence of item, size_t.max if not found
128 size_t indexOf(T item) {
129 for (size_t i = 0; i < _len; i++)
130 if (_items[i] == item)
131 return i;
132 return size_t.max;
133 }
134 /// remove single item, returning removed item
135 T remove(size_t index) {
136 assert(index < _len);
137 T result = _items[index];
138 for (size_t i = index; i + 1 < _len; i++)
139 _items[i] = _items[i + 1];
140 _len--;
141 _items[_len] = T.init;
142 return result;
143 }
144 /// remove single item by value - if present in collection, returning true if item was found and removed
145 bool removeValue(T value) {
146 size_t index = indexOf(value);
147 if (index == size_t.max)
148 return false;
149 T res = remove(index);
150 static if (ownItems)
151 destroy(res);
152 return true;
153 }
154 /// support of foreach with reference
155 int opApply(int delegate(ref T param) op) {
156 int result = 0;
157 for (size_t i = 0; i < _len; i++) {
158 result = op(_items[i]);
159 if (result)
160 break;
161 }
162 return result;
163 }
164 /// remove all items
165 void clear() {
166 static if (is(T == class) || is(T == struct)) {
167 /// clear references
168 for(size_t i = 0; i < _len; i++) {
169 static if (ownItems)
170 destroy(_items[i]);
171 _items[i] = T.init;
172 }
173 }
174 _len = 0;
175 _items = null;
176 }
177
178 //===================================
179 // stack/queue-like ops
180
181 /// remove first item
182 @property T popFront() {
183 if (empty)
184 return T.init; // no items
185 return remove(0);
186 }
187
188 /// peek first item
189 @property T peekFront() {
190 if (empty)
191 return T.init; // no items
192 return _items[0];
193 }
194
195 /// insert item at beginning of collection
196 void pushFront(T item) {
197 add(item, 0);
198 }
199
200 /// remove last item
201 @property T popBack() {
202 if (empty)
203 return T.init; // no items
204 return remove(length - 1);
205 }
206
207 /// peek last item
208 @property T peekBack() {
209 if (empty)
210 return T.init; // no items
211 return _items[length - 1];
212 }
213
214 /// insert item at end of collection
215 void pushBack(T item) {
216 add(item);
217 }
218
219 /// peek first item
220 @property T front() {
221 if (empty)
222 return T.init; // no items
223 return _items[0];
224 }
225
226 /// peek last item
227 @property T back() {
228 if (empty)
229 return T.init; // no items
230 return _items[_len - 1];
231 }
232 /// removes all items on destroy
233 ~this() {
234 clear();
235 }
236 }
237
238
239 /** object list holder, owning its objects - on destroy of holder, all own objects will be destroyed */
240 struct ObjectList(T) {
241 protected T[] _list;
242 protected int _count;
243 /** returns count of items */
244 @property int count() const { return _count; }
245 alias length = count;
246 /** get item by index */
247 T get(int index) {
248 assert(index >= 0 && index < _count, "child index out of range");
249 return _list[index];
250 }
251 /** get const item by index */
252 const(T) get(int index) const {
253 assert(index >= 0 && index < _count, "child index out of range");
254 return _list[index];
255 }
256 /// get item by index
257 T opIndex(int index) {
258 return get(index);
259 }
260 /** add item to list */
261 T add(T item) {
262 if (_list.length <= _count) // resize
263 _list.length = _list.length < 4 ? 4 : _list.length * 2;
264 _list[_count++] = item;
265 return item;
266 }
267 /** add item to list */
268 T insert(T item, int index = -1) {
269 if (index > _count || index < 0)
270 index = _count;
271 if (_list.length <= _count) // resize
272 _list.length = _list.length < 4 ? 4 : _list.length * 2;
273 for (int i = _count; i > index; i--)
274 _list[i] = _list[i - 1];
275 _list[index] = item;
276 _count++;
277 return item;
278 }
279 /** find child index for item, return -1 if not found */
280 int indexOf(T item) {
281 if (item is null)
282 return -1;
283 for (int i = 0; i < _count; i++)
284 if (_list[i] is item)
285 return i;
286 return -1;
287 }
288 /** find child index for item by id, return -1 if not found */
289 static if (__traits(hasMember, T, "compareId")) {
290 int indexOf(string id) {
291 for (int i = 0; i < _count; i++)
292 if (_list[i].compareId(id))
293 return i;
294 return -1;
295 }
296 }
297 /** remove item from list, return removed item */
298 T remove(int index) {
299 assert(index >= 0 && index < _count, "child index out of range");
300 T item = _list[index];
301 for (int i = index; i < _count - 1; i++)
302 _list[i] = _list[i + 1];
303 _count--;
304 return item;
305 }
306 /** Replace item with another value, destroy old value. */
307 void replace(T item, int index) {
308 T old = _list[index];
309 _list[index] = item;
310 destroy(old);
311 }
312 /** Replace item with another value, destroy old value. */
313 void replace(T newItem, T oldItem) {
314 int idx = indexOf(oldItem);
315 if (newItem is null) {
316 if (idx >= 0) {
317 T item = remove(idx);
318 destroy(item);
319 }
320 } else {
321 if (idx >= 0)
322 replace(newItem, idx);
323 else
324 add(newItem);
325 }
326 }
327 /** remove and destroy all items */
328 void clear(bool destroyObj = true) {
329 for (int i = 0; i < _count; i++) {
330 if(destroyObj) {
331 destroy(_list[i]);
332 }
333 _list[i] = null;
334 }
335 _count = 0;
336 }
337 /// Support foreach
338 int opApply(int delegate(ref T) callback) {
339 int res = 0;
340 for(int i = 0; i < _count; i++) {
341 res = callback(_list[i]);
342 if (res)
343 break;
344 }
345 return res;
346 }
347 /// Get items array slice. Don't try to resize it!
348 T[] asArray() {
349 if (!_count)
350 return null;
351 return _list[0.._count];
352 }
353 /// destructor destroys all items
354 ~this() {
355 clear();
356 }
357 }
358