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(size_t.max, null); // Removing ALL. 138 139 size_t iw; 140 iw = 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(size_t.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(size_t 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 = 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