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