1 dojo.provide("calitha.collections.TreeMap"); 2 dojo.require("calitha.collections.INavigableMap"); 3 dojo.require("calitha.collections.AbstractMap"); 4 dojo.require("calitha.collections.util"); 5 dojo.require("calitha.collections.treemap.AscendingSubMap"); 6 dojo.require("calitha.collections.treemap.DescendingSubMap"); 7 dojo.require("calitha.collections.treemap.Entry"); 8 dojo.require("calitha.collections.treemap.KeyIterator"); 9 dojo.require("calitha.collections.treemap.KeySet"); 10 dojo.require("calitha.collections.treemap.Values"); 11 dojo.require("calitha.collections.IComparable"); 12 dojo.require("calitha.collections.treemap.EntrySet"); 13 dojo.require("calitha.collections.imap.ImmutableEntry"); 14 dojo.require("calitha.exception.IllegalStateException"); 15 dojo.require("calitha.exception.NullPointerException"); 16 dojo.require("calitha.exception.IllegalArgumentException"); 17 18 dojo.declare("calitha.collections.TreeMap", [calitha.collections.INavigableMap, calitha.collections.AbstractMap], 19 /** @lends calitha.collections.TreeMap# */ 20 { 21 /** 22 * @constructs 23 * @param comparator optional comparator 24 * @extends calitha.collections.INavigableMap 25 * @extends calitha.collections.AbstractMap 26 * @description A Red-Black tree based NavigableMap implementation. 27 * <p>This class is similar to the 28 * <a href="http://java.sun.com/javase/6/docs/api/java/util/TreeMap.html">Java TreeMap class</a> 29 * <p>This map needs a comparator object or function, or all elements must be IComparable. 30 * Take a look at the comparator functions in the {@link calitha.collections} namespace. 31 */ 32 constructor: function(/**(calitha.collections.IComparator|Function)?*/ comparator) 33 { 34 if (calitha.collections.util.isObjectInstanceOf(comparator, calitha.collections.IComparator)) 35 { 36 this._comparator = comparator; 37 } 38 else if (calitha.collections.util.isObjectInstanceOf(comparator, Function)) 39 { 40 this._comparator = new calitha.collections.base.Comparator(comparator); 41 } 42 else 43 { 44 this._comparator = null; 45 } 46 this._root = null; 47 this._size = 0; 48 this._modCount = 0; 49 this._entrySet = null; 50 this._navigableKeySet = null; 51 this._descendingMap = null; 52 } 53 , 54 ceilingEntry: function(/**Object*/ key) 55 { 56 return this._exportEntry(this._getCeilingEntry(key)); 57 } 58 , 59 ceilingKey: function(/**Object*/ key) 60 { 61 return this._keyOrNull(this._getCeilingEntry(key)); 62 } 63 , 64 clear: function() 65 { 66 this._modCount++; 67 this._size = 0; 68 this._root = null; 69 } 70 , 71 comparator: function() 72 { 73 return this._comparator; 74 } 75 , 76 containsKey: function(/**Object*/ key) 77 { 78 return this._getEntry(key) != null; 79 } 80 , 81 containsValue: function(/**Object*/ value) 82 { 83 var entry = this._getFirstEntry(); 84 while (entry != null) 85 { 86 if (calitha.collections.util.equals(entry.value, value)) 87 { 88 return true; 89 } 90 entry = this._successor(entry); 91 } 92 return false; 93 } 94 , 95 descendingKeySet: function() 96 { 97 return this.descendingMap().navigableKeySet(); 98 } 99 , 100 descendingMap: function() 101 { 102 if (this._descendingMap === null) 103 { 104 this._descendingMap = new calitha.collections.treemap.DescendingSubMap(this, true, null, true, true, null, true); 105 } 106 return this._descendingMap; 107 } 108 , 109 entrySet: function() 110 { 111 if (this._entrySet === null) 112 { 113 this._entrySet = new calitha.collections.treemap.EntrySet(this); 114 } 115 return this._entrySet; 116 } 117 , 118 firstKey: function() 119 { 120 return this._key(this._getFirstEntry()); 121 } 122 , 123 floorEntry: function(/**Object*/ key) 124 { 125 return this._exportEntry(this._getFloorEntry(key)); 126 } 127 , 128 get: function(key) 129 { 130 var p = this._getEntry(key); 131 return (p === null ? null : p.value); 132 } 133 , 134 headMap: function(/**Object*/ toKey, /**Boolean*/ inclusive) 135 { 136 return new calitha.collections.treemap.AscendingSubMap(this, 137 true, null, true, 138 false, toKey, inclusive); 139 } 140 , 141 higherEntry: function(/**Object*/ key) 142 { 143 return this._exportEntry(this._getHigherEntry(key)); 144 } 145 , 146 keyIterator: function() 147 { 148 return new calitha.collections.treemap.KeyIterator(this, this._getFirstEntry()); 149 } 150 , 151 keySet: function() 152 { 153 return this.navigableKeySet(); 154 } 155 , 156 lastKey: function() 157 { 158 return this._key(this._getLastEntry()); 159 } 160 , 161 lowerEntry: function(/**Object*/ key) 162 { 163 return this._exportEntry(this._getLowerEntry(key)); 164 } 165 , 166 navigableKeySet: function() 167 { 168 if (this._navigableKeySet === null) 169 { 170 this._navigableKeySet = new calitha.collections.treemap.KeySet(this); 171 } 172 return this._navigableKeySet; 173 } 174 , 175 put: function(/**Object*/ key, /**Object*/ value) 176 { 177 if (this._root === null) 178 { 179 if (key === null) 180 { 181 throw new calitha.exception.NullPointerException(Error()); 182 } 183 this._root = new calitha.collections.treemap.Entry(key, value, null); 184 this._size = 1; 185 this._modCount++; 186 return null; 187 } 188 var node = this._root; 189 var compare; 190 var parent; 191 192 // split comparator and comparable paths 193 if (this._comparator != null) 194 { 195 do 196 { 197 parent = node; 198 compare = this._comparator.compare(key, node.getKey()); 199 if (compare < 0) 200 { 201 node = node.getLeft(); 202 } 203 else if (compare > 0) 204 { 205 node = node.getRight(); 206 } 207 else 208 { 209 return node.setValue(value); 210 } 211 } while (node != null); 212 } 213 else 214 { 215 if (!calitha.collections.util.isObjectInstanceOf(key, calitha.collections.IComparable)) 216 { 217 throw new calitha.exception.IllegalArgumentException(Error("key must be IComparable")); 218 } 219 do 220 { 221 parent = node; 222 compare = key.compareTo(node.getKey()); 223 if (compare < 0) 224 { 225 node = node.getLeft(); 226 } 227 else if (compare > 0) 228 { 229 node = node.getRight(); 230 } 231 else 232 { 233 return node.setValue(value); 234 } 235 } while (node != null); 236 } 237 var entry = new calitha.collections.treemap.Entry(key, value, parent); 238 if (compare < 0) 239 { 240 parent.setLeft(entry); 241 } 242 else 243 { 244 parent.setRight(entry); 245 } 246 this._fixAfterInsertion(entry); 247 this._size++; 248 this._modCount++; 249 return null; 250 } 251 , 252 remove: function(/**Object*/ key) 253 { 254 var node = this._getEntry(key); 255 if (node === null) 256 { 257 return null; 258 } 259 260 var oldValue = node.getValue(); 261 this._deleteEntry(node); 262 return oldValue; 263 }, 264 size: function() 265 { 266 return this._size; 267 } 268 , 269 subMap: function(/**Object*/ fromKey, /**Boolean*/ fromInclusive, /**Object*/ toKey, /**Boolean*/ toInclusive) 270 { 271 return new calitha.collections.treemap.AscendingSubMap(this, 272 false, fromKey, fromInclusive, 273 false, toKey, toInclusive); 274 } 275 , 276 tailMap: function(/**Object*/ fromKey, /**Boolean*/ inclusive) 277 { 278 return new calitha.collections.treemap.AscendingSubMap(this, 279 false, fromKey, inclusive, 280 true, null, true); 281 } 282 , 283 values: function() 284 { 285 if (this._values === null) 286 { 287 this._values = new calitha.collections.treemap.Values(this); 288 } 289 return this._values; 290 } 291 , 292 _colorOf: function(/**calitha.collections.treemap.Entry*/ node) 293 { 294 return (node === null ? calitha.collections.TreeMap._BLACK : node.getColor()); 295 } 296 , 297 _compare: function(/**Object*/ k1, /**Object*/ k2) 298 { 299 if (this._comparator === null) 300 { 301 if ((!calitha.collections.util.isObjectInstanceOf(k1, calitha.collections.IComparable)) || (!calitha.collections.util.isObjectInstanceOf(k2, calitha.collections.IComparable))) 302 { 303 throw new calitha.exception.IllegalStateException(Error("No comparator available. Therefore key must be an IComparable instance.")); 304 } 305 return k1.compareTo(k2); 306 } 307 else 308 { 309 return this._comparator.compare(k1, k2); 310 } 311 } 312 , 313 _deleteEntry:function(/**calitha.collections.treemap.Entry*/ node) 314 { 315 var BLACK = calitha.collections.TreeMap._BLACK; 316 317 this._modCount++; 318 this._size--; 319 320 // If strictly internal, copy successor's element to node and then make node 321 // point to successor. 322 if (node.getLeft() != null && node.getRight() != null) 323 { 324 var s = this._successor(node); 325 node.setKey(s); 326 node.setValue(s.getValue()); 327 node = s; 328 } // node has 2 children 329 330 // Start fixup at replacement node, if it exists. 331 var replacement = (node.getLeft() != null ? node.getLeft() : node.getRight()); 332 333 if (replacement != null) 334 { 335 // Link replacement to parent 336 replacement.setParent(node.getParent()); 337 if (node.getParent() === null) 338 { 339 this._root = replacement; 340 } 341 else if (node === node.getParent().getLeft()) 342 { 343 node.getParent().getLeft() = replacement; 344 } 345 else 346 { 347 node.getParent().getRight() = replacement; 348 } 349 350 // Null out links so they are OK to use by fixAfterDeletion. 351 node.setLeft(null); 352 node.setRight(null); 353 node.setParent(null); 354 355 // Fix replacement 356 if (node.getColor() === BLACK) 357 { 358 this._fixAfterDeletion(replacement); 359 } 360 } 361 else if (node.getParent() === null) 362 { // return if we are the only node. 363 this._root = null; 364 } 365 else 366 { // No children. Use self as phantom replacement and unlink. 367 if (node.getColor() === BLACK) 368 { 369 this._fixAfterDeletion(node); 370 } 371 372 if (node.getParent() != null) 373 { 374 if (node === node.getParent().getLeft()) 375 { 376 node.getParent().setLeft(null); 377 } 378 else if (node === node.getParent().getRight()) 379 { 380 node.getParent().setRight(null); 381 } 382 node.setParent(null); 383 } 384 } 385 } 386 , 387 _exportEntry: function(/**calitha.collections.treemap.Entry*/ entry) 388 { 389 return entry === null ? null : calitha.collections.imap.ImmutableEntry.newInstance(entry); 390 } 391 , 392 _fixAfterDeletion: function(/**calitha.collections.treemap.Entry*/ x) 393 { 394 var BLACK = calitha.collections.TreeMap._BLACK; 395 var RED = calitha.collections.TreeMap._RED; 396 397 var sib; 398 while (x != this._root && this._colorOf(x) === BLACK) 399 { 400 if (x === this._leftOf(this._parentOf(x))) 401 { 402 sib = this._rightOf(this._parentOf(x)); 403 404 if (this._colorOf(sib) === RED) 405 { 406 this._setColor(sib, BLACK); 407 this._setColor(this._parentOf(x), RED); 408 this._rotateLeft(this._parentOf(x)); 409 sib = this._rightOf(this._parentOf(x)); 410 } 411 412 if (this._colorOf(this._leftOf(sib)) === BLACK && 413 this._colorOf(this._rightOf(sib)) === BLACK) 414 { 415 this._setColor(sib, RED); 416 x = this._parentOf(x); 417 } 418 else 419 { 420 if (this._colorOf(this._rightOf(sib)) === BLACK) 421 { 422 this._setColor(this._leftOf(sib), BLACK); 423 this._setColor(sib, RED); 424 this._rotateRight(sib); 425 sib = this._rightOf(this._parentOf(x)); 426 } 427 this._setColor(sib, this._colorOf(this._parentOf(x))); 428 this._setColor(this._parentOf(x), BLACK); 429 this._setColor(this._rightOf(sib), BLACK); 430 this._rotateLeft(this._parentOf(x)); 431 x = this._root; 432 } 433 } 434 else 435 { // symmetric 436 sib = this._leftOf(this._parentOf(x)); 437 438 if (this._colorOf(sib) === RED) 439 { 440 this._setColor(sib, BLACK); 441 this._setColor(this._parentOf(x), RED); 442 this._rotateRight(this._parentOf(x)); 443 sib = this._leftOf(this._parentOf(x)); 444 } 445 446 if (this._colorOf(this._rightOf(sib)) === BLACK && 447 this._colorOf(this._leftOf(sib)) === BLACK) 448 { 449 this._setColor(sib, RED); 450 x = this._parentOf(x); 451 } 452 else 453 { 454 if (this._colorOf(this._leftOf(sib)) === BLACK) 455 { 456 this._setColor(this._rightOf(sib), BLACK); 457 this._setColor(sib, RED); 458 this._rotateLeft(sib); 459 sib = this._leftOf(this._parentOf(x)); 460 } 461 this._setColor(sib, this._colorOf(this._parentOf(x))); 462 this._setColor(this._parentOf(x), BLACK); 463 this._setColor(this._leftOf(sib), BLACK); 464 this._rotateRight(this._parentOf(x)); 465 x = this._root; 466 } 467 } 468 } 469 470 this._setColor(x, BLACK); 471 } 472 , 473 _fixAfterInsertion: function(/**calitha.collections.treemap.Entry*/ x) 474 { 475 var BLACK = calitha.collections.TreeMap._BLACK; 476 var RED = calitha.collections.TreeMap._RED; 477 x.setColor(RED); 478 var y; 479 while (x != null && x != this._root && x.getParent().getColor() === RED) 480 { 481 if (this._parentOf(x) === this._leftOf(this._parentOf(this._parentOf(x)))) 482 { 483 y = this._rightOf(this._parentOf(this._parentOf(x))); 484 if (this._colorOf(y) === RED) 485 { 486 this._setColor(this._parentOf(x), BLACK); 487 this._setColor(y, BLACK); 488 this._setColor(this._parentOf(this._parentOf(x)), RED); 489 x = this._parentOf(this._parentOf(x)); 490 } 491 else 492 { 493 if (x === this._rightOf(this._parentOf(x))) 494 { 495 x = this._parentOf(x); 496 this._rotateLeft(x); 497 } 498 this._setColor(this._parentOf(x), BLACK); 499 this._setColor(this._parentOf(this._parentOf(x)), RED); 500 this._rotateRight(this._parentOf(this._parentOf(x))); 501 } 502 } 503 else 504 { 505 y = this._leftOf(this._parentOf(this._parentOf(x))); 506 if (this._colorOf(y) === RED) 507 { 508 this._setColor(this._parentOf(x), BLACK); 509 this._setColor(y, BLACK); 510 this._setColor(this._parentOf(this._parentOf(x)), RED); 511 x = this._parentOf(this._parentOf(x)); 512 } 513 else 514 { 515 if (x === this._leftOf(this._parentOf(x))) 516 { 517 x = this._parentOf(x); 518 this._rotateRight(x); 519 } 520 this._setColor(this._parentOf(x), BLACK); 521 this._setColor(this._parentOf(this._parentOf(x)), RED); 522 this._rotateLeft(this._parentOf(this._parentOf(x))); 523 } 524 } 525 } 526 this._root.color = BLACK; 527 } 528 , 529 _getCeilingEntry: function(/**Object*/ key) 530 { 531 var p = this._root; 532 while (p != null) 533 { 534 var cmp = this._compare(key, p.key); 535 if (cmp < 0) 536 { 537 if (p.left != null) 538 { 539 p = p.left; 540 } 541 else 542 { 543 return p; 544 } 545 } 546 else if (cmp > 0) 547 { 548 if (p.right != null) 549 { 550 p = p.right; 551 } 552 else 553 { 554 var parent = p.parent; 555 var ch = p; 556 while (parent != null && ch === parent.right) 557 { 558 ch = parent; 559 parent = parent.parent; 560 } 561 return parent; 562 } 563 } 564 else 565 { 566 return p; 567 } 568 } 569 return null; 570 } 571 , 572 _getEntry: function(/**Object*/ key) 573 { 574 // Offload comparator-based version for sake of performance 575 if (this._comparator != null) 576 { 577 return this._getEntryUsingComparator(key); 578 } 579 if (key === null) 580 { 581 throw new calitha.exception.NullPointerException(Error()); 582 } 583 var p = this._root; 584 while (p != null) 585 { 586 var cmp = key.compareTo(p.key); 587 if (cmp < 0) 588 { 589 p = p.left; 590 } 591 else if (cmp > 0) 592 { 593 p = p.right; 594 } 595 else 596 { 597 return p; 598 } 599 } 600 return null; 601 } 602 , 603 _getEntryUsingComparator: function(/**Object*/ key) 604 { 605 if (this._comparator != null) 606 { 607 var p = this._root; 608 while (p != null) 609 { 610 var cmp = this._comparator.compare(key, p.key); 611 if (cmp < 0) 612 { 613 p = p.left; 614 } 615 else if (cmp > 0) 616 { 617 p = p.right; 618 } 619 else 620 { 621 return p; 622 } 623 } 624 } 625 return null; 626 } 627 , 628 _getFirstEntry: function() 629 { 630 var p = this._root; 631 if (p != null) 632 { 633 while (p.left != null) 634 { 635 p = p.left; 636 } 637 } 638 return p; 639 } 640 , 641 _getFloorEntry: function(/**Object*/ key) 642 { 643 var p = this._root; 644 while (p != null) 645 { 646 var cmp = this._compare(key, p.key); 647 if (cmp > 0) 648 { 649 if (p.right != null) 650 { 651 p = p.right; 652 } 653 else 654 { 655 return p; 656 } 657 } 658 else if (cmp < 0) 659 { 660 if (p.left != null) 661 { 662 p = p.left; 663 } 664 else 665 { 666 var parent = p.parent; 667 var ch = p; 668 while (parent != null && ch === parent.left) 669 { 670 ch = parent; 671 parent = parent.parent; 672 } 673 return parent; 674 } 675 } 676 else 677 { 678 return p; 679 } 680 } 681 return null; 682 } 683 , 684 _getHigherEntry: function(/**Object*/ key) 685 { 686 var p = this._root; 687 while (p != null) 688 { 689 var cmp = this._compare(key, p.key); 690 if (cmp < 0) 691 { 692 if (p.left != null) 693 { 694 p = p.left; 695 } 696 else 697 { 698 return p; 699 } 700 } 701 else 702 { 703 if (p.right != null) 704 { 705 p = p.right; 706 } 707 else 708 { 709 var parent = p.parent; 710 var ch = p; 711 while (parent != null && ch === parent.right) 712 { 713 ch = parent; 714 parent = parent.parent; 715 } 716 return parent; 717 } 718 } 719 } 720 return null; 721 } 722 , 723 _getLastEntry: function() 724 { 725 var p = this._root; 726 if (p != null) 727 { 728 while (p.right != null) 729 { 730 p = p.right; 731 } 732 } 733 return p; 734 } 735 , 736 _getLowerEntry: function(/**Object*/ key) 737 { 738 var p = this._root; 739 while (p != null) 740 { 741 var cmp = this._compare(key, p.key); 742 if (cmp > 0) 743 { 744 if (p.right != null) 745 { 746 p = p.right; 747 } 748 else 749 { 750 return p; 751 } 752 } 753 else 754 { 755 if (p.left != null) 756 { 757 p = p.left; 758 } 759 else 760 { 761 var parent = p.parent; 762 var ch = p; 763 while (parent != null && ch === parent.left) 764 { 765 ch = parent; 766 parent = parent.parent; 767 } 768 return parent; 769 } 770 } 771 } 772 return null; 773 } 774 , 775 _key: function(/**calitha.collections.treemap.Entry*/ entry) 776 { 777 if (entry === null) 778 { 779 throw new calitha.exception.NoSuchElementException(Error()); 780 } 781 return entry.getKey(); 782 } 783 , 784 _keyOrNull: function(/**calitha.collections.treemap.Entry*/ entry) 785 { 786 return entry === null? null : entry.getKey(); 787 } 788 , 789 _leftOf: function(/**calitha.collections.treemap.Entry*/ node) 790 { 791 return (node === null) ? null: node.getLeft(); 792 } 793 , 794 _parentOf: function(/**calitha.collections.treemap.Entry*/ node) 795 { 796 return (node === null ? null: node.getParent()); 797 } 798 , 799 _predecessor: function(/**calitha.collections.treemap.Entry*/ t) 800 { 801 var p; 802 if (t === null) 803 { 804 return null; 805 } 806 else if (t.getLeft() != null) 807 { 808 p = t.getLeft(); 809 while (p.getRight() != null) 810 { 811 p = p.getRight(); 812 } 813 return p; 814 } 815 else 816 { 817 p = t.getParent(); 818 var ch = t; 819 while (p != null && ch === p.getLeft()) 820 { 821 ch = p; 822 p = p.getParent(); 823 } 824 return p; 825 } 826 } 827 , 828 _rightOf: function(/**calitha.collections.treemap.Entry*/ node) 829 { 830 return (node === null) ? null: node.getRight(); 831 } 832 , 833 _rotateLeft: function(/**calitha.collections.treemap.Entry*/ node) 834 { 835 if (node != null) 836 { 837 var rightNode = node.getRight(); 838 node.setRight(rightNode.getLeft()); 839 if (rightNode.getLeft() != null) 840 { 841 rightNode.getLeft().setParent(node); 842 } 843 rightNode.setParent(node.getParent()); 844 if (node.getParent() === null) 845 { 846 this._root = rightNode; 847 } 848 else if (node.getParent().getLeft() === node) 849 { 850 node.getParent().setLeft(rightNode); 851 } 852 else 853 { 854 node.getParent().setRight(rightNode); 855 } 856 rightNode.setLeft(node); 857 node.setParent(rightNode); 858 } 859 } 860 , 861 _rotateRight: function(/**calitha.collections.treemap.Entry*/ node) 862 { 863 if (node != null) 864 { 865 var leftNode = node.getLeft(); 866 node.setLeft(leftNode.getRight()); 867 if (leftNode.getRight() != null) 868 { 869 leftNode.getRight().setParent(node); 870 } 871 leftNode.setParent(node.getParent()); 872 if (node.getParent() === null) 873 { 874 this._root = leftNode; 875 } 876 else if (node.getParent().getRight() === node) 877 { 878 node.getParent().setRight(leftNode); 879 } 880 else 881 { 882 node.getParent().setLeft(leftNode); 883 } 884 leftNode.setRight(node); 885 node.setParent(leftNode); 886 } 887 } 888 , 889 _setColor: function(/**calitha.collections.treemap.Entry*/ node, /**Number*/ color) 890 { 891 if (node != null) 892 { 893 node.setColor(color); 894 } 895 } 896 , 897 _successor: function(/**calitha.collections.treemap.Entry*/ node) 898 { 899 if (node === null) 900 { 901 return null; 902 } 903 else if (node.getRight() != null) 904 { 905 node = node.getRight(); 906 while (node.getLeft() != null) 907 { 908 node = node.getLeft(); 909 } 910 return node; 911 } 912 else 913 { 914 var parent = node.getParent(); 915 while (parent != null && node === parent.getRight()) 916 { 917 node = parent; 918 parent = parent.getParent(); 919 } 920 return parent; 921 } 922 } 923 924 }); 925 926 calitha.collections.TreeMap._RED = false; 927 calitha.collections.TreeMap._BLACK = true; 928