1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 // Not actually part of forms, but is handy.
6 
7 
8 module dfl.collections;
9 
10 private import dfl.internal.dlib;
11 
12 private import dfl.base;
13 
14 
15 void _blankListCallback(TValue)(size_t idx, TValue val) { // package
16 }
17 
18 
19 // Mixin.
20 // Item*Callback called before modifications.
21 // For clear(), index is size_t.max and value is null. If CLEAR_EACH, also called back for each value.
22 template ListWrapArray(TValue, alias Array,
23                        /+ // DMD 1.005: basic type expected, not function
24 alias ItemAddingCallback = function(size_t idx, TValue val) {},
25 alias ItemAddedCallback = function(size_t idx, TValue val) {},
26 alias ItemRemovingCallback = function(size_t idx, TValue val) {},
27 alias ItemRemovedCallback = function(size_t idx, TValue val) {},
28 +/
29 alias ItemAddingCallback,
30 alias ItemAddedCallback,
31 alias ItemRemovingCallback,
32 alias ItemRemovedCallback,
33 bool OVERLOAD_STRING = false,
34 bool OVERLOAD_OBJECT = false,
35 bool COW = true,
36 bool CLEAR_EACH = false) { // package
37    mixin OpApplyWrapArray!(TValue, Array); // Note: this overrides COW.
38 
39 
40    static if(OVERLOAD_OBJECT) {
41       static assert(!is(TValue == Object));
42    }
43 
44    static if(OVERLOAD_STRING) {
45       static assert(!is(TValue == Dstring));
46 
47       static if(is(TValue == Object)) {
48          alias StringObject TValueString;
49       } else {
50          alias TValue TValueString;
51       }
52    }
53 
54 
55 
56    void opIndexAssign(TValue value, int index) {
57       TValue oldval = Array[index];
58       ItemRemovingCallback(index, oldval); // Removing.
59       static if(COW) {
60          Array = Array.dup;
61       } else {
62          //Array[index] = TValue.init;
63       }
64       ItemRemovedCallback(index, oldval); // Removed.
65 
66       ItemAddingCallback(index, value); // Adding.
67       Array[index] = value;
68       ItemAddedCallback(index, value); // Added.
69    }
70 
71    static if(OVERLOAD_OBJECT) {
72       /// ditto
73       void opIndexAssign(Object value, int index) {
74          TValue tval;
75          tval = cast(TValue)value;
76          if(tval) {
77             return opIndexAssign(tval, index);
78          } else {
79             return opIndexAssign(new TValue(value), index);   // ?
80          }
81       }
82    }
83 
84    static if(OVERLOAD_STRING) {
85       /// ditto
86       void opIndexAssign(Dstring value, int index) {
87          return opIndexAssign(new TValueString(value), index);
88       }
89    }
90 
91 
92 
93    @property TValue opIndex(int index) { // getter
94       return Array[index];
95    }
96 
97 
98 
99    void add(TValue value) {
100       _insert(cast(int)Array.length, value);
101    }
102 
103    static if(OVERLOAD_OBJECT) {
104       /// ditto
105       void add(Object value) {
106          _insert(cast(int)Array.length, value);
107       }
108    }
109 
110    static if(OVERLOAD_STRING) {
111       /// ditto
112       void add(Dstring value) {
113          _insert(cast(int)Array.length, new TValueString(value));
114       }
115    }
116 
117 
118 
119    void clear() {
120       ItemRemovingCallback(size_t.max, null); // Removing ALL.
121 
122       size_t iw;
123       iw = Array.length;
124       if(iw) {
125          static if(CLEAR_EACH) {
126             try {
127                // Remove in reverse order so the indices don't keep shifting.
128                TValue oldval;
129                for(--iw;; iw--) {
130                   oldval = Array[iw];
131                   static if(CLEAR_EACH) {
132                      ItemRemovingCallback(iw, oldval); // Removing.
133                   }
134                   /+static if(COW) {
135                   } else {
136                      //Array[iw] = TValue.init;
137                   }+/
138                   debug {
139                      Array = Array[0 .. iw]; // 'Temporarily' removes it for ItemRemovedCallback.
140                   }
141                   static if(CLEAR_EACH) {
142                      ItemRemovedCallback(iw, oldval); // Removed.
143                   }
144                   if(!iw) {
145                      break;
146                   }
147                }
148             }
149             finally {
150                Array = Array[0 .. iw];
151                static if(COW) {
152                   if(!iw) {
153                      Array = null;
154                   }
155                }
156             }
157          } else {
158             Array = Array[0 .. 0];
159             static if(COW) {
160                Array = null;
161             }
162          }
163       }
164 
165       ItemRemovedCallback(size_t.max, null); // Removed ALL.
166    }
167 
168 
169 
170    bool contains(TValue value) {
171       return -1 != findIsIndex!(TValue)(Array, value);
172    }
173 
174    static if(OVERLOAD_OBJECT) {
175       /// ditto
176       bool contains(Object value) {
177          return -1 != indexOf(value);
178       }
179    }
180 
181    static if(OVERLOAD_STRING) {
182       /// ditto
183       bool contains(Dstring value) {
184          return -1 != indexOf(value);
185       }
186    }
187 
188 
189 
190    int indexOf(TValue value) {
191       return findIsIndex!(TValue)(Array, value);
192    }
193 
194    static if(OVERLOAD_OBJECT) {
195       /// ditto
196       int indexOf(Object value) {
197          TValue tval;
198          tval = cast(TValue)value;
199          if(tval) {
200             return indexOf(tval);
201          } else {
202             foreach(size_t idx, TValue onval; Array) {
203                if(onval == value) { // TValue must have opEquals.
204                   return idx;
205                }
206             }
207             return -1;
208          }
209       }
210    }
211 
212    static if(OVERLOAD_STRING) {
213       /// ditto
214       int indexOf(Dstring value) {
215          foreach(size_t idx, TValue onval; Array) {
216             static if(is(TValue == TValueString)) {
217                if(onval == value) { // TValue must have opEquals.
218                   return idx;
219                }
220             } else {
221                if(getObjectString(onval) == value) {
222                   return idx;
223                }
224             }
225          }
226          return -1;
227       }
228    }
229 
230 
231    private final void _insert(int index, TValue value) {
232       if(index > Array.length) {
233          index = Array.length;
234       }
235       ItemAddingCallback(index, value); // Adding.
236       static if(COW) {
237          if(index >= Array.length) {
238             if(Array.length) { // Workaround old bug ?
239                Array = Array[0 .. index] ~ (&value)[0 .. 1];
240             } else {
241                Array = (&value)[0 .. 1].dup;
242             }
243             goto insert_done;
244          }
245       } else {
246          if(index >= Array.length) {
247             Array ~= value;
248             goto insert_done;
249          }
250       }
251       Array = Array[0 .. index] ~ (&value)[0 .. 1] ~ Array[index .. Array.length];
252    insert_done:
253       ItemAddedCallback(index, value); // Added.
254    }
255 
256    static if(OVERLOAD_OBJECT) {
257       private final void _insert(int index, Object value) {
258          TValue tval;
259          tval = cast(TValue)value;
260          if(tval) {
261             return _insert(index, tval);
262          } else {
263             return _insert(index, new TValue(value));   // ?
264          }
265       }
266    }
267 
268    static if(OVERLOAD_STRING) {
269       /// ditto
270       private final void _insert(int index, Dstring value) {
271          return _insert(index, new TValueString(value));
272       }
273    }
274 
275 
276 
277    void insert(int index, TValue value) {
278       _insert(index, value);
279    }
280 
281    static if(OVERLOAD_OBJECT) {
282       /// ditto
283       void insert(int index, Object value) {
284          _insert(index, value);
285       }
286    }
287 
288    static if(OVERLOAD_STRING) {
289       /// ditto
290       void insert(int index, Dstring value) {
291          return _insert(index, value);
292       }
293    }
294 
295 
296 
297    void remove(TValue value) {
298       int index;
299       index = findIsIndex!(TValue)(Array, value);
300       if(-1 != index) {
301          removeAt(index);
302       }
303    }
304 
305    static if(OVERLOAD_OBJECT) {
306       /// ditto
307       void remove(Object value) {
308          TValue tval;
309          tval = cast(TValue)value;
310          if(tval) {
311             return remove(tval);
312          } else {
313             int i;
314             i = indexOf(value);
315             if(-1 != i) {
316                removeAt(i);
317             }
318          }
319       }
320    }
321 
322    static if(OVERLOAD_STRING) {
323       /// ditto
324       void remove(Dstring value) {
325          int i;
326          i = indexOf(value);
327          if(-1 != i) {
328             removeAt(i);
329          }
330       }
331    }
332 
333 
334 
335    void removeAt(int index) {
336       TValue oldval = Array[index];
337       ItemRemovingCallback(index, oldval); // Removing.
338       if(!index) {
339          Array = Array[1 .. Array.length];
340       } else if(index == Array.length - 1) {
341          Array = Array[0 .. index];
342       } else if(index > 0 && index < cast(int)Array.length) {
343          Array = Array[0 .. index] ~ Array[index + 1 .. Array.length];
344       }
345       ItemRemovedCallback(index, oldval); // Removed.
346    }
347 
348 
349    deprecated alias length count;
350 
351 
352    @property size_t length() { // getter
353       return Array.length;
354    }
355 
356 
357    deprecated alias dup clone;
358 
359 
360    TValue[] dup() {
361       return Array.dup;
362    }
363 
364 
365 
366    void copyTo(TValue[] dest, int destIndex) {
367       dest[destIndex .. destIndex + Array.length] = Array[];
368    }
369 
370 
371 
372    void addRange(TValue[] values) {
373       foreach(TValue value; values) {
374          add(value);
375       }
376    }
377 
378    static if(OVERLOAD_OBJECT) {
379       /// ditto
380       void addRange(Object[] values) {
381          foreach(Object value; values) {
382             add(value);
383          }
384       }
385    }
386 
387    static if(OVERLOAD_STRING) {
388       /// ditto
389       void addRange(Dstring[] values) {
390          foreach(Dstring value; values) {
391             add(value);
392          }
393       }
394    }
395 }
396 
397 
398 // Mixin.
399 template OpApplyAddIndex(alias ApplyFunc, TValue, bool ADD_APPLY_FUNC = false) { // package
400 
401    int opApply(int delegate(ref size_t, ref TValue val) dg) {
402       size_t idx = 0;
403       return ApplyFunc(
404       (ref TValue val) {
405          int result;
406          result = dg(idx, val);
407          idx++;
408          return result;
409       });
410    }
411 
412 
413    static if(ADD_APPLY_FUNC) {
414       /// ditto
415       int opApply(int delegate(ref TValue val) dg) {
416          return ApplyFunc(dg);
417       }
418    }
419 }
420 
421 
422 // Mixin.
423 template OpApplyWrapArray(TValue, alias Array) { // package
424 
425    int opApply(int delegate(ref TValue val) dg) {
426       int result = 0;
427       foreach(ref TValue val; Array) {
428          result = dg(val);
429          if(result) {
430             break;
431          }
432       }
433       return result;
434    }
435 
436    /// ditto
437    int opApply(int delegate(ref size_t, ref TValue val) dg) {
438       int result = 0;
439       foreach(size_t idx, ref TValue val; Array) {
440          result = dg(idx, val);
441          if(result) {
442             break;
443          }
444       }
445       return result;
446    }
447 }
448 
449 
450 template removeIndex(T) { // package
451    T[] removeIndex(T[] array, size_t index) {
452       if(!index) {
453          array = array[1 .. array.length];
454       } else if(index == array.length - 1) {
455          array = array[0 .. index];
456       } else {
457          array = array[0 .. index] ~ array[index + 1 .. array.length];
458       }
459       return array;
460    }
461 }
462 
463 
464 // Returns -1 if not found.
465 template findIsIndex(T) { // package
466    int findIsIndex(T[] array, T obj) {
467       int idx;
468       for(idx = 0; idx != array.length; idx++) {
469          if(obj is array[idx]) {
470             return idx;
471          }
472       }
473       return -1;
474    }
475 }
476