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